EF6 + DatabaseFirst 的存储库模式 [英] Repository pattern with EF6 + DatabaseFirst

本文介绍了EF6 + DatabaseFirst 的存储库模式的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当前系统:

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

I am working on a project which is multi layered as shown below (in order of flow) and I am learning and trying to implement Repo Pattern with UOW on EF Database first.

  • 服务(Web API)
  • 商业(C# 类库)
  • 存储库(回购模式 + 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);
    }

通用 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 和UOW 实施课程,我再次认为这是错误的,应该只能创建一次,我不确定哪里是最好的地方和创建该实例的方式.
  • 我的架构模型是正确的方法吗?如果不能有人可以请建议对其进行适当的更改,以使其更加可测试、可维护且可靠.

希望我的问题很详细.由于我正处于构建架构的学习阶段,因此我感谢任何其他建议/评论/改进.

Hope my question is elaborate. As I am in my learning phase of laying down architecture, I appreciate any additional suggestions/comments/improvements.

干杯.

推荐答案

以下是我设计存储库的方式,希望对您有所帮助.合同看起来像:

Here's how I design my repositories, hope it helps. The contract looks like :

public interface IFooRepository
{
  void Add(Foo foo);
  IReadonlyList<Foo> GetAll();
  IReadonlyList<Foo> GetAParticularSubset();
  Foo GetById(int id);
  Foo Remove(int id);
}

  • 注意集合语义.存储库的设计就像它是一个内存中的域对象列表,您可以从中添加、删除或查询.存储库的主要作用是充当抽象,让我们假装数据库从未存在过.所以没有 Update(),没有 Commit() 之类的.方法返回简单的只读对象列表,没有 IQueryable 因为它将是一个 来自较低持久层的泄漏抽象,具有不需要的功能,例如高级查询操作.

    • Notice the collection semantics. The repository is designed as if it were an in-memory list of domain objects you can add to, remove or query from. The primary role of a Repository is to act as an abstraction for us to pretend that the database never existed. So no Update(), no Commit() or such. Methods return simple readonly lists of objects, no IQueryable because it would be a leaky abstraction from your lower persistence layer with unneeded features such as advanced query manipulation.

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

      Internally, concrete EF repositories have a DbContext. They use it to add/remove stuff and query, but not to save changes (see below).

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

      Unit of Work has a dumb simple signature :

      公共接口 IUnitOfWork{无效的 SaveChanges();}

      public interface IUnitOfWork { void SaveChanges(); }

      这是存储库中缺少的我提交我的业务事务,请将其刷新到所有必须存储的内容"部分.我们基本上不希望它出现在回购中,因为关注点分离.为人们提供一堆对象和提供一个上下文来跟踪这些对象与持久存储相关的变化是两件非常不同的事情.

      This is the "I commit my business transaction, please flush it to whatever all this has to be stored into" part that's missing from the Repository. We basically don't want it to be in the repo because Separation of Concerns. Providing people with a bunch of objects and providing a context which keeps track of changes in these objects in relation with a persistent store are two very different things.

      棘手的是,事实证明,Entity Framework 可以并且将为您在幕后完成这两件事.这两个功能都体现在 DbContext 中.但是,虽然 Repository 会调用 DbContext.Foos.Add/Remove/query 方法,但 UnitOfWork 只会调用 DbContext.SaveChanges().

      The tricky thing is, as it turns out, Entity Framework can and will do both under the covers for you. Both features are embodied in a DbContext. But while Repository will call DbContext.Foos.Add/Remove/query methods, UnitOfWork will only call DbContext.SaveChanges().

      因此,RepositoryUnitOfWork 都需要引用 DbContext.如果您希望对象中的更改生效,它必须是相同的 DbContext 实例.这可以通过您的 DI 容器(假设您使用一个)将相同的实例注入到两者中来完成.

      Consequently, both Repository and UnitOfWork will need to have a reference to a DbContext. It has to be the same instance of DbContext if you want changes in the objects to take effect. This can be done by injecting the same instance into both via your DI Container (assuming you're using one).

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

      From a consumer standpoint, you only need to place yourself in a UnitOfWork scope when you intend to change the objects. For simple querying where the results won't be modified, just use a Repo.

      这篇关于EF6 + DatabaseFirst 的存储库模式的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