Entity framework EntityFramework6代码优先-存储库实现好吗?

Entity framework EntityFramework6代码优先-存储库实现好吗?,entity-framework,repository-pattern,unit-of-work,Entity Framework,Repository Pattern,Unit Of Work,我将要实现一个EntityFramework6设计,其中包含一个存储库和工作单元 有这么多的文章,我不确定最好的建议是什么:例如,我真的很喜欢这里实现的模式:因为文章中提出的原因 然而,Tom Dykstra(微软Web平台和工具内容团队的高级编程作家)建议在另一篇文章中完成: 我订阅了Pluralsight,每次在课程中使用它时,它的实现方式都略有不同,因此选择设计很困难 有些人似乎认为工作单元已经由DbContext实现了,就像这样,所以我们根本不需要实现它 我意识到以前有人问过这类问题,这

我将要实现一个EntityFramework6设计,其中包含一个存储库和工作单元

有这么多的文章,我不确定最好的建议是什么:例如,我真的很喜欢这里实现的模式:因为文章中提出的原因

然而,
Tom Dykstra(微软Web平台和工具内容团队的高级编程作家)
建议在另一篇文章中完成:

我订阅了
Pluralsight
,每次在课程中使用它时,它的实现方式都略有不同,因此选择设计很困难

有些人似乎认为工作单元已经由
DbContext
实现了,就像这样,所以我们根本不需要实现它

我意识到以前有人问过这类问题,这可能是主观的,但我的问题是直接的:

我喜欢第一篇文章(CodeFizzle)中的方法,想知道它是否比其他方法更易于维护和测试,并且是否安全


任何其他视图都非常受欢迎。

我总是先使用UoW和EF代码。我发现管理上下文、防止内存泄漏等更有效、更容易。你可以在我的github上找到一个变通方法的例子:在雷达项目中


如果您对此有任何疑问,请随时询问。

DbContext
确实是使用工作单元模式构建的。它允许它的所有实体在我们与它们一起工作时共享相同的上下文。此实现是
DbContext
的内部实现

但是,应该注意的是,如果实例化两个
DbContext
对象,它们都不会看到各自正在跟踪的另一个实体。它们彼此绝缘,这可能会有问题

在构建MVC应用程序时,我希望确保在请求过程中,所有数据访问代码都在单个
DbContext
中工作。为了实现这一点,我将工作单元作为
DbContext
外部的模式应用

这是我正在构建的烧烤食谱应用程序中的工作单元对象:

public class UnitOfWork : IUnitOfWork
{
    private BarbecurianContext _context = new BarbecurianContext();
    private IRepository<Recipe> _recipeRepository;
    private IRepository<Category> _categoryRepository;
    private IRepository<Tag> _tagRepository;

    public IRepository<Recipe> RecipeRepository
    {
        get
        {
            if (_recipeRepository == null)
            {
                _recipeRepository = new RecipeRepository(_context);
            }
            return _recipeRepository;
        }
    }

    public void Save()
    {
        _context.SaveChanges();
    }
    **SNIP**
现在,在我们的操作中,我们可以确保所有数据访问代码都将使用相同的
DbContext

