簡體   English   中英

如何在不使用 OracleObjectMappingAttribute 的情況下使用 ODP.NET 從 Oracle UDT 進行映射?

[英]How do I map from Oracle UDTs with ODP.NET without using OracleObjectMappingAttribute?

我們應用程序中的數據訪問層將使用 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 似乎只是需要它們來獲取要映射到的類型——但我認為這可以通過不同的方式實現。 想法?

這些額外的屬性有什么不好? .net 類和框架(例如 WCF)中已經有大量的屬性。 不喜歡屬性幾乎與不喜歡 .NET 相同。

無論如何,您可以探索 devart 的 Oracle 提供程序 ( http://www.devart.com/dotconnect/oracle/ ) 的可能性。 他們也有免費版本。 它處理 udts 是基於字符串而不是屬性。

您可以將您的 BusinessObject 設置為私有成員並將屬性映射到相應的屬性。

雖然這不提供任何功能,但至少如果使用它們會正常運行。

歡迎使用甲骨文。 你會討厭這里。 你是對的,當你必須明確獲取/設置值時,你必須提供這些屬性絕對是荒謬的。 .NET 生態系統非常通用(特別是在更現代的 .NET Core 中),您完全希望該框架能夠處理這樣的事情。 然而,甲骨文完全錯過了那個備忘錄(他們經常這樣做)。 大多數 Oracle 框架(以及整個 Java 語言)都有這個問題。 這是我的解決方法:

從實現IOracleCustomType的基本 UDT 類開始

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類將迭代所有屬性並相應地設置它們。 我理解您在為模型添加業務邏輯方面猶豫不決,但這在很多情況下是不可避免的。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM