N层应用程序中的多个DbContexts

时间:2011-07-11 18:00:42

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

我正在创建我的第一个N-Tier MVC应用程序,我遇到了如何使用我的数据库第一种方法管理多个DbContexts的路障。

我有以下图层

Presentation
Service (WCF)
Business
Data Access

我不希望在我的服务层中使用实体框架引用,但是我没有看到如何创建接口或管理两个上下文的东西。我在IDatabaseFactory中使用了单个上下文,但我似乎找不到管理两个上下文的方法。

以下是我的服务ctor中创建的UnitOfWork,但我看到它的每一个方面我仍然与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();
    }
}

2 个答案:

答案 0 :(得分:17)

为您的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接口也必须是通用的。

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

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

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

修改

要摆脱服务类中对EF的依赖,您可以尝试这样的方法。该服务只知道这三个接口:

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注入:

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

创建服务时,将注入工厂的具体实例:

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

请记住,艰苦的工作将在抽象存储库及其实现中。只要您的服务类中不再具有EF上下文,就必须在repo界面中模仿许多方法,以支持操作数据的所有必要方案。

答案 1 :(得分:0)

您可以创建一个包装器,它是跨DbContexts的通用存储库(并利用底层的ObjectContext来支持它)。

以下是我过去使用过的一个示例(它还将您的代码与对Entity Framework的任何直接依赖关系分离)。

// Make your DbContext inherit from this. This goes in your Unit of Work.
public interface IEntitySetProvider : IDisposable
{
    IEntitySet<T> CreateEntitySet<T>();
}

// This is your adapted DBContext
public class MyDbContext1 : DbContext, IEntitySetProvider
{
    public IEntitySet<T> CreateEntitySet<T>()
    {
        return new EntitySet<T>(((IObjectContextAdapter)this).CreateObjectSet<T>());
    }

    .
    .
    .
}


/// <summary>
///   A wrapper for an IQueryable that exposes AddNew and Attach methods.
/// </summary>
/// <typeparam name = "T"></typeparam>
public interface IEntitySet<T> : IQueryable<T>
{
    /// <summary>
    ///   Attaches the specified value and considers it new.
    /// </summary>
    /// <param name = "value">The value.</param>
    void AddNew(T value);

    /// <summary>
    ///   Attaches the specified value and considers it modified.
    /// </summary>
    /// <param name = "value">The value.</param>
    void Attach(T value);
}

/// <summary>
///   An IEntitySet for Entity Framework.
/// </summary>
/// <typeparam name = "T"></typeparam>
internal class EntitySet<T> : IEntitySet<T> where T : class
{
    private readonly ObjectSet<T> _objectSet;

    public EntitySet(ObjectSet<T> objectSet)
    {
        _objectSet = objectSet;
    }

    #region IEntitySet<T> Members

    public void AddNew(T value)
    {
        _objectSet.AddObject(value);
    }

    public void Attach(T value)
    {
        _objectSet.Attach(value);
        _objectSet.Context.ObjectStateManager.ChangeObjectState(value, EntityState.Modified);
    }

    public IEnumerator<T> GetEnumerator()
    {
        return ((IQueryable<T>) _objectSet).GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return ((IQueryable) _objectSet).GetEnumerator();
    }

    public Type ElementType
    {
        get { return ((IQueryable<T>) _objectSet).ElementType; }
    }

    public Expression Expression
    {
        get { return ((IQueryable<T>) _objectSet).Expression; }
    }

    public IQueryProvider Provider
    {
        get { return ((IQueryable<T>) _objectSet).Provider; }
    }

    #endregion
}