【问题标题】:Why is it bad to implicitly commit a Unit of Work in a Dispose() method?为什么在 Dispose() 方法中隐式提交工作单元是不好的?
【发布时间】:2011-08-23 22:06:39
【问题描述】:

我编写了一个不公开公共Commit() 方法的UnitOfWork 实现。取而代之的是 UnitOfWork 实现 IDisposable 并且 Commit 在 Dispose() 方法中执行。我没有看到任何直接的问题,但它似乎不正统,所以我想知道你们是否可以指出一些我忽略的不这样做的主要原因。

这里是一些示例代码:

public class DataService
{
    public DataService()
    {
        _db = new MyDataContext();
        _exceptionHandler = new SqlExceptionHandler();
    }
    private readonly MyDataContext _db;
    private readonly SqlExceptionHandler _exceptionHandler;
    public void Add(Product product, Cart cart)
    {
        using(UnitOfWork unitOfWork = new UnitOfWork(_db, ex=>_exceptionHandler.Handle(ex)))
        {
            unitOfWork.Create<CartItem>(new CartItem{CartId = cart.Id, ProductId = product.Id});
            unitOfWork.Update<Product>(x => x.Id == product.Id, product => { product.OrderCount++; });
        }
    }
}


public class UnitOfWork : IDisposable
{
    private readonly DataContext _dataContext;
    private readonly Func<Exception, bool> _handleException;
    private bool _dirty;

    public UnitOfWork(DataContext dataContext, Func<Exception,bool> handleException)
    {
        _dataContext = dataContext;
        _handleException = handleException;
    }

    private Table<T> Table<T>()
        where T: class
    {
        return _dataContext.GetTable<T>();
    }
    private T[] Find<T>(Expression<Func<T,bool>> select)
        where T: class
    {
        return Table<T>().Where(select).ToArray();
    }

    public void Create<T>(T persistentObject)
        where T: class 
    {
        Table<T>().InsertOnSubmit(persistentObject);
        _dirty = true;
    }
    public void Update<T>(Expression<Func<T, bool>> select, Action<T> update)
        where T : class
    {
        var items = Find<T>(select);
        if (items.Length > 0)
        {
            foreach (var target in items) update(target);
            _dirty = true;
        }
    }
    public void Delete<T>(Expression<Func<T, bool>> select)
        where T : class 
    {
        var items = Find<T>(select);
        switch (items.Length)
        {
            case 0: return;
            case 1:
                Table<T>().DeleteOnSubmit(items[0]);
                break;
            default:
                Table<T>().DeleteAllOnSubmit(items);
                break;
        }
        _dirty = true;
    }

    public void Dispose()
    {
        if (_dirty)
        {
            Commit(1);
        }
    }

    private void Commit(int attempt)
    {
            try
            {
                _dataContext.SubmitChanges();
            }
            catch (Exception exception)
            {
                if (attempt == 1 && _handleException != null && _handleException(exception))
                {
                    Commit(2);
                }
                else
                {
                    throw;
                }
            }
    }
}  

【问题讨论】:

  • +1 因为这是一个有趣的问题,而不是因为我同意你想做的事情 ;)

标签: .net linq-to-sql design-patterns datacontext unit-of-work


【解决方案1】:

因为未处理的异常将提交事务。一个例外意味着某些事情没有按计划进行 = 不应提交事务。

如果在处理之前没有调用Commit,最好在Dispose 中使用Rollback

【讨论】:

  • @jgauffin,看看我的Dispose() 方法。我不认为这会是一个问题。
  • +1,这就是我要回答的问题。隐式回滚要好得多。此外,它与其他 API(例如 ADO.NET 事务)一致
  • a) 除了 UOW 语句之外,using 块内是否没有任何逻辑?如果是这样,那些也可能产生异常 => 提交。 b) 由于我看不到 unitOfWork.Create&lt;CartItem&gt; 是如何工作的,所以我不能真正说出您如何处理其中的异常。
  • @smartcaveman:如果您需要执行更新,然后执行其他操作,然后执行删除怎么办?如果更新完成没有错误,那么在做“其他事情”时发生错误,删除不会被执行,但你仍然会提交 UnitOfWork
  • 我认为在一天结束的时候,你不会是唯一一个阅读你的代码的人,所以你需要设计你的代码来显示意图。我认为在您的事务代码之后立即使用“可见” Commit 方法对其他人(以及从现在起一周后的您自己)发生的事情更加明显。
【解决方案2】:

如果您在using 块中调用的某个函数引发异常怎么办?它可能会使您的工作单元处于不一致/不完整的状态,然后您提交。

【讨论】:

    猜你喜欢
    • 2013-12-21
    • 2017-12-05
    • 1970-01-01
    • 1970-01-01
    • 2011-03-21
    • 1970-01-01
    • 2010-10-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多