9

我们应用程序中的数据访问层将使用 Oracle 的 UDT 功能。我们只会将 UDT 对象传入和传出数据库。

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

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

我正在努力寻找更好的方法来做到这一点。

我想我会放弃生成的类,只写一个实现 IOracleCustomType 的映射类。From/ToCustomObject 方法然后会从我的 UDT 映射到我的业务对象。但是,当我尝试它时,这给我带来了问题 - 我收到错误“对象属性未映射到自定义类型成员”。看来,除了这两种方法外,我的映射类中还需要属性 - UDT 中的每个项目都有一个属性。

例如 - 一个工作流 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 似乎只是需要它们来获取要映射到的类型——但我认为这可以通过不同的方式实现。想法?

4

3 回答 3

2

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

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

于 2009-10-01T19:29:54.110 回答
0

您可以将 BusinessObject 设置为私有成员并将属性映射到相应的属性。

虽然这不提供任何功能,但如果使用它们至少可以正常工作。

于 2014-05-20T22:12:51.637 回答
0

欢迎来到甲骨文。你会讨厌这里的。你是对的,当你必须显式地获取/设置值时你必须提供这些属性是绝对荒谬的。.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; }
}

我喜欢属性,因为它可以利用反射来减少重复代码。使用这种方法,BaseUDT该类将迭代所有属性并相应地设置它们。我理解您在向模型中添加业务逻辑时犹豫不决,但这在很多情况下是不可避免的。

于 2020-06-04T18:32:01.387 回答