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

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

问题描述

当前系统:



我正在开发一个多层次的项目,如下所示(in我正在学习并试图在EF数据库上实现具有UOW的Repo模式。




  • 服务(Web API)

  • 业务(C#类库)

  • 存储库(Repo Pattern + UOW)

  • ViewModels(由我的服务使用发送到我的UI层)

  • 数据(实体)

  • 数据库(SQL Server)



存储库:



通用存储库:

  public interface IRepository< TEntity>其中TEntity:class 
{
void Create(TEntity entity);
IQueryable< TEntity> ReadAll();
TEntity ReadById();
IQueryable< TEntity> Filter(表达式< Func< TEntity,bool>>谓词);
TEntity ReadSingle(Expression< Func< TEntity,bool>> predicate);
TEntity ReadSingleOrDefault(Expression< Func< TEntity,bool>> predicate);
void Delete(TEntity entity);
}

资源库实现:

 内部类RepositoryBase< TEntity> :IRepository< TEntity>其中TEntity:class 
{
private bool _isDisposed;

private readonly DbSet< TEntity> _dbSet;

私人实体_entities;

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

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

public TEntity ReadById()
{
//虚拟返回。不用担心返回null会稍后实现。
返回null;
}

public IQueryable< TEntity>过滤器(System.Linq.Expressions.Expression< Func< TEntity,bool>>谓词)
{
return _dbSet.Where(predicate);
}

public TEntity ReadSingle(System.Linq.Expressions.Expression< Func< TEntity,bool>>谓词)
{
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;
//其他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 disposal)
{
if(disposal)
{
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()。选择(myEntity => new myViewModel
{
Prop1 = myEntity.Prop1,
Prop1 = myEntity.Prop2,
etc ...
}));
}

return myVMList;
}

问题:




  • 我现在的问题是,在我的商业代码中,当我使用UOW获取
    数据时,我必须投递MyEntity到MyViewModel,我认为是错误的。
    因为使用Repo Pattern与UOW的整个想法是抽象
    并避免DB / Entity依赖于业务代码,并能够
    独立测试该层。

  • 我在ReposirotyBase和
    UOW实现类中创建了一个DBEntities实例,我再次认为这是错误的,应该
    只能被创建一次,我不知道哪里是最好的地方和
    方式哟创建该实例。

  • 我的架构模型是正确的方法吗?如果不能有人
    请建议适当的更改,以使其更多
    可测试,可维护和声音。


$ b $希望我的问题很详细。因为我正在学习架构设计阶段,感谢任何其他建议/意见/改进。



干杯。

解决方案

这是我如何设计我的存储库,希望它有帮助。合同看起来像:

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




  • 注意收集语义。存储库的设计好像是可以添加到,删除或查询的域对象的内存列表。一个Repository的主要作用是作为我们假装数据库不存在的抽象。所以没有 Update(),no Commit()等等。方法返回简单的只读对象列表,没有 IQueryable ,因为它将是一个渗漏抽象,从较低的持久层与不需要的功能,如高级查询操作。


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


  • 工作单位有一个简单的签名: p>

    public interface IUnitOfWork
    {
    void SaveChanges();
    }



    这是我提交我的商业交易,请将其冲洗到所有这些都要存储到存储库中缺少的部分。我们基本上不希望它在回购中,因为分离担忧。为人们提供一堆对象,并提供跟踪持久存储相关的这些对象变化的上下文是两个完全不同的事情。



    棘手的事情是事实证明,实体框架可以并将为您提供封面。这两个特征都体现在一个 DbContext 中。但是,当Repository将调用 DbContext.Foos.Add / Remove / query方法时,UnitOfWork将只调用 DbContext.SaveChanges()


  • 因此, Repository UnitOfWork 将需要引用 DbContext 。如果要使对象中的更改生效,它必须是DbContext的相同实例。这可以通过您的DI容器(假设您正在使用它们)将相同的实例注入到两者中。


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



Current System:

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.

  • Service (Web API)
  • Business (C# Class Library)
  • Repository (Repo Pattern + UOW)
  • ViewModels (Used by my Service for sent to my UI layer)
  • Data (Entities)
  • Database (SQL Server)

Repository:

Generic Repository:

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

Repository Implementation:

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 Implementation:

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

Business Logic

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

Problem:

  • My problem now is that, in my business code, when i use UOW to get data, I have to cast MyEntity to MyViewModel which I think is wrong. because the whole idea of using Repo Pattern with UOW is to abstract and avoid DB/Entity dependency on the business code and be able to independently test that layer.
  • I am creating an instance of my DBEntities in both ReposirotyBase and UOW implemntation classes, which again I think is wrong and should only be created once and I am not sure where is the best place and way yo create that instance.
  • Is my architecture model the correct approach? If not can someone please suggest the appropriate changes to it so as to make it more testable, maintainable and sound.

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

Cheers.

解决方案

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

  • 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.

  • 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 :

    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.

    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().

  • 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).

  • 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天全站免登陆