31

如何使用存儲庫模式以事務方式封裝保存多個實體?例如,如果我想添加訂單並根據訂單創建更新客戶狀態,但只有在訂單成功完成時纔會這樣做。請記住,在本例中,訂單不是客戶內部的集合。他們是他們自己的實體。存儲庫模式中的事務

這只是一個人爲的例子,所以我並不關心訂單是否應該在客戶對象內部,或者甚至在相同的有界環境中。我並不在乎底層技術的用途(nHibernate,EF,ADO.Net,Linq等)。我只是想看看在這個公認的人爲操作的例子中,一些調用代碼可能看起來像什麼樣子。

回答

7

我會看看使用某種類型的事務範圍/上下文系統。所以你可能有下面的代碼,它大致基於.Net & C#。

public class OrderService 
{ 

public void CreateNewOrder(Order order, Customer customer) 
{ 
    //Set up our transactional boundary. 
    using (TransactionScope ts=new TransactionScope()) 
    { 
    IOrderRepository orderRepos=GetOrderRespository(); 
    orderRepos.SaveNew(order); 
    customer.Status=CustomerStatus.OrderPlaced; 

    ICustomerRepository customerRepository=GetCustomerRepository(); 
    customerRepository.Save(customer) 
    ts.Commit(); 
    } 
} 
} 

TransactionScope可以嵌套,所以假設你有一個跨越多個服務的動作,你的應用程序也會創建一個TransactionScope。如果您使用TransactionScope,那麼現在在.net中,您有可能會對DTC產生影響,但將來會解決這個問題。

我們創建了自己的TransactionScope類,它基本上管理了我們的數據庫連接並使用了本地SQL事務。

+0

我不認爲這是DDD精神的解決方案。基本上你已經創建了一個交易腳本來完成域模型的工作。例如,服務不應改變客戶狀態。 – 2013-10-30 09:06:36

+1

代碼中的某些東西必須處理這個業務規則,無論是在這個級別還是更高級別上,在單個TransactionScope中進行更改都允許本地事務或分佈式事務處理事務。如果業務規則規定每當下訂單時都要更新客戶,那麼這是一個處理所有訂單的好地方。 – JoshBerke 2013-11-02 18:12:42

3

你想看看實施工作模式的單位。有NHibernate的實現。一個在Rhino Commons項目中,還有Machine.UoW。

5

使用Spring.NET AOP + NHibernate的,你可以寫你的倉庫類爲正常和自定義XML文件來配置你的交易:

public class CustomerService : ICustomerService 
{ 
    private readonly ICustomerRepository _customerRepository; 
    private readonly IOrderRepository _orderRepository; 

    public CustomerService(
     ICustomerRepository customerRepository, 
     IOrderRepository orderRepository) 
    { 
     _customerRepository = customerRepository; 
     _orderRepository = orderRepository; 
    } 

    public int CreateOrder(Order o, Customer c) 
    { 
     // Do something with _customerRepository and _orderRepository 
    } 
} 

在您選擇您想在內部執行該方法的XML文件交易:

<object id="TxProxyConfigurationTemplate" 
      abstract="true" 
      type="Spring.Transaction.Interceptor.TransactionProxyFactoryObject, Spring.Data"> 

    <property name="PlatformTransactionManager" ref="HibernateTransactionManager"/> 

    <property name="TransactionAttributes"> 
     <name-values> 
     <add key="Create*" value="PROPAGATION_REQUIRED"/> 
     </name-values> 
    </property> 
    </object> 

    <object id="customerService" parent="TxProxyConfigurationTemplate"> 
    <property name="Target"> 
     <object type="MyNamespace.CustomerService, HibernateTest"> 
      <constructor-arg name="customerRepository" ref="customerRepository" /> 
      <constructor-arg name="orderRepository" ref="orderRepository" /> 
     </object> 
    </property> 

    </object> 

並在代碼中,你得到的CustomerService類的一個實例是這樣的:

ICustomerService customerService = (ICustomerService)ContextRegistry 
    .GetContent() 
    .GetObject("customerService"); 

Spring.NET將返回一個CustomerService類的代理,該類將在您調用CreateOrder方法時應用事務。這樣,您的服務類中就沒有特定於交易的代碼。 AOP照顧它。欲瞭解更多詳情,你可以看看Spring.NET的文檔。

