存储库模式中的事务 [英] Transactions in the Repository Pattern

查看:155
本文介绍了存储库模式中的事务的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如何使用存储库模式以事务方式封装多个实体的保存?例如,如果我想添加订单并根据订单创建更新客户状态,但只有在订单成功完成后才能做到这一点?请记住,对于这个例子,订单不是客户内的一个集合。他们是他们自己的实体。



这只是一个有创意的例子,所以我不在乎订单是应该还是不应该在客户对象内,同样有界的语境。我不太在乎什么基础技术将被使用(nHibernate,EF,ADO.Net,Linq等)我只是想看看一些调用代码可能看起来像这是一个全部或没有操作的例子。

解决方案

今天上午启动我的电脑,我遇到了正在处理的项目的确切问题。我有一些想法导致以下设计 - 而且评论将不仅仅是令人敬畏。不幸的是,Josh建议的设计是不可能的,因为我必须使用远程SQL服务器,并且无法启用它所依赖的分发事务协调器服务。



我的解决方案是基于现有代码的一些简单更改。



首先,我的所有存储库都实现了一个简单的标记界面:

  ///< summary> 
///所有存储库实现的基本界面。
///< / summary>
public interface IRepository
{}

其次,让我的所有交易启用存储库实现以下界面:

  ///< summary> 
///提供启用事务支持的方法。
///< / summary>
public interface IHasTransactions:IRepository
{
///< summary>
///启动事务范围。
///< / summary>
void BeginTransaction();

///< summary>
///执行事务。
///< / summary>
void CommitTransaction();
}

这个想法是,在我所有的存储库中,我实现了这个接口并添加了根据实际提供程序直接引入事务(对于虚拟存储库,我已经提交了在提交时执行的代理列表)。对于LINQ to SQL,可以轻松实现如下功能:

  #region IHasTransactions成员

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

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

#endregion

这当然要求为每个线程创建新的存储库类,但这对我的项目是合理的。



使用存储库的每个方法都需要调用 BeginTransaction() EndTransaction(),如果存储库实现 IHasTransactions 。为了使这个电话更容易,我想出了以下扩展:

  ///< summary> 
///用于生成并随后执行事务的扩展。
///< / summary>
public static class TransactionExtensions
{
///< summary>
///如果存储库实现< see cref =IHasTransactions/>开始一个事务。
///< / summary>
///< param name =repository>< / param>
public static void BeginTransaction(此IRepository存储库)
{
var transactionSupport = repository as IHasTransactions;
if(transactionSupport!= null)
{
transactionSupport.BeginTransaction();
}
}

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

p>

How do I encapsulate the saving of more than one entity in a transactional manner using the repository pattern? For example, what if I wanted to add an order and update the customer status based on that order creation, but only do so if the order completed successfully? Keep in mind that for this example, orders are not a collection inside the customer. They are their own entity.

This is just a contrived example, so I don’t really care whether orders should or should not be inside the customer object or even in the same bounded context. I don’t really care what underlying technology will be used (nHibernate, EF, ADO.Net, Linq, etc.) I just want to see what some calling code might look like in this admittedly contrived example of an all or nothing operation.

解决方案

Booting my computer this morning I faced the exact problem for a project I am working on. I had some ideas which lead to the following design - and comments would be more than awesome. Unfortunately the design suggested by Josh isn't possible, as I have to work with a remote SQL server and can't enable the Distribute Transaction Coordinator service it relies on.

My solution is based on a few yet simple changes to my existing code.

First, I have all my repositories implement a simple marker interface:

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

Secondly, I let all my transaction enabled repositories implement the following interface:

/// <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();
}

The idea is that in all my repositories I implement this interface and add code which introduces transaction directly depending on the actual provider (for fake repositories I have made a list of delegates which gets executed on commit). For LINQ to SQL it would be easy to make implementations such as:

#region IHasTransactions Members

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

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

#endregion

This of course requires that a new repository class is created for each thread, but this is reasonable for my project.

Each method using the repository needs to invoke the BeginTransaction() and the EndTransaction(), if the repository implements IHasTransactions. To make this call even easier, I came up with the following extensions:

/// <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();
        }
    }
}

Comments are appreciated!

这篇关于存储库模式中的事务的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