使用 Entity Framework 4.1 和 DDD 访问代码生成的 DbContext 上的存储过程
我正在开发一个使用 ASP.Net MVC 3、EF 4.1 和 Ninject 进行依赖注入的大型项目。我已经阅读了这里关于 DDD、EF 和存储库模式的许多现有问题,但我似乎找不到任何人将存储过程与这些模式合并。
我不喜欢在似乎已经是使用 DbContext 定义的 UnitOfWork/RepositoryPattern 之上实现另一个存储库模式的想法。另外,如果可能的话,我通常不喜欢为系统中的每种类型的实体创建服务和存储库类的想法。
我的问题的根源在于这个似乎每个人都使用的通用存储库接口。
public interface IRepository<TEntity> where TEntity : class
{
TEntity Get(Expression<Func<TEntity, bool>> whereClause);
IEnumerable<TEntity> List();
IEnumerable<TEntity> Find(Expression<Func<TEntity, bool>> whereClause);
void Add(TEntity entity);
void Delete(TEntity entity);
// And so on...
}
如果您的所有查询都可以在单个实体的上下文中,那就太好了。当我想要访问存储过程时,这对我来说是一个问题。使用 EF 4.1 和代码生成您可以添加存储过程(例如 SelectUser),它将生成一个如下所示的上下文。
namespace MyCompany.Data.Database
{
using System;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Data.Objects;
using MyCompany.Domain.Entities;
using MyCompany.Domain.Contracts;
public partial class MyCompanyEntities : DbContext
{
public MyCompanyEntities()
: base("name=MyCompanyEntities")
{
this.Configuration.LazyLoadingEnabled = false;
this.Configuration.ProxyCreationEnabled = false;
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();
}
public DbSet<Order> Orders { get; set; }
public DbSet<User> Users { get; set; }
public virtual ObjectResult<User> SelectUser(Nullable<int> userId)
{
((IObjectContextAdapter)this).ObjectContext.MetadataWorkspace.LoadFromAssembly(typeof(User).Assembly);
var userIdParameter = userId.HasValue ?
new ObjectParameter("UserId", userId) :
new ObjectParameter("UserId", typeof(int)); MyCompanyEntities x; x.
return ((IObjectContextAdapter)this).ObjectContext.ExecuteFunction<User>("SelectUser", userIdParameter);
}
public virtual ObjectResult<User> SelectUser(Nullable<int> userId, MergeOption mergeOption)
{
((IObjectContextAdapter)this).ObjectContext.MetadataWorkspace.LoadFromAssembly(typeof(User).Assembly);
var userIdParameter = userId.HasValue ?
new ObjectParameter("UserId", userId) :
new ObjectParameter("UserId", typeof(int));
return ((IObjectContextAdapter)this).ObjectContext.ExecuteFunction<User>("SelectUser", mergeOption, userIdParameter);
}
}
}
作为我的 DDD 设置的一部分,我有一个 UserService 类,我想将一个存储库“注入”到它的构造函数中。许多示例表明构造函数应该接受 (IRepository
我唯一能想到的就是创建另一个带有存储过程方法的接口。我真的不想将其添加到通用 IRepository 中,因为当您有 IRepository
也许我的处理方式是错误的。如果我不尝试创建一个全新的存储库模式,我是否应该不必费心在 DbContext 之上创建一个接口?我实际上是为了依赖注入而创建它的。如果 UserService 构造函数采用 MyCompanyEntities 实例而不是接口,会出现错误吗?
I'm working on a large project using ASP.Net MVC 3, EF 4.1 and Ninject for Dependecy Injection. I've read through many of the existing questions here regarding DDD, EF and the Repository Pattern but I can't seem to find anyone incorporating stored procedures with these patterns.
I don't like the idea of implementing yet another repository pattern on top of what seems to already be a UnitOfWork/RepositoryPattern already defined with a DbContext. Also, I generally don't like the idea of creating Service and Repository classes for every type of entity in the system if possible.
The source of my problem stems from this common repository interface which everyone seems to use.
public interface IRepository<TEntity> where TEntity : class
{
TEntity Get(Expression<Func<TEntity, bool>> whereClause);
IEnumerable<TEntity> List();
IEnumerable<TEntity> Find(Expression<Func<TEntity, bool>> whereClause);
void Add(TEntity entity);
void Delete(TEntity entity);
// And so on...
}
That's great if all your queries can be in context of a single entity. Where this breaks for me is when I want to access a stored procedure. With EF 4.1 & Code Generatrion you can add stored procedures (e.g. SelectUser) and it will generate a context which looks something like this.
namespace MyCompany.Data.Database
{
using System;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Data.Objects;
using MyCompany.Domain.Entities;
using MyCompany.Domain.Contracts;
public partial class MyCompanyEntities : DbContext
{
public MyCompanyEntities()
: base("name=MyCompanyEntities")
{
this.Configuration.LazyLoadingEnabled = false;
this.Configuration.ProxyCreationEnabled = false;
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();
}
public DbSet<Order> Orders { get; set; }
public DbSet<User> Users { get; set; }
public virtual ObjectResult<User> SelectUser(Nullable<int> userId)
{
((IObjectContextAdapter)this).ObjectContext.MetadataWorkspace.LoadFromAssembly(typeof(User).Assembly);
var userIdParameter = userId.HasValue ?
new ObjectParameter("UserId", userId) :
new ObjectParameter("UserId", typeof(int)); MyCompanyEntities x; x.
return ((IObjectContextAdapter)this).ObjectContext.ExecuteFunction<User>("SelectUser", userIdParameter);
}
public virtual ObjectResult<User> SelectUser(Nullable<int> userId, MergeOption mergeOption)
{
((IObjectContextAdapter)this).ObjectContext.MetadataWorkspace.LoadFromAssembly(typeof(User).Assembly);
var userIdParameter = userId.HasValue ?
new ObjectParameter("UserId", userId) :
new ObjectParameter("UserId", typeof(int));
return ((IObjectContextAdapter)this).ObjectContext.ExecuteFunction<User>("SelectUser", mergeOption, userIdParameter);
}
}
}
As part of my DDD setup I have a UserService class and I would like to 'inject' a repository to its constructor. Many examples suggest that the constructor should accept an (IRepository<User> userRepository). This doesn't work for me. Stored procedures are generated on the DbContext class as a method and I am unable to see it.
The only thing I can think of is to either create another interface with the stored procedure methods on it. I don't really want to add it to the generic IRepository because then when you have an instance of IRepository<Order> you'll still see SelectUser which seems a bit odd. Maybe it's not a big deal?
Perhaps I'm going about this the wrong way. Should I not be bothering with creating an interface on top of my DbContext if I'm not trying to create a whole new repository pattern? I was really creating it for the dependency injection. Would it be wrong if the UserService constructor took a MyCompanyEntities instance instead of an interface?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
你发现的东西是自然的。问题是通用存储库不足以满足实际场景。它只对“基础”实施有好处。您需要用户实体的特定存储库,它将公开对上下文公开的存储过程的方法包装调用。
What you found is natural. The problem is that generic repository is insufficient for real scenarios. It is only good for "base" implementation. You need specific repository for User entity which will expose method wrapping call to context exposed stored procedure.