c# - N 层应用程序中的多个 DbContext

标签 c# entity-framework-4.1 repository dbcontext unit-of-work

我正在创建我的第一个 N 层 MVC 应用程序,但我遇到了如何使用我的数据库优先方法管理多个 DbContexts 的障碍。

我有以下图层

Presentation
Service (WCF)
Business
Data Access

我不想在我的服务层中引用 Entity Framework ,但我不知道如何创建接口(interface)或其他东西来管理两个上下文。我让它在 IDatabaseFactory 中处理单个上下文,但我似乎找不到管理两个上下文的方法。

下面是我的 UnitOfWork,它是在我的 Service ctor 中创建的,但无论我怎么看,我仍然绑定(bind)到 SiteModelContainer,而实际上我还有另一个上下文。

public class UnitOfWork : IUnitOfWork
    {
        private SiteModelContainer _context;

        private readonly IDatabaseFactory _databaseFactory;

        protected SiteModelContainer SiteContext
        {
            get { return _context ?? (_context = _databaseFactory.Get()); }
        }

        public UnitOfWork(IDatabaseFactory factory)
        {
            _databaseFactory = factory;
            _context = _databaseFactory.Get();
        }
        //More code
    }



public class DatabaseFactory : Disposable, IDatabaseFactory
{
    private SiteModelContainer _dataContext;

    public SiteModelContainer Get()
    {
        return _dataContext ?? (_dataContext = new SiteModelContainer());
    }

    protected override void DisposeCore()
    {
        if (_dataContext != null)
            _dataContext.Dispose();
    }
}

最佳答案

为您的 Factory 和 UnitOfWork 提供通用类型参数可能是一种解决方案:

public class UnitOfWork<T> : IUnitOfWork<T>
    where T : DbContext, new()
{
    private T _context;

    private readonly IDatabaseFactory<T> _databaseFactory;

    protected T Context
    {
        get { return _context ?? (_context = _databaseFactory.Get()); }
    }

    public UnitOfWork(IDatabaseFactory<T> factory)
    {
        _databaseFactory = factory;
        _context = _databaseFactory.Get();
    }
    //More code
}

public class DatabaseFactory<T> : Disposable, IDatabaseFactory<T>
    where T : DbContext, new()
{
    private T _dataContext;

    public T Get()
    {
        return _dataContext ?? (_dataContext = new T());
    }

    protected override void DisposeCore()
    {
        if (_dataContext != null)
            _dataContext.Dispose();
    }
}

IDatabaseFactoryIUnitWork 接口(interface)也必须是通用的。

然后您可以为不同的上下文创建工作单元:

var factory1 = new DatabaseFactory<SiteModelContainer>();
var unitOfWork1 = new UnitOfWork<SiteModelContainer>(factory1);

var factory2 = new DatabaseFactory<AnotherModelContainer>();
var unitOfWork2 = new UnitOfWork<AnotherModelContainer>(factory2);

编辑:

要在您的服务类中摆脱对 EF 的依赖,您可以尝试这样的事情。该服务只知道这三个接口(interface):

public interface IUnitOfWorkFactory
{
    IUnitOfWork Create(string contextType);
}

public interface IUnitOfWork : IDisposable
{
    IRepository<TEntity> CreateGenericRepository<TEntity>()
        where TEntity : class;
    void Commit();
}

public interface IRepository<T>
{
    IQueryable<T> Find(Expression<Func<T, bool>> predicate);
    void Attach(T entity);
    void Add(T entity);
    // etc.
}

以下是特定于 EF 的特殊实现:

public class UnitOfWorkFactory : IUnitOfWorkFactory
{
    public IUnitOfWork Create(string contextType)
    {
        switch (contextType)
        {
            case "SiteModelContainer":
                return new UnitOfWork<SiteModelContainer>();
            case "AnotherModelContainer":
                return new UnitOfWork<AnotherModelContainer>();
        }

        throw new ArgumentException("Unknown contextType...");
    }
}

public class UnitOfWork<TContext> : IUnitOfWork
    where TContext : DbContext, new()
{
    private TContext _dbContext;

    public UnitOfWork()
    {
        _dbContext = new TContext();
    }

    public IRepository<TEntity> CreateGenericRepository<TEntity>()
        where TEntity : class
    {
        return new Repository<TEntity>(_dbContext);
    }

    public void Commit()
    {
        _dbContext.SaveChanges();
    }

    public void Dispose()
    {
        _dbContext.Dispose();
    }
}

public class Repository<T> : IRepository<T>
    where T : class
{
    private DbContext _dbContext;
    private DbSet<T> _dbSet;

    public Repository(DbContext dbContext)
    {
        _dbContext = dbContext;
        _dbSet = dbContext.Set<T>();
    }

    public IQueryable<T> Find(Expression<Func<T, bool>> predicate)
    {
        return _dbSet.Where(predicate);
    }

    public void Attach(T entity)
    {
        _dbSet.Attach(entity);
    }

    public void Add(T entity)
    {
        _dbSet.Add(entity);
    }

    // etc.
}

您的服务将得到一个 IUnitOfWorkFactory 注入(inject):

public class MyService
{
    private IUnitOfWorkFactory _factory;

    public MyService(IUnitOfWorkFactory factory)
    {
        _factory = factory;
    }

    public MyMethod()
    {
        using(var unitOfWork1 = _factory.Create("SiteModelContainer"))
        {
            var repo1 = unitOfWork1.
                CreateGenericRepository<SomeEntityTypeInSiteModel>();
            // Do some work
            unitOfWork1.Commit();
        }

        using(var unitOfWork2 = _factory.Create("AnotherModelContainer"))
        {
            var repo2 = unitOfWork2.
                CreateGenericRepository<SomeEntityTypeInAnotherModel>();
            // Do some work
            unitOfWork2.Commit();
        }
    }
}

当服务被创建时,工厂的具体实例被注入(inject):

var service = new MyService(new UnitOfWorkFactory());

请记住,艰苦的工作将在抽象存储库及其实现中进行。一旦您的服务类中不再有 EF 上下文,您就必须模仿 repo 接口(interface)中的许多方法来支持所有必要的场景来操作数据。

关于c# - N 层应用程序中的多个 DbContext,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6654355/

相关文章:

c# - 使用LINQ to SQL根据内存列表生成SQL

c# - Entity Framework 类问题中的两个相同类类型

linux - 如何配置Yum从自托管存储库更新

apache - Mercurial 推送,中止 : authorization failed

c# - 属性与字段 : Need help grasping the uses of Properties over Fields

c# - 列出 CHM 文件中的所有主题

c# - 给定开始日期和结束日期的简单方法

entity-framework - InvalidOperationException(集合已设置为 EntityCollection)在模板修改后所有属性都是虚拟的

entity-framework - 使用 POCO 时,我应该保持外键与引用同步吗

php - Symfony2 - 找不到自定义存储库