在存储库模式中加载子记录

使用LINQ TO SQL作为基于存储库的解决方案的基础。 我的实现如下:

IRepository

FindAll FindByID Insert Update Delete 

然后我有扩展方法,用于查询结果:

 WhereSomethingEqualsTrue() ... 

我的问题如下:

我的用户存储库有N个角色。 我是否创建了角色存储库来管理角色? 我担心如果我走这条路,我最终会创建几十个存储库(每个表1个,几乎除了连接表)。 每个表的存储库是否常见?

如果要构建特定于一个实体(表)的存储库,以便每个实体都具有上面列出的IRepository接口中的方法列表,那么您实际执行的是Active Record模式的实现。

绝对不应该为每个表都有一个Repository。 您需要识别域模型中的聚合以及要对其执行的操作。 用户和角色通常紧密相关,通常您的应用程序将与它们一起执行操作 – 这需要一个单独的存储库,以用户及其密切相关的实体为中心。

我猜你在post中看到过这个例子 。 此示例的问题是所有存储库在基础级别共享相同的CRUDfunction,但他不会超出此范围并实现任何域function。 该示例中的所有存储库看起来都是相同的 – 但实际上,真正的存储库并不都看起来相同(尽管它们仍然应该是接口的),每个存储库都会有特定的域操作。

您的存储库域操作应该更像:

 userRepository.FindRolesByUserId(int userID) userRepository.AddUserToRole(int userID) userRepository.FindAllUsers() userRepository.FindAllRoles() userRepository.GetUserSettings(int userID) 

等等…

这些是您的应用程序想要对基础数据执行的特定操作,而存储库应提供该操作。 可以把它想象成Repository表示您将在域上执行的primefaces操作集。 如果您选择通过通用存储库共享某些function,并使用扩展方法扩展特定存储库,那么这种方法可能适用于您的应用程序。

一个好的经验法则是,您的应用程序很少需要实例化多个存储库来完成操作。 这种需求确实存在,但是如果你的应用程序中的每个事件处理程序都只是为了获取用户的输入而正确地实例化输入所代表的实体,那么你可能会遇到设计问题。

每个表的存储库是否常见?

不,但你仍然可以有几个repositiories。 您应该围绕聚合构建存储库。

此外,您可能能够从所有存储库中抽象出一些function……而且,由于您使用的是Linq-to-Sql,因此您可能…

您可以实现基本存储库,它以通用方式实现所有这些常用function。

以下示例仅用于certificate这一点。 它可能需要很多改进……

  interface IRepository : IDisposable where T : class { IEnumerable FindAll(Func predicate); T FindByID(Func predicate); void Insert(T e); void Update(T e); void Delete(T e); } class MyRepository : IRepository where T : class { public DataContext Context { get; set; } public MyRepository(DataContext context) { Context = Context; } public IEnumerable FindAll(Func predicate) { return Context.GetTable().Where(predicate); } public T FindByID(Func predicate) { return Context.GetTable().SingleOrDefault(predicate); } public void Insert(T e) { Context.GetTable().InsertOnSubmit(e); } public void Update(T e) { throw new NotImplementedException(); } public void Delete(T e) { Context.GetTable().DeleteOnSubmit(e); } public void Dispose() { Context.Dispose(); } } 

对我来说,存储库模式是关于为数据访问方法设置一个薄的包装器。 在你的情况下LINQ to SQL,但NHibernate,在其他人手工推出。 我发现自己正在做的是为每个表创建一个非常简单的存储库(就像bruno列表,你已经拥有)。 它负责查找和执行CRUD操作。

但是,正如约翰内斯所提到的,我的服务水平更多地涉及聚合根。 我会有一个UserService,其方法类似于GetExistingUser(int id)。 这将在内部调用UserRepository.GetById()方法来检索用户。 如果您的业务流程要求GetExistingUser()返回的用户类几乎总是需要填充User.IsInRoles()属性,那么只需让UserService依赖于UserRepository RoleRepository。 在伪代码中,它看起来像这样:

 public class UserService { public UserService(IUserRepository userRep, IRoleRepository roleRep) {...} public User GetById(int id) { User user = _userService.GetById(id); user.Roles = _roleService.FindByUser(id); return user; } 

userRep和roleRep将使用您的LINQ to SQL位构造,如下所示:

 public class UserRep : IUserRepository { public UserRep(string connectionStringName) { // user the conn when building your datacontext } public User GetById(int id) { var context = new DataContext(_conString); // obviously typing this freeform but you get the idea... var user = // linq stuff return user; } public IQueryable FindAll() { var context = // ... same pattern, delayed execution } } 

我个人会在内部确定存储库类的范围,并将UserService和其他XXXXXService类公开,因此请保持服务API的使用者诚实。 因此,我再次看到存储库与与数据存储区通信的行为更紧密地联系在一起,但您的服务层与业务流程的需求更紧密地对齐。

我经常发现自己真的过分思考Linq对象和所有这些东西的灵活性,并使用IQuerable 而不仅仅是构建吐出我实际需要的服务方法。 用户LINQ在适当的地方,但不要试图使存储库做所有事情。

 public IList ActiveUsersInRole(Role role) { var users = _userRep.FindAll(); // IQueryable() - delayed execution; var activeUsersInRole = from users u where u.IsActive = true && u.Role.Contains(role); // I can't remember any linq and i'm type pseudocode, but // again the point is that the service is presenting a simple // interface and delegating responsibility to // the repository with it's simple methods. return activeUsersInRole; } 

所以,这有点漫无边际。 不确定我是否真的帮助了,但我的建议是避免使用扩展方法过于花哨,只需添加另一层以保持每个移动部件非常简单。 适合我。

如果我们像Womp建议的那样详细编写我们的存储库层,那么我们将在服务层中添加什么。 我们是否必须重复相同的方法调用,这些调用主要包括对相应存储库方法的调用,以便在我们的控制器或代码隐藏中使用? 这假设您有一个服务层,您可以在其中编写validation,缓存,工作流,身份validation/授权代码,对吗? 还是我离开基地?