多个DbContext的仓储和工作单元模式最佳实践

13

我计划使用ASP.NET MVC和Entity Framework 6(Code First / POCO)开发Web应用程序。 我还想在我的应用程序中使用通用存储库和工作单元模式。 由于此应用程序连接超过两个数据库,因此我必须在应用程序中使用多个DbContext。

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)
    }
}

如果我从控制器中调用IRepository和IUnitOfWork,如何知道匹配的上下文?这个问题的最佳实践是什么?

2个回答

16
我建议您使用构造函数参数创建UnitOfWork模式,以接受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];
    }
}

在这里,IDbContext是什么 -

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

仓库的实现将是 -

 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();
        }
    }

采用这种方法,您可以为每个独立的DBContext创建一个UnitOfWork,并且在UnitOfWork中具有特定的提交或回滚逻辑。


我应该如何在我的BaseController中调用IRepository和IUnitOfWork?我应该为每个模型创建数百个IRepository实例吗?例如,IRepository repositoryStudent = new Repository<StudentViewModel>()等等。 - derodevil
1
我认为BaseController不是初始化UoW的正确位置。相反,它应该在业务流程级别上根据需求基于DbContext创建特定的UoW,然后获取特定的repositories。 - ramiramilu
1
由于您的“UnitOfWork”没有析构函数和非托管资源,因此您不必调用“GC.SuppressFinalize(this)”,也不必实现处理程序。您只需要在“UnitOfWork.Dispose”中调用“_context.Dispose()”。 - AlbertK
@ramiramilu,我想请你提供获取这段代码的Github路径。 - Sandip
@ramiramilu,我的代码库出了问题,你能否通过Skype与我联系并查看一下吗? - xxxsenatorxxx

1
我会将UnitOfWork实现为一个ActionAttribute,其中OnActionExecuting打开事务,OnActionExecuted如果一切正常,则提交事务,如果在ActionContext中有异常,则回滚事务。棘手的问题是你有两个DbContexts。我认为,你应该延迟创建dbContexts。引入一种标志变量,并在UnitOfWork.OnActionExecuting中将其设置为True。然后,当您第一次接触dbContext时,应检查是否正在处理UnitOfWork,如果是,则应为此特定dbContext打开事务。所有打开的事务都可以放入一个可从UnitOfWork.ActionExecuted访问的列表中。最后,检查ActionContext中是否有任何异常:是-Rollback,否-Commit

1
提交和回滚事务没有问题。我需要关于使用多个DbContext创建Repsitory和UoW模式的最佳实践的解释,因为我的应用程序必须连接到同一服务器和不同服务器上的多个数据库。我希望有一个单一的UoW和单一的通用仓储库可以处理这些DbContexts。 - derodevil

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接