I developed an ASP.NET MVC application to manage projects, using Entity Framework 6.0 and the Repository design pattern. Now I want to integrate transactions in order to insure that some insert/update database operations respect the ACID principal and especially the atomicity principal.
Below is a snippets of my Generic repository :
1. Generic repository interface
public interface IGenericRepository<T> : IRepository where T : BaseEntity
{
void Create(T entity);
void Delete(T entity);
IEnumerable<T> GetAll();
void Update(T entity);
}
2. Generic repository class
public abstract class GenericRepository<T> : IGenericRepository<T> where T : BaseEntity
{
protected IContext _context;
protected IDbSet<T> _dbset;
public GenericRepository(IContext context)
{
_context = context;
_dbset = _context.Set<T>();
}
public virtual void Create(T entity)
{
if (entity == null)
{
throw new ArgumentNullException("entity");
}
_dbset.Add(entity);
_context.SaveChanges();
}
public virtual void Update(T entity)
{
if (entity == null) throw new ArgumentNullException("entity");
_context.Entry(entity).State = System.Data.Entity.EntityState.Modified;
_context.SaveChanges();
}
public virtual void Delete(T entity)
{
if (entity == null) throw new ArgumentNullException("entity");
_dbset.Remove(entity);
_context.SaveChanges();
}
public virtual IEnumerable<T> GetAll()
{
return _dbset.AsEnumerable<T>();
}
}
3. My Icontext implementation
public interface IContext
{
IDbSet<Projet> Projects { get; set; }
IDbSet<Task> Tasks{ get; set; }
IDbSet<Entite> Entities { get; set; }
DbSet<TEntity> Set<TEntity>() where TEntity : class;
DbEntityEntry<TEntity> Entry<TEntity>(TEntity entity) where TEntity : class;
int SaveChanges();
}
4. The Project Entity
public class ProjectRepository : GenericRepository<Projet>, IProjectRepository
{
IContext _context;
public ProjectRepository(IContext context) : base(context)
{
_context = context;
_dbset = _context.Set<Projet>();
}
public Projet GetProjectById(int Id)
{
return _dbset.FirstOrDefault(x=>x.Id == Id);
}
}
So, what I want to do, is to have transactions work with the model above. For example, when a project is created with his tasks I want to use a transaction to save the Project and the Task entities, so I'm sure that the insert of theses entities would be an atomic operation.
thanks for your help and suggestions.
Usually, your repositories are injected into engine / service classes. I will assume we have a ProjectEngine.cs where ProjectRepo and TaskRepo are injected. The code will look as follows:
public class ProjectEngine : IProjectEngine
{
IProjectRepository projectRepository;
ITaskRepository taskRepository;
public ProjectEngine(
IProjectRepository ProjectRepository,
ITaskRepository TaskRepository)
{
projectRepository = ProjectRepository;
taskRepository = TaskRepository;
}
public void CreateProject(CreateProjectRequest createProjectRequest)
{
using (TransactionScope scope = new TransactionScope())
{
// these operations are atomic since they below to same transactionscope
projectRepository.Add([project]);
taskRepository.Add([tasks]);
// data will not be affected until complete operation is called. Any database exception would rollback the transaction.
scope.Complete();
}
}
}
To recap, the best way to do that is by including multiple repositories operations within same transactionscope.