#.net #linq-to-sql #design-patterns #datacontext #unit-of-work
#.net #linq-to-sql #шаблоны проектирования #datacontext #единица работы
Вопрос:
Я написал реализацию UnitOfWork, которая не предоставляет общедоступный Commit()
метод. Вместо этого UnitOfWork реализует IDisposable
и фиксация выполняется в 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 amp;amp; _handleException != null amp;amp; _handleException(exception))
{
Commit(2);
}
else
{
throw;
}
}
}
}
Комментарии:
1. 1 потому что это интересный вопрос, а не потому, что я одобряю то, что вы хотите сделать 😉
Ответ №1:
Потому что необработанное исключение зафиксирует транзакцию. И исключение подразумевает, что что-то пошло не так, как планировалось = транзакция не должна быть зафиксирована.
Гораздо лучше Rollback
использовать Dispose
if Commit
, который не был вызван перед удалением.
Комментарии:
1. @jgauffin, взгляни на мой
Dispose()
метод. Я не думаю, что это было бы проблемой.2. 1, вот на что я собирался ответить. Гораздо лучше неявно выполнять откат. Кроме того, это совместимо с другими API (например ADO.NET транзакции)
3. a) У вас никогда нет никакой логики внутри
using
блока, кроме инструкций UOW? Если это так, они также могут генерировать исключения => зафиксировать. б) Поскольку я не вижу, какunitOfWork.Create<CartItem>
работает, я не могу точно сказать, как вы обрабатываете исключения в них.4. @smartcaveman: что, если вам нужно выполнить обновление, затем что-то еще, затем удаление? Если обновление завершается без ошибок, тогда возникает ошибка при выполнении «чего-то другого», удаление не будет выполнено, но вы все равно зафиксируете единицу работы
5. Я думаю, в конце концов, вы не будете единственным, кто читает ваш код, поэтому вам нужно разработать свой код, чтобы показать намерение. Я думаю, что более очевидно, что происходит для других (и для вас самих через неделю) с «видимым» методом фиксации сразу после вашего кода транзакции.
Ответ №2:
Что, если одна из ваших функций, которую вы вызываете в using
блоке, выдает исключение? Это может привести к тому, что ваша единица работы окажется в несогласованном / незавершенном состоянии, которое вы затем зафиксируете.