Answer the question
In order to leave comments, you need to log in
Implementing the UnitOfWork pattern when dealing with EntityFramework transactions?
Good afternoon!
There are 3 interfaces (infrastructure layer SomeCompany.SomeProduct.Infrastructure):
public interface IRepository<TEntity> where TEntity : Entity
{
TEntity Insert(TEntity entity);
// ... other CRUD methods
}
public interface IUnitOfWork
{
void Commit();
void Rollback();
}
public interface IUnitOfWorkFactory
{
IUnitOfWork Create(Isolationlevel isolationLevel);
IUnitOfWork Create(); // For default isolationLevel = Isolationlevel.ReadCommited
}
public class RepositoryBase<TEntity> : IRepository<TEntity> where TEntity : Entity
{
protected readonly IDbContext _dbContext;
public RepositoryBase(IDbContext dbContext)
{
_dbContext = dbContext;
}
public virtual TEntity Insert(TEntity entity)
{
if (entity == null)
throw new ArgumentNullException("entity");
return _dbContext.Set<TEntity>().Add(entity);
}
// other methods
}
public class UnitOfWork : IUnitOfWork
{
private readonly IDbContext _dbContext;
private readonly IsolationLevel _isolationLevel;
protected DbContextTransaction _transaction;
private bool _isTransactionBeginer = false;
private bool _commited = false;
protected bool _disposed = false;
public UnitOfWork(IDbContext dbContext, IsolationLevel isolationLevel)
{
if (dbContext == null)
throw new ArgumentNullException("dbContext");
_dbContext = dbContext;
_isolationLevel = isolationLevel;
if (_dbContext.Database.CurrentTransaction == null)
{
_transaction = _dbContext.Database.BeginTransaction(_isolationLevel);
_isTransactionBeginer = true;
}
}
// Dispose ...
public void Commit()
{
_dbContext.SaveChanges();
if (_isTransactionBeginer && _transaction != null)
{
_transaction.Commit();
_transaction = _dbContext.Database.BeginTransaction(_isolationLevel);
_commited = true;
}
}
}
public class UnitOfWorkFactory<TDbContext> : IUnitOfWorkFactory<TDbContext> where TDbContext : IDbContext
{
private readonly TDbContext _dbContext;
public UnitOfWorkFactory(TDbContext dbContext)
{
if (dbContext == null)
throw new ArgumentNullException("dbContext");
_dbContext = dbContext;
}
public IUnitOfWork Create(IsolationLevel isolationLevel)
{
return new UnitOfWork(_dbContext, isolationLevel);
}
public IUnitOfWork Create()
{
return Create(IsolationLevel.ReadCommitted);
}
}
public class CustomerService : ICustomerService
{
private readonly IUnitOfWorkFactory<IApplicationDbContext> _uowFactory;
private readonly ICustomerRepository _customerRepository;
public CustomerService(IUnitOfWorkFactory<IApplicationDbContext> uowFactory, ICustomerRepository _customerRepository)
{
_uowFactory = uowFactory;
_customerRepository = _customerRepository;
}
public void EditCustomer(int customerId, string customerName)
{
Customer customer = _customerRepository.FindbyId(customerId); // working with IApplicationDbContext (ApplicationDbContext) instance. Without transaction.
customer.ChangeName(customerName);
// some validation...
using(var uow = _uowFactory.Create()) // use same ApplicationDbContext instance, but start new transaction if not nested uow
{
_customerRepository.Update(customer);
uow.Commit(); // Transaction of ApplicationDbContext commited;
} // Close transaction if not nested uow
}
}
builder.RegisterType<ApplicationDbContext>()
.As<IApplicationDbContext>()
.InstancePerRequest();
Answer the question
In order to leave comments, you need to log in
2 question. Watching what you do. Usually it is created for a transaction. A transaction, in theory, should just fit into a web request.
3 question. Are you talking about IApplicationDbContext ? It is not clear what it is. Is it IDbContext ? If so, then it's strange to create it on Autofac's Lifetimescope, because then it will probably live in this container until the end of the domain's life. EF context is lightweight and involves frequent creation and deletion.
In general, somehow you treat UoW strangely: you oblige business logic to take care of transaction isolation. This "smells" like data access logic in a business model. Perhaps it would be better to pass the responsibility of UoW to the repository. I mean that the repository would have a Commit method and not atomic methods, but waiting for the transaction to complete. Then you don't have to think about it in business logic. Also, maybe TransactionScope would help to implement transaction isolation.
Didn't find what you were looking for?
Ask your questionAsk a Question
731 491 924 answers to any question