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