如何使用存储库模式以事务方式封装保存多个实体?例如,如果我想添加订单并根据订单创建更新客户状态,但只有在订单成功完成时才会这样做。请记住,在本例中,订单不是客户内部的集合。他们是他们自己的实体。存储库模式中的事务
这只是一个人为的例子,所以我并不关心订单是否应该在客户对象内部,或者甚至在相同的有界环境中。我并不在乎底层技术的用途(nHibernate,EF,ADO.Net,Linq等)。我只是想看看在这个公认的人为操作的例子中,一些调用代码可能看起来像什么样子。
如何使用存储库模式以事务方式封装保存多个实体?例如,如果我想添加订单并根据订单创建更新客户状态,但只有在订单成功完成时才会这样做。请记住,在本例中,订单不是客户内部的集合。他们是他们自己的实体。存储库模式中的事务
这只是一个人为的例子,所以我并不关心订单是否应该在客户对象内部,或者甚至在相同的有界环境中。我并不在乎底层技术的用途(nHibernate,EF,ADO.Net,Linq等)。我只是想看看在这个公认的人为操作的例子中,一些调用代码可能看起来像什么样子。
我会看看使用某种类型的事务范围/上下文系统。所以你可能有下面的代码,它大致基于.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事务。
你想看看实施工作模式的单位。有NHibernate的实现。一个在Rhino Commons项目中,还有Machine.UoW。
使用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的文档。
如何封装的 节约超过一个实体更多使用 库模式的 事务的方式?例如,如果我想添加订单并根据 订单创建客户状态 更新什么 ,但只有在订单成功完成 订单时才这样做?保留在 请注意,对于此示例,订单 不是客户内部的集合。 他们是他们自己的实体。
它不是仓库的责任,它通常是在更高层次上完成的。虽然你说你对特定技术不感兴趣,但我认为它值得追求解决方案,例如在使用NHibernate和Web应用程序时,你可能会考虑使用session-per request。
所以,如果你能够在更高层次上的管理事务,然后我的两个选择是:
如果您选择第二个选项,那么问题是内存中的对象会发生什么,您的客户可能会处于不一致的状态。如果这一点很重要,而且我的工作方式并不像对象只是为了请求而加载的那样,那么我会考虑先检查它是否可能,因为它比替代方案更容易(回滚内 - 内存更改或重新加载对象)。
为什么它不是仓库的责任?将数据库操作从领域模型中抽象出来不是整个想法吗?对我而言,存储库是放置该事务支持的最佳地点。 – 2009-02-22 19:31:27
今天早上启动我的电脑我遇到了我正在进行的项目的确切问题。我有一些想法导致了以下设计 - 评论将不仅仅是真棒。不幸的是,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();
}
}
}
评论感谢!
我不认为这是DDD精神的解决方案。基本上你已经创建了一个交易脚本来完成域模型的工作。例如,服务不应改变客户状态。 – 2013-10-30 09:06:36
代码中的某些东西必须处理这个业务规则,无论是在这个级别还是更高级别上,在单个TransactionScope中进行更改都允许本地事务或分布式事务处理事务。如果业务规则规定每当下订单时都要更新客户,那么这是一个处理所有订单的好地方。 – JoshBerke 2013-11-02 18:12:42