2

如何封裝的 節約超過一個實體更多使用 庫模式的 事務的方式?例如,如果我想添加訂單並根據 訂單創建客戶狀態 更新什麼 ,但只有在訂單成功完成 訂單時才這樣做?保留在 請注意,對於此示例,訂單 不是客戶內部的集合。 他們是他們自己的實體。

它不是倉庫的責任,它通常是在更高層次上完成的。雖然你說你對特定技術不感興趣,但我認爲它值得追求解決方案,例如在使用NHibernate和Web應用程序時,你可能會考慮使用session-per request

所以,如果你能夠在更高層次上的管理事務,然後我的兩個選擇是:

  1. 前期檢查 - 例如,在服務協調行爲的決定,如果你想通過要求繼續訂單/客戶,如果他們說他們甚至不嘗試更新他們中的任何一個。
  2. 回滾 - 只需繼續更新客戶/訂單,如果事情部分通過回滾數據庫事務處理失敗。

如果您選擇第二個選項,那麼問題是內存中的對象會發生什麼,您的客戶可能會處於不一致的狀態。如果這一點很重要,而且我的工作方式並不像對象只是爲了請求而加載的那樣,那麼我會考慮先檢查它是否可能,因爲它比替代方案更容易(回滾內 - 內存更改或重新加載對象)。

+1

爲什麼它不是倉庫的責任?將數據庫操作從領域模型中抽象出來不是整個想法嗎?對我而言,存儲庫是放置該事務支持的最佳地點。 – 2009-02-22 19:31:27

12

今天早上啓動我的電腦我遇到了我正在進行的項目的確切問題。我有一些想法導致了以下設計 - 評論將不僅僅是真棒。不幸的是,Josh建議的設計是不可能的,因爲我必須使用遠程SQL服務器,並且無法啓用它所依賴的Distribute Transaction Coordinator服務。

我的解決方案基於對現有代碼的一些簡單更改。

首先,我有我的所有存儲庫實現一個簡單的標記接口:

/// <summary> 
/// A base interface for all repositories to implement. 
/// </summary> 
public interface IRepository 
{ } 

其次,我讓我所有的交易使倉庫實現以下接口:

/// <summary> 
/// Provides methods to enable transaction support. 
/// </summary> 
public interface IHasTransactions : IRepository 
{ 
    /// <summary> 
    /// Initiates a transaction scope. 
    /// </summary> 
    void BeginTransaction(); 

    /// <summary> 
    /// Executes the transaction. 
    /// </summary> 
    void CommitTransaction(); 
} 

的想法是,在我所有的存儲庫都實現了這個接口,並添加了直接引入事務的代碼,具體取決於實際的提供者(對於僞代碼庫,我已經創建了一個在提交時執行的代理列表)。對於LINQ to SQL中它會很容易做出的實現,例如:

#region IHasTransactions Members 

public void BeginTransaction() 
{ 
    _db.Transaction = _db.Connection.BeginTransaction(); 
} 

public void CommitTransaction() 
{ 
    _db.Transaction.Commit(); 
} 

#endregion 

當然,這需要一個新的存儲庫類爲每個線程創建的,但這是合理的爲我的項目。

如果存儲庫實現了IHasTransactions,則使用存儲庫的每種方法都需要調用BeginTransaction()EndTransaction()。爲了使這個呼叫更容易,我想出了以下擴展:

/// <summary> 
/// Extensions for spawning and subsequently executing a transaction. 
/// </summary> 
public static class TransactionExtensions 
{ 
    /// <summary> 
    /// Begins a transaction if the repository implements <see cref="IHasTransactions"/>. 
    /// </summary> 
    /// <param name="repository"></param> 
    public static void BeginTransaction(this IRepository repository) 
    { 
     var transactionSupport = repository as IHasTransactions; 
     if (transactionSupport != null) 
     { 
      transactionSupport.BeginTransaction(); 
     } 
    } 

    public static void CommitTransaction(this IRepository repository) 
    { 
     var transactionSupport = repository as IHasTransactions; 
     if (transactionSupport != null) 
     { 
      transactionSupport.CommitTransaction(); 
     } 
    } 
} 

評論感謝!