根据我的经验,编写自己的存储库是多余的,因为 EF 已经通过 DbSet 实现了这种模式。
我在最近的一个项目中使用了 MVC3 + EF Code Fisrt。我们在一些教程之后开始实现一个通用存储库,很快我们意识到我们正在编写大量不必要和冗余的代码。实际上,存储库除了隐藏了很多 DbSet 的功能外,什么都没有给我们。最后,我们决定删除它们并直接使用我们的 DbContext 和 DbSet。
但是,除了简单的 CRUD 操作之外,复杂的业务逻辑又如何呢?
好吧,我们通过服务层公开了所有复杂的功能,例如查询和多个 CRUD 操作。您可以按功能构建不同的服务类。例如,您可以编写一个 AccountService 来管理与用户帐户相关的所有功能。像这样的东西:
public class AccountService {
private MyContext ctx;
public AccountService(DbContext dbContext) {
this.ctx = (MyContext)dbContext;
}
/// <summary>
/// Gets the underlying DbContext object.
/// </summary>
public DbContext DbContext {
get { return ctx; }
}
/// <summary>
/// Gets the users repository.
/// </summary>
public DbSet<User> Users {
get {return ctx.Users;}
}
public bool ValidateLogin(string username, string password) {
return ctx.Users.Any(u => u.Username == username && u.Password == password);
}
public string[] GetRolesForUser(string username) {
var qry = from u in ctx.Users
from r in u.Roles
where u.Username == username
select r.Code;
return qry.ToArray<String>();
}
public User CreateUser(string username, string password) {
if (String.IsNullOrWhiteSpace(username)) throw new ArgumentException("Invalid user name");
if (String.IsNullOrWhiteSpace(password)) throw new ArgumentException("Invalid password");
User u = new User {
Username = username.Trim().ToLower(),
Password = password.Trim().ToLower(),
Roles = new List<Role>()
};
ctx.Users.Add(u);
ctx.SaveChanges();
return u;
}
依赖注入呢?
使用这种方法,我们唯一需要注入的是 DbContext。服务类有一个采用 DbContext 的构造函数。因此,当您的控制器构造函数采用服务实例时,DbContext 将被注入其中。
编辑:示例代码
这是一个关于控制器外观的示例代码:
public class HomeController : Controller {
private readonly AccountService accountService;
public AccountController(AccountService accountService) {
this.accountService = accountService;
}
}
这可能是使用 NInject 的 DI 配置:
private static void RegisterServices(IKernel kernel) {
kernel.Bind<MyContext>().ToSelf().InRequestScope();
kernel.Bind<DbContext>().ToMethod(ctx => ctx.Kernel.Get<MyContext>());
}
单元测试怎么样?
您可以为每个服务层类构建特定的接口,并在需要的地方模拟它。