Cha*_*ham 39 design-patterns domain-driven-design architectural-patterns repository-pattern
如何使用存储库模式以事务方式封装多个实体的保存?例如,如果我想根据订单创建添加订单并更新客户状态,但只有在订单成功完成时才会这样做?请记住,对于此示例,订单不是客户内的集合.他们是自己的实体.
这只是一个人为的例子,所以我并不关心订单是否应该在客户对象内部,甚至不在同一个有限的上下文中.我真的不在乎将使用什么底层技术(nHibernate,EF,ADO.Net,Linq等).我只是想看看一些调用代码在这个公认的全有或全无操作示例中的样子.
Tro*_*sen 14
今天早上启动我的电脑我遇到了我正在进行的项目的确切问题.我有一些想法导致以下设计 - 评论将超过令人敬畏.不幸的是,Josh建议的设计是不可能的,因为我必须使用远程SQL服务器并且无法启用它所依赖的Distribute Transaction Coordinator服务.
我的解决方案基于对现有代码的一些简单更改.
首先,我的所有存储库都实现了一个简单的标记接口:
/// <summary>
/// A base interface for all repositories to implement.
/// </summary>
public interface IRepository
{ }
Run Code Online (Sandbox Code Playgroud)
其次,我让所有启用事务的存储库实现以下接口:
/// <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();
}
Run Code Online (Sandbox Code Playgroud)
我的想法是,在我的所有存储库中,我实现了这个接口,并添加了直接引入事务的代码,具体取决于实际的提供程序(对于伪造的存储库,我已经创建了一个在提交时执行的委托列表).对于LINQ to SQL,可以很容易地实现如下的实现:
#region IHasTransactions Members
public void BeginTransaction()
{
_db.Transaction = _db.Connection.BeginTransaction();
}
public void CommitTransaction()
{
_db.Transaction.Commit();
}
#endregion
Run Code Online (Sandbox Code Playgroud)
这当然要求为每个线程创建一个新的存储库类,但这对我的项目来说是合理的.
如果存储库实现,则使用存储库的每个方法都需要调用BeginTransaction()和.为了使这个电话更容易,我提出了以下扩展:EndTransaction()IHasTransactions
/// <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();
}
}
}
Run Code Online (Sandbox Code Playgroud)
评论赞赏!
我会看一下使用某种类型的Transaction Scope/Context系统.所以你可能有以下代码,大致基于.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();
}
}
}
Run Code Online (Sandbox Code Playgroud)
TransactionScope可以嵌套,所以假设您有一个跨越多个服务的操作,您的应用程序也会创建一个TransactionScope.现在在当前的.net中,如果您使用TransactionScope,他们就有可能冒险进入DTC,但这将在未来得到解决.
我们创建了自己的TransactionScope类,它基本上管理了我们的数据库连接并使用了本地SQL事务.
使用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
}
}
Run Code Online (Sandbox Code Playgroud)
在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>
Run Code Online (Sandbox Code Playgroud)
在您的代码中,您将获得CustomerService类的实例,如下所示:
ICustomerService customerService = (ICustomerService)ContextRegistry
.GetContent()
.GetObject("customerService");
Run Code Online (Sandbox Code Playgroud)
Spring.NET将返回CustomerService类的代理,该类将在您调用CreateOrder方法时应用事务.这样,服务类中就没有特定于事务的代码.AOP照顾它.有关更多详细信息,您可以查看Spring.NET的文档.
如何使用存储库模式以事务性方式封装多个实体的保存?例如,如果我想添加一个订单并根据该订单创建更新客户状态,但只有在订单成功完成时才这样做怎么办?请记住,对于此示例,订单不是客户内部的集合。他们是自己的实体。
这不是存储库的责任,它通常是在更高级别上完成的。尽管您说您对特定技术不感兴趣,但我认为值得确定解决方案,例如,当将 NHibernate 与 Web 应用程序一起使用时,您可能会考虑使用session-per request。
因此,如果您可以在更高级别管理交易,那么我的两个选择是:
如果你选择第二个选项,那么问题是内存对象会发生什么,你的客户可能会处于不一致的状态。如果这很重要,并且我在它不为该请求加载对象的情况下工作,那么我会考虑是否可能进行前期检查,因为它比替代方案容易得多(回滚- 内存更改或重新加载对象)。