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;
}
}
}
}
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
in Dispose
if Commit
has not been called before disposing.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With