7

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

我有以下几层

Presentation
Service (WCF)
Business
Data Access

我不想在我的服务层中引用实体框架,但我看不到如何创建接口或其他东西来管理两个上下文。我让它使用 IDatabaseFactory 中扭曲的单个上下文,但我似乎找不到管理两个上下文的方法。

下面是我UnitOfWork在我的 Service ctor 中创建的,但我从每一个角度来看,我仍然与 相关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();
    }
}
4

2 回答 2

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 接口中模拟许多方法,以支持所有必要的场景来操作数据。

于 2011-07-11T18:23:30.327 回答
0

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

这是我过去使用的一个示例(它还将您的代码与对实体框架的任何直接依赖解耦)。

// 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
}
于 2011-07-11T18:13:38.800 回答