    [HttpPost]
    public ActionResult Create(CreateEditRecipeViewModel model)
    {
        Mapper.CreateMap<CreateEditRecipeViewModel, Recipe>().ForMember(r => r.IngredientAmounts, opt => opt.Ignore());

        Recipe recipe = Mapper.Map<CreateEditRecipeViewModel, Recipe>(model);
        _recipeRepository.Create(recipe);
        foreach(Tag t in model.Tags){
             _tagRepository.Create(tag); //I'm using the same DbContext as the recipe repo!
        }
        _unitOfWork.Save();
[HttpPost]
公共操作结果创建(CreateEditRecipeViewModel模型)
{
Mapper.CreateMap();
配方=映射器.Map(模型);
_Recipereposition.创建(配方);
foreach(model.Tags中的标记t){
_tagRepository.Create(tag);//我使用的是与配方repo相同的DbContext!
}
_unitOfWork.Save();

@Chris Hardie是正确的,EF实现了UoW开箱即用。但是许多人忽略了一个事实,即EF也实现了一个开箱即用的通用存储库模式:

var repos1 = _dbContext.Set<Widget1>();
var repos2 = _dbContext.Set<Widget2>();
var reposN = _dbContext.Set<WidgetN>();
var repos1=_dbContext.Set();
var repos2=_dbContext.Set();
var reposN=_dbContext.Set();
…这是一个非常好的通用存储库实现,内置在工具本身中

当DbContext为您提供了所需的一切时,为什么还要费劲地创建大量其他接口和属性呢?如果您希望在应用程序级接口后面抽象DbContext,并且希望应用命令查询隔离,您可以做如下简单的事情:

public interface IReadEntities
{
    IQueryable<TEntity> Query<TEntity>();
}

public interface IWriteEntities : IReadEntities, IUnitOfWork
{
    IQueryable<TEntity> Load<TEntity>();
    void Create<TEntity>(TEntity entity);
    void Update<TEntity>(TEntity entity);
    void Delete<TEntity>(TEntity entity);
}

public interface IUnitOfWork
{
    int SaveChanges();
}
公共接口IReadEntities
{
IQueryable查询();
}
公共接口IWriteEntities:IReadEntities、IUnitOfWork
{
可调负荷();
无效创建(tenty实体);
无效更新(潜在实体);
无效删除(潜在实体);
}
公共接口工作单元
{
int SaveChanges();
}
您可以将这3个接口用于所有实体访问,而不必担心将3个或更多不同的存储库注入到处理3个或更多实体集的业务代码中。当然,您仍然可以使用IoC来确保每个web请求只有1个DbContext实例,但所有3个接口都是由相同的类,这使它更容易

public class MyDbContext : DbContext, IWriteEntities
{
    public IQueryable<TEntity> Query<TEntity>()
    {
        return Set<TEntity>().AsNoTracking(); // detach results from context
    }

    public IQueryable<TEntity> Load<TEntity>()
    {
        return Set<TEntity>();
    }

    public void Create<TEntity>(TEntity entity)
    {
        if (Entry(entity).State == EntityState.Detached)
            Set<TEntity>().Add(entity);
    }

    ...etc
}
公共类MyDbContext:DbContext,IWriteEntities { 公共IQueryable查询() { return Set().AsNoTracking();//从上下文分离结果 } 公共可查询负载() { 返回集(); } 公共void创建(TEntity实体) { if(条目(entity.State==EntityState.Detached) Set().Add(实体); } 等 } 现在,您只需向依赖项中注入一个接口,而不管它需要处理多少不同的实体:

// NOTE: In reality I would never inject IWriteEntities into an MVC Controller.
// Instead I would inject my CQRS business layer, which consumes IWriteEntities.
// See @MikeSW's answer for more info as to why you shouldn't consume a
// generic repository like this directly by your web application layer.
// See http://www.cuttingedge.it/blogs/steven/pivot/entry.php?id=91 and
// http://www.cuttingedge.it/blogs/steven/pivot/entry.php?id=92 for more info
// on what a CQRS business layer that consumes IWriteEntities / IReadEntities
// (and is consumed by an MVC Controller) might look like.
public class RecipeController : Controller
{
    private readonly IWriteEntities _entities;

    //Using Dependency Injection 
    public RecipeController(IWriteEntities entities)
    {
        _entities = entities;
    }

    [HttpPost]
    public ActionResult Create(CreateEditRecipeViewModel model)
    {
        Mapper.CreateMap<CreateEditRecipeViewModel, Recipe>()
            .ForMember(r => r.IngredientAmounts, opt => opt.Ignore());

        Recipe recipe = Mapper.Map<CreateEditRecipeViewModel, Recipe>(model);
        _entities.Create(recipe);
        foreach(Tag t in model.Tags) {
            _entities.Create(tag);
        }
        _entities.SaveChanges();
        return RedirectToAction("CreateRecipeSuccess");
    }
}
//注意:实际上,我不会将IWriteEntities注入MVC控制器。
//相反,我将注入我的CQRS业务层,它使用IWriteEntities。
//请参阅@MikeSW的答案,以了解有关您不应消费手机的更多信息
//像这样的通用存储库直接由web应用程序层提供。
//看http://www.cuttingedge.it/blogs/steven/pivot/entry.php?id=91 和
// http://www.cuttingedge.it/blogs/steven/pivot/entry.php?id=92 更多信息
//在使用IWriteEntities/IReadEntities的CQRS业务层上
//(由MVC控制器使用)可能看起来像。
公共类RecipeController:控制器
{
私有只读IWriteEntities\u实体;
//使用依赖注入
公共RecipeController(IWriteEntities)
{
_实体=实体;
}
[HttpPost]
公共操作结果创建(CreateEditRecipeViewModel模型)
{
Mapper.CreateMap()
.ForMember(r=>r.IngredientAmounts,opt=>opt.Ignore());
配方=映射器.Map(模型);
_实体。创建(配方);
foreach(model.Tags中的标记t){
_实体。创建(标记);
}
_entities.SaveChanges();
返回RedirectToAction(“CreateRecipe
// NOTE: In reality I would never inject IWriteEntities into an MVC Controller.
// Instead I would inject my CQRS business layer, which consumes IWriteEntities.
// See @MikeSW's answer for more info as to why you shouldn't consume a
// generic repository like this directly by your web application layer.
// See http://www.cuttingedge.it/blogs/steven/pivot/entry.php?id=91 and
// http://www.cuttingedge.it/blogs/steven/pivot/entry.php?id=92 for more info
// on what a CQRS business layer that consumes IWriteEntities / IReadEntities
// (and is consumed by an MVC Controller) might look like.
public class RecipeController : Controller
{
    private readonly IWriteEntities _entities;

    //Using Dependency Injection 
    public RecipeController(IWriteEntities entities)
    {
        _entities = entities;
    }

    [HttpPost]
    public ActionResult Create(CreateEditRecipeViewModel model)
    {
        Mapper.CreateMap<CreateEditRecipeViewModel, Recipe>()
            .ForMember(r => r.IngredientAmounts, opt => opt.Ignore());

        Recipe recipe = Mapper.Map<CreateEditRecipeViewModel, Recipe>(model);
        _entities.Create(recipe);
        foreach(Tag t in model.Tags) {
            _entities.Create(tag);
        }
        _entities.SaveChanges();
        return RedirectToAction("CreateRecipeSuccess");
    }
}
public interface IStore<TDomainObject> //where TDomainObject != Ef (ORM) entity
{
   void Save(TDomainObject entity);
   TDomainObject Get(Guid id);
   void Delete(Guid id);
 }
public class UsersRepository:IStore<User>
 {
   public UsersRepository(DbContext db) {}


    public void Save(User entity)
    {
       //map entity to one or more ORM entities
       //use EF to save it
    }
           //.. other methods implementation ...

 }
 public interface IQueryUsers
 {
       PagedResult<UserData> GetAll(int skip, int take);
       //or
       PagedResult<UserData> Get(CriteriaObject criteria,int skip, int take); 
 }
void Add(Customer c)
{
   _context.Customers.Add(c);
}
kernel.Bind<ITeststepService>().To<TeststepService>().InRequestScope().WithConstructorArgument("context", c => new ITMSContext());