13

I plan to develop a web application using ASP.NET MVC with Entity Framework 6 (Code First / POCO). I also want to use generic Repository and Unit of Work Pattern in my application. This application connects to more than two databases so, I have to use multiple DbContext in the application.

public class ContextOne : DbContext
{
    public DbSet<Model_One1>
    public DbSet<Model_One2>
}

public class ContextTwo : DbContext
{
    public DbSet<Model_Two1>
    public DbSet<Model_Two2>
}

public class ContextThree : DbContext
{
    public DbSet<Model_Three1>
    public DbSet<Model_Three2>
}

public interface IRepository<T> where T : DbContext
{
    void Add<T>(T entity) where T : class;
}

public class Repository<T> where T : DbContext
{
    void Add<T>(T entity) where T : class
    {
        //T is DbContext and Model. So confusing
    }
}

public interface IUnitOfWork<IRepository>
{
}

public class UnitOfWork<IRepository>
{
    //IRepository contains more than one DbContext how can I initiate them here?
}

//in application should look like this
public class BaseController : Controller
{
    protected IRepository repository = new .. //here I have no idea with multiple DbContext
}

public class HomeController : BaseController
{
    public ActionResult Add(Model_Two2 model)
    {
        base.repository.Add<Model_Two2>(model)
    }
}

If I call the IRepository and IUnitOfWork from Controller how can I know the matching context? What's the best practice of this problem?

derodevil
  • 811
  • 1
  • 11
  • 37

2 Answers2

16

I would suggest you to create UnitOfWork pattern with a Constructor parameter to accept DbContext -

public class UnitOfWork : IUnitOfWork
{
    private readonly IDbContext _context;

    private bool _disposed;
    private Hashtable _repositories;

    public UnitOfWork(IDbContext context)
    {
        _context = context;
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    public void Save()
    {
        _context.SaveChanges();
    }

    public virtual void Dispose(bool disposing)
    {
        if (!_disposed)
            if (disposing)
                _context.Dispose();

        _disposed = true;
    }

    public IRepository<TEntity> Repository<TEntity>() where TEntity : class
    {
        if (_repositories == null)
            _repositories = new Hashtable();

        var type = typeof(TEntity).Name;

        if (_repositories.ContainsKey(type)) return (IRepository<TEntity>) _repositories[type];

        var repositoryType = typeof (Repository<>);

        var repositoryInstance =
            Activator.CreateInstance(repositoryType
                .MakeGenericType(typeof (TEntity)), _context);

        _repositories.Add(type, repositoryInstance);

        return (IRepository<TEntity>) _repositories[type];
    }
}

where IDbContext is -

public interface IDbContext
{
    IDbSet<T> Set<T>() where T : class;
    int SaveChanges();
    void Dispose();
}

And the repository implementation would be -

 public class Repository<TEntity> : IRepository<TEntity> where TEntity : class
    {
        internal IDbContext Context;
        internal IDbSet<TEntity> DbSet;

        public Repository(IDbContext context)
        {
            Context = context;
            DbSet = context.Set<TEntity>();
        }

        public virtual TEntity FindById(object id)
        {
            return DbSet.Find(id);
        }

        public virtual void Update(TEntity entity)
        {
            DbSet.Attach(entity);
        }
        public virtual void Delete(object id)
        {
            var entity = DbSet.Find(id);
            var objectState = entity as IObjectState;
            if (objectState != null)
                objectState.State = ObjectState.Deleted;
            Delete(entity);
        }

        public virtual void Delete(TEntity entity)
        {
            DbSet.Attach(entity);
            DbSet.Remove(entity);
        }

        public virtual void Insert(TEntity entity)
        {
            DbSet.Attach(entity);
        }

        public virtual List<TEntity> GetAll()
        {
            return DbSet.ToList();
        }
    }

With this approach you can create a UnitOfWork for individual DBContext and you have specific logic to commit or rollback in UnitOfWork.

ramiramilu
  • 17,044
  • 6
  • 49
  • 66
  • How should I call the IRepository and IUnitOfWork in my BaseController? Should I create hundreds of IRepository instance for each model? For example, `IRepository repositoryStudent = new Repository()` etc, etc. – derodevil Sep 28 '14 at 12:20
  • 1
    I do not think `BaseController` would be the right place to initiate a UoW. Instead it should be created on demand basis at your business process level. So create specific UoW based out of DbContexts as per requirement basis and then get specific repositories. – ramiramilu Sep 28 '14 at 14:15
  • 1
    Since your `UnitOfWork` doesn't have a destructor and unmanaged resources you don't have to call `GC.SuppressFinalize(this)` and don't have to implement dispose pattern. You need to call just `_context.Dispose()` in `UnitOfWork.Dispose` – AlbertK Oct 24 '17 at 09:10
  • @ramiramilu I would request to provide github path from where you have get this code – Sandip Jul 11 '18 at 11:02
  • @ramiramilu i have a problem in my repository. can you skype to me and see it? – xxxsenatorxxx Jun 10 '19 at 03:37
1

I would implement UnitOfWork as an ActionAttribute where OnActionExecuting I open transaction, and OnActionExecuted I commit transaction if everything is ok, if there is exception in ActionContext, transaction should be rolled back.

The tricky thing is that you have 2 DbContexts. I think, you should have lazy creation of dbContexts. Introduce a kind of flag variable and set it to True in UnitOfWork.OnActionExecuting. Then, when you first time touch dbContext, you should check if you are dealing with UnitOfWork, and if yes, you should open transaction for this particular dbContext. All the open transactions could be put into a List which is accessible from UnitOfWork.ActionExecuted. Finally, check if there are any exceptions in ActionContext: yes - Rollback, no - Commit.

Andrei
  • 42,814
  • 35
  • 154
  • 218
  • 1
    No problem for Commit and Rollback transaction. I need explanation about the best practice to create Repsitory and UoW pattern with multiple DbContext since my application has to connect to multiple databases on the same and different servers. I want a single UoW with single generic repository can handle these DbContexts. – derodevil Sep 28 '14 at 07:41