12

使用LINQ TO SQL作为基于存储库的解决方案的基础。我的实现如下:在存储库模式中加载子记录

IRepository

FindAll 
FindByID 
Insert 
Update 
Delete 

然后我有一个用于查询结果作为这样的扩展方法:

WhereSomethingEqualsTrue() ... 

我的问题如下:

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

回答

31

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

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

我从你的帖子中猜测你已经seen this example。这个例子的问题是,所有的存储库在基本级别共享相同的CRUD功能,但他并没有超越这个范围并实现任何域功能。在这个例子中所有的仓库看起来都是一样的 - 但实际上,真正的仓库看起来并不相同(尽管它们仍然应该被连接),但是每个仓库都会有特定的域操作。

你的仓库域操作应该更像:

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

等等

这些是您的应用程序需要对基础数据进行特定的操作,和存储库应该提供。将其视为仓库代表您将在域上执行的一组原子操作。如果您选择通过通用存储库共享某些功能,并使用扩展方法扩展特定存储库,那么这对您的应用程序来说可能工作得很好。

一个很好的经验法则是它应该是罕见为您的应用程序需要实例化多个存储库以完成操作。确实会出现这种需求,但是如果应用中的每个事件处理程序都在玩弄六个存储库,以便接受用户的输入并正确实例化输入所代表的实体,那么您可能有设计问题。

4

每个表格的存储库是否共同?

不,但您仍然可以拥有多个存储空间。你应该围绕一个聚合构建一个仓库。

而且,你也许可以从所有仓库抽象一些功能...而且,因为你是使用LINQ到SQL,你也许可以......

您可以实现一个基础库,其以通用的方式实现所有这些通用功能。

以下示例仅用于证明这一点。它可能需要大量的改进......

interface IRepository<T> : IDisposable where T : class 
    { 
     IEnumerable<T> FindAll(Func<T, bool> predicate); 
     T FindByID(Func<T, bool> predicate); 
     void Insert(T e); 
     void Update(T e); 
     void Delete(T e); 
    } 

    class MyRepository<T> : IRepository<T> where T : class 
    { 
     public DataContext Context { get; set; } 

     public MyRepository(DataContext context) 
     { 
      Context = Context; 
     } 

     public IEnumerable<T> FindAll(Func<T,bool> predicate) 
     { 
      return Context.GetTable<T>().Where(predicate); 
     } 

     public T FindByID(Func<T,bool> predicate) 
     { 
      return Context.GetTable<T>().SingleOrDefault(predicate); 
     } 

     public void Insert(T e) 
     { 
      Context.GetTable<T>().InsertOnSubmit(e); 
     } 

     public void Update(T e) 
     { 
      throw new NotImplementedException(); 
     } 

     public void Delete(T e) 
     { 
      Context.GetTable<T>().DeleteOnSubmit(e); 
     } 

     public void Dispose() 
     { 
      Context.Dispose(); 
     } 
    } 
+2

不,每个存储库应该封装一个聚合根的持久性逻辑 – 2009-08-03 20:47:11

+0

@Johannes是的,但是如何? – 2009-08-03 23:27:28

1

对我来说,仓库格局即将把一个简单包装的数据访问方法。 LINQ to SQL在你的情况下,但NHibernate,在其他人手里滚动。我发现自己在做的是创建一个每个表的存储库,因为这非常简单(就像bruno列表和你已经拥有的那样)。这是负责寻找事情和做CRUD操作。

但是,我有一个服务级别,可以处理更多的聚合根,就像Johannes提到的那样。我会有一个像GetExistingUser(int id)方法的UserService。这将在内部调用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到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<User> FindAll() 
    { 
     var context = // ... same pattern, delayed execution 
    } 
} 

个人而言,我会做仓库类在内部作用域并且公开UserService和其他XXXXXService类,以便让服务API的使用者保持诚实。我再次看到存储库与与数据存储库交谈的行为更紧密地联系在一起,但您的服务层更紧密地与业务流程的需求保持一致。

我经常发现自己得太多真中的LINQ到对象的灵活性和所有的东西和使用的IQuerable 而不是吐出什么,我真正需要的只是建立服务方法。用户LINQ在适当的情况下,但不要试图让仓库做所有事情。

public IList<User> ActiveUsersInRole(Role role) 
{ 
    var users = _userRep.FindAll(); // IQueryable<User>() - 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; 
} 

所以,这有点漫不经心。不确定我是否真的帮助过任何人,但我的建议是避免使用扩展方法太过花哨,只需添加另一层,以保证每个移动部件都非常简单。适用于我。

1

如果我们编写我们的存储库层详细的Womp建议,我们在我们的服务层放什么。我们是否必须重复相同的方法调用,这些方法调用主要由调用相应的存储库方法组成,以用于我们的控制器或代码隐藏?这假定你有一个服务层,在那里你写你的验证,缓存,工作流,认证/授权码,对吗?或者我离开基地?