如何在不使用OracleObjectMappingAttribute的情况下,用ODP.NET从Oracle UDTs中进行映射?

问题描述 投票:9回答:3

我们的应用程序中的数据访问层将使用Oracle的UDT功能。我们将只把UDT对象传递到数据库和从数据库中传递出来。

目前,我们使用ODP.NET提供的函数生成自定义类(它创建了一个看起来非常可怕的类,我们真的不希望在我们的代码库中出现)。

然后,我们使用一个单独的映射类,将自定义类映射到我们的一个业务对象上(保存时再返回)。

我正试图找到一个更好的方法来完成这个任务。

我想我应该放弃生成的类,直接写一个实现IOracleCustomType的映射类。FromToCustomObject方法将从我的UDT映射到我的业务对象。然而,当我尝试时,这给我带来了问题--我得到了 "Object属性没有映射到自定义类型成员 "的错误信息。看来,除了这两个方法,我还需要在我的映射类中加入属性--UDT中的每个项目都有一个属性。

例如--一个工作流UDT包含三个项目--状态、创建时间和创建者。

TYPE workflow_type AS OBJECT
(status                                  VARCHAR2(8)
,created_by                              VARCHAR2(30)
,created_datetime            DATE
);

就像我想让它最终进入的业务对象一样。

public class Workflow
{
    /// <summary>
    /// Gets the status of the workflow.
    /// </summary>
    /// <value>The status.</value>
    public string Status { get; private set; }

    /// <summary>
    /// Gets the Windows Logon Id of the user performing the action
    /// </summary>
    public string CreatedBy{ get; private set; }
    /// <summary>
    /// Gets the time of the action 
    /// </summary>
    public DateTime CreatedTime { get; private set; }
}

我想从一个对象到另一个对象,而不需要在业务对象中添加Oracle代码。

所以我的想法是创建一个这样的映射类。

public class WorkFlowMapper : IOracleCustomType
{
    public BusinessObjects.WorkFlow BusinessObject {get; private set;}

    public WorkFlowMapper(BusinessObjects.WorkFlow businessObject)
    {
        BusinessObject = businessObject;
    }

    public WorkFlowMapper(){}

    public void FromCustomObject(OracleConnection con, IntPtr pUdt)
    {
        OracleUdt.SetValue(con, pUdt, "STATUS", BusinessObject.Status);
        OracleUdt.SetValue(con, pUdt, "CREATED_BY", BusinessObject.CreatedBy);
        OracleUdt.SetValue(con, pUdt, "CREATED_DATETIME", BusinessObject.CreatedTime);
    }

    public void ToCustomObject(OracleConnection con, IntPtr pUdt)
    {

        BusinessObject = new BusinessObjects.WorkFlow(
            (string)OracleUdt.GetValue(con, pUdt, "STATUS"),
            (string)OracleUdt.GetValue(con, pUdt, "CREATED_BY"),
            (string)OracleUdt.GetValue(con, pUdt, "CREATED_DATETIME")
        );
    }
}

// Factory to create an object for the above class
[OracleCustomTypeMappingAttribute("MYUSER.WORKFLOW_TYPE")]
public class CurrencyExposureFactory : IOracleCustomTypeFactory
{

    public virtual IOracleCustomType CreateObject()
    {
        WorkFlowMapper obj = new WorkFlowMapper();
        return obj;
    }
}

但这是行不通的,因为每个要映射的属性都需要OracleObjectMappingAttribute(就像在ODP.NET生成的类中一样)。这看起来真的很愚蠢,因为我根本不会使用它们。

    [OracleObjectMappingAttribute("STATUS")] public string a;
    [OracleObjectMappingAttribute("CREATED_BY")] public string b;
    [OracleObjectMappingAttribute("CREATED_DATETIME")] public DateTime c;

当然,一定有更好的方法,而不是用这种可怕的黑客方法?毕竟,这些变量从来没有被使用过--ODP.NET似乎只是需要它们来获取要映射的类型--但我认为可以用另一种方式来实现。

c# .net oracle odp.net user-defined-types
3个回答
2
投票

那些额外的属性有什么不好?在.net类和框架中已经有大量的属性(例如WCF)。不喜欢属性几乎等同于不喜欢.NET。

无论如何,你可以探索devart的Oracle提供商的可能性(http:/www.devart.comdotconnectoracle). 他们也有一个免费的版本。它处理udts是基于字符串而不是属性。


0
投票

你可以将你的BusinessObject设置为私有成员,并将属性映射到各自的属性上。

虽然这并不能提供任何功能,但如果使用的话,至少它们会正确地发挥作用。


0
投票

欢迎来到Oracle。你会讨厌这里的。你是对的,当你无论如何都要显式地getset值时,你必须提供这些属性,这绝对是荒谬的。.NET生态系统是如此美妙的通用性(特别是在更现代的.NET Core中),以至于你完全期望该框架能够处理这样的事情。然而甲骨文公司完全错过了这个备忘录(他们经常这样做)。大多数Oracle框架(以及整个Java语言)都有这个问题。下面是我的变通方法。

从实现UDT的基类开始。IOracleCustomType

public abstract class BaseUDT : IOracleCustomType
    {
        private IEnumerable<PropertyInfo> GetTypeProperties() => GetType()
                .GetProperties(BindingFlags.Public | BindingFlags.Instance)
                .Where(p => p.GetCustomAttribute<OracleObjectMappingAttribute>() != null);

        public virtual void ToCustomObject(OracleConnection con, IntPtr pUdt)
        {
            foreach (var prop in GetTypeProperties())
            {
                var attr = prop.GetCustomAttribute<OracleObjectMappingAttribute>();
                prop.SetValue(this, OracleUdt.GetValue(con, pUdt, attr.AttributeName));
            }
        }

        public virtual void FromCustomObject(OracleConnection con, IntPtr pUdt)
        {
            foreach (var prop in GetTypeProperties())
            {
                var attr = prop.GetCustomAttribute<OracleObjectMappingAttribute>();
                OracleUdt.SetValue(con, pUdt, attr.AttributeName, prop.GetValue(this));
            }
        }
    }

上面的类将寻找所有的公共实例属性,这些公共实例属性有 OracleObjectMappingAttribute 属性,然后动态地获取其值。你的UDT就变成了

public class Workflow : BaseUDT
{
    /// <summary>
    /// Gets the status of the workflow.
    /// </summary>
    /// <value>The status.</value>
    [OracleObjectMapping("STATUS")]
    public string Status { get; private set; }

    /// <summary>
    /// Gets the Windows Logon Id of the user performing the action
    /// </summary>
    [OracleObjectMapping("CREATED_BY")]
    public string CreatedBy{ get; private set; }
    /// <summary>
    /// Gets the time of the action 
    /// </summary>
    [OracleObjectMapping("CREATED_DATETIME")]
    public DateTime CreatedTime { get; private set; }
}

我喜欢属性,因为它可以利用Reflection来减少重复的代码。通过这种方法, BaseUDT 类将迭代所有属性并进行相应的设置。我理解你对在模型中添加业务逻辑的犹豫,但这在很多场景下是不可避免的。

© www.soinside.com 2019 - 2024. All rights reserved.