为什么在 Dispose() 方法中隐式提交工作单元不好?
我编写了一个 UnitOfWork 实现,它不公开公共 Commit()
方法。相反,UnitOfWork 实现了 IDisposable,并在 Dispose() 方法中执行 Commit。我认为这没有任何直接问题,但这似乎不正统,所以我想知道你们是否可以指出一些我忽略的不这样做的主要原因。
这是一些示例代码:
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;
}
}
}
}
I wrote a UnitOfWork implementation that does not expose a public Commit()
method. Instead the UnitOfWork implements IDisposable
and the Commit is executed in the Dispose()
method. I don't see any immediate problems with this, but it seems unorthodox so I want to know if you guys can point out some major reason not to do this that I am overlooking.
Here is some sample code:
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;
}
}
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
因为未处理的异常将提交事务。异常意味着某些事情没有按计划进行=不应提交事务。
如果在处置之前尚未调用
Commit
,最好在Dispose
中Rollback
。Because an unhandled exception will commit the transaction. And an exception implies that something did not go as planned = transaction should not be committed.
It's far better to
Rollback
inDispose
ifCommit
has not been called before disposing.如果您在
using
块中调用的函数之一抛出异常怎么办?它可能会使您的工作单元处于不一致/不完整的状态,然后您再提交。What if one of your functions you call within the
using
block throws an exception? It might leave your unit of work in an inconsistent/incomplete state which you then commit.