使用EF6 + DatabaseFirst的存储库模式

时间:2015-07-10 09:03:43

标签: entity-framework design-patterns entity-framework-6 repository-pattern unit-of-work

当前系统:

我正在开发一个多层次的项目,如下所示(按流程顺序),我正在学习并尝试首先在EF数据库上使用UOW实现Repo模式。

  • 服务(Web API)
  • 商业(C#班级图书馆)
  • 知识库(Repo Pattern + UOW)
  • ViewModels(由我的服务用于发送到我的UI图层)
  • 数据(实体)
  • 数据库(SQL Server)

存储库:

通用存储库:

public interface IRepository<TEntity> where TEntity : class
{
    void Create(TEntity entity);
    IQueryable<TEntity> ReadAll();
    TEntity ReadById();
    IQueryable<TEntity> Filter(Expression<Func<TEntity, bool>> predicate);
    TEntity ReadSingle(Expression<Func<TEntity, bool>> predicate);
    TEntity ReadSingleOrDefault(Expression<Func<TEntity, bool>> predicate);
    void Delete(TEntity entity);
}

存储库实施:

internal class RepositoryBase<TEntity> : IRepository<TEntity> where TEntity : class
{
    private bool _isDisposed;

    private readonly DbSet<TEntity> _dbSet;

    private Entities _entities;

    public RepositoryBase(Entities entities)
    {
        this._entities = entities;
        this._dbSet = _entities.Set<TEntity>();
    }

    public IQueryable<TEntity> ReadAll()
    {
        return _dbSet;
    }

    public TEntity ReadById()
    {
        // Dummy return. Don't worry about returning null will be implemented later.
        return null;
    }

    public IQueryable<TEntity> Filter(System.Linq.Expressions.Expression<Func<TEntity, bool>> predicate)
    {
        return _dbSet.Where(predicate);
    }

    public TEntity ReadSingle(System.Linq.Expressions.Expression<Func<TEntity, bool>> predicate)
    {
        return _dbSet.Single(predicate);
    }

    public TEntity ReadSingleOrDefault(System.Linq.Expressions.Expression<Func<TEntity, bool>> predicate)
    {
        return _dbSet.SingleOrDefault(predicate);
    }

    public void Create(TEntity entity)
    {
        _dbSet.Add(entity);
    }

    public void Delete(TEntity entity)
    {
        _dbSet.Remove(entity);
    }

    public virtual void Dispose(bool isManuallyDisposing)
    {
        if (!_isDisposed)
        {
            if (isManuallyDisposing)
                _tatwaEntities.Dispose();
        }

        _isDisposed = true;
    }

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

    ~RepositoryBase()
    {
        Dispose(false);
    }

Generic UOW:

public interface IUnitOfWork
{
    IRepository<MyEntity> MyEntityRepository { get; }
    //Other EntityRepositories

    void SaveChanges();
}

UOW实施:

public class UnitOfWork : IUnitOfWork, IDisposable
{
    private Entities _entities;

    public UnitOfWork()
    {
        _entities = new entities();
    }

    private IRepository<MyEntity> _myEntityRepository;
    public IRepository<MyEntity> MyEntityRepository
    {
        get
        {
            return _myEntityRepository ?? (_myEntityRepository = new RepositoryBase<MyEntity>(_entities));
        }
    }

    public void SaveChanges()
    {
        _entities.SaveChanges();
    }

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

    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        {
            if (_entities != null)
            {
                _entities.Dispose();
                _entities = null;
            }
        }
    }
}

业务逻辑

public List<MyViewModel> ReadMyViewModelList()
    {
        var myVMList = new List<MyViewModel>();
        using (var unitOfWork = new UnitOfWork())
        {
            userList.AddRange(unitOfWork.MyEntityRepository.ReadAll().Select(myEntity => new myViewModel
            {
                Prop1 = myEntity.Prop1,
                Prop1 = myEntity.Prop2,
        etc...
            }));
        }

        return myVMList;
    }

问题:

  • 我现在的问题是,在我的商业代码中,当我使用UOW获取时 数据,我必须将MyEntity转换为我认为错误的MyViewModel。 因为在UOW中使用Repo Pattern的整个想法是抽象的 并避免DB / Entity依赖于业务代码并能够 独立测试该层。
  • 我在ReposirotyBase和。中创建了我的DBEntities实例 UOW实现类,我认为这也是错误的 只创建一次,我不知道哪里是最好的地方 方式哟创建该实例。
  • 我的架构模型是正确的方法吗?如果没有人可以 请建议对其进行适当的更改,以使其更多 可测试的,可维护的和可靠的。

希望我的问题很精细。由于我处于建立架构的学习阶段,我感谢任何其他建议/意见/改进。

干杯。

1 个答案:

答案 0 :(得分:3)

这是我如何设计我的存储库,希望它有所帮助。合同如下:

public interface IFooRepository
{
  void Add(Foo foo);
  IReadonlyList<Foo> GetAll();
  IReadonlyList<Foo> GetAParticularSubset();
  Foo GetById(int id);
  Foo Remove(int id);
}
  • 注意集合语义。存储库的设计就好像它是可以添加,删除或查询的域对象的内存列表。 存储库的主要作用是充当我们假装数据库永远不存在的抽象。所以没有Update(),没有Commit()等。方法返回简单的只读对象列表,没有IQueryable,因为它是来自较低持久层的leaky abstraction,具有不需要的功能,如高级查询操作。

  • 在内部,具体的EF存储库有DbContext。他们使用它来添加/删除内容和查询,但不保存更改(见下文)。

  • 工作单位有一个愚蠢的简单签名:

    公共接口IUnitOfWork  {   void SaveChanges();  }

    这是“我提交我的业务交易,请将其刷新到所有必须存储的内容”,这是存储库中遗漏的部分。我们基本上不希望它在回购中因为分离关注。为人们提供一堆对象并提供跟踪这些对象与持久性存储相关的变化的上下文是两个截然不同的事情。

    事实证明,实体框架可以并且将会为您完成这两项工作。这两个功能都体现在DbContext中。但是,虽然Repository将调用DbContext.Foos.Add/Remove /查询方法,但UnitOfWork只会调用DbContext.SaveChanges()

  • 因此,RepositoryUnitOfWork都需要引用DbContext。如果要使对象中的更改生效,它必须是DbContext的相同实例。这可以通过DI容器注入相同的实例来完成(假设你正在使用它)。

  • 从消费者的角度来看,当您打算更改对象时,您只需将自己置于UnitOfWork范围内。对于不会修改结果的简单查询,只需使用Repo。