0

我在 ASP.NET MVC 3 应用程序中使用 EF Code First 方法,而不是重新创建轮子,我想知道是否已经存在一个可靠的基础 Repository 类,我的自定义 Repository 类可以扩展以便提供开箱即用的默认功能(例如基本 CRUD 等)。

所以像这样的东西......

public class CustomerRepository : BaseRepository { ... }

...因此将提供一种默认方式来处理开箱即用的 Customer 对象。然后,我想将 ICustomerRepository 注入我的 MVC 控制器,并在那里为我提供可用的功能。

我确信这样的事情已经存在,因为我已经用 NHibernate 做了类似的事情。

谢谢

4

4 回答 4

5

不,除了 EF 本身(它本身就是工作单元模式的实现,而 DbSet 基本上是存储库)之外,没有内置存储库。

目前,软件社区中存在关于通用存储库是否具有很大实际价值的争论。许多人认为,出于测试目的,它们提供了简单的单元测试。其他人说单元测试存储库没有帮助,因为模拟存储库的行为方式与真实存储库不同(因为 linq -> Sql 转换层,它不存在于模拟存储库中)。

许多人建议您使用 SqlLite 之类的内存数据库对 EF 进行集成测试,而不是对其进行单元测试。

尽管如此,如果您打算使用存储库,网络上仍有许多示例,它们具有不同的样式和方法。或者你可以自己滚动。MS 不提供。

于 2012-07-11T16:02:44.330 回答
3

根据我的经验,编写自己的存储库是多余的,因为 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>());
}

单元测试怎么样?

您可以为每个服务层类构建特定的接口,并在需要的地方模拟它。

于 2012-07-11T19:21:02.580 回答
0

我的一个朋友,Sacha Barber 写了一篇很好的文章,涵盖了其中的一些想法。

链接可以在这里找到。

RESTful WCF / EF POCO / 工作单元 / 存储库 / MEF:1 of 2

于 2012-07-11T16:01:31.967 回答
0

EF 有一个名为DbContext. 您可以添加类型的属性DbSet<TEntity>

这使您可以执行以下操作:

public class User {
    public int Id { get; set; }
    public string Name { get; set; }
}

public class DatabaseContext : DbContext {
    public DbSet<User> Users { get; set; }
}

您现在可以像这样使用它:

using(var db = new DatabaseContext()) {
    User jon = new User {Name = "Jon Smith"};
    db.Users.Add(jon);
    db.SaveChanges();
    var jonById = db.Users.Single(x => x.Id == 1);
}

如果您想要更多抽象,请参阅这篇关于围绕 EF Entity Framework 4 CTP 4 / CTP 5 通用存储库模式和可单元测试构建通用存储库的帖子请注意,并不总是需要这种抽象级别。您应该确定您的缩写是否真正受益于添加通用存储库而不是直接使用 DbContext。

于 2012-07-11T16:06:42.270 回答