我们的应用程序中的数据访问层将使用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似乎只是需要它们来获取要映射的类型--但我认为可以用另一种方式来实现。
那些额外的属性有什么不好?在.net类和框架中已经有大量的属性(例如WCF)。不喜欢属性几乎等同于不喜欢.NET。
无论如何,你可以探索devart的Oracle提供商的可能性(http:/www.devart.comdotconnectoracle). 他们也有一个免费的版本。它处理udts是基于字符串而不是属性。
你可以将你的BusinessObject设置为私有成员,并将属性映射到各自的属性上。
虽然这并不能提供任何功能,但如果使用的话,至少它们会正确地发挥作用。
欢迎来到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
类将迭代所有属性并进行相应的设置。我理解你对在模型中添加业务逻辑的犹豫,但这在很多场景下是不可避免的。