【问题标题】:Shared repositories in repository pattern存储库模式中的共享存储库
【发布时间】:2017-08-31 13:15:55
【问题描述】:

存储库依赖?假设我有一个如下所示的域:

public class User
{
    public int UserId { get; set; }
    public Lazy<Post> Posts { get; set; }
}

public class Post
{
    public int PostId { get; set; }
    public User Poster { get; set; }
}

有我的帖子的用户。

如何设置我的存储库,以便对于每个 user,它返回一个 Post 列表,对于每个 post,我得到 Poster

我有 2 个存储库:

public class UserRepository : IUserRepository
{
    public IQueryable<User> GetUsers();
}

public class PostRepository : IPostRepository
{
    public IQueryable<Post> GetPosts();
}

我尝试从 1 个存储库调用另一个存储库,但由于存在循环依赖关系,最终导致崩溃。

我在实体框架中使用存储库模式的 POCO 方法。

【问题讨论】:

  • 您的 ORM 使用什么? NHibernate,实体框架或其他东西。这将有助于我们为您提供更好的答案。

标签: c# repository-pattern


【解决方案1】:

我有 2 个存储库:

public class UserRepository : IUserRepository
{
    public IQueryable<User> GetUsers();
}

public class PostRepository : IPostRepository
{
    public IQueryable<Post> GetPosts();
}

这很可能会成为问题。

您可能不想简单地GetUsers。正如您所说,您希望子实体加载聚合或顶级实体。

会发生什么,取决于使用的上下文 - 您的直接目的是什么 - 您将需要对 User 进行更改。您最终可能会使用 GetUsers、'GetUsersWithPostsGetUsersForImportantReportGetUsersForAttendingAnIceCreamSocial 等方法,令人作呕。

缺少的是角色的概念。

您检索用户的角色是什么?在这里明确建模。

从聚合的基本接口开始。基本属性可以去这里

public interface IUser {
  public Guid UserId { get; set; }
  public string UserName { get; set; }
  public IEnumerable<Posts> { get; set; }
}

添加接口以支持您将使用用户的角色。

public interface IAddPostsToUser : IUser {
  public void AddPost(Post post);
}

您的存储库可以这样定义:

public interface IUserRepository {
  User Get<TRole>(Guid userId) where TRole : IUser;
}

现在,使用该角色定义获取策略;

public interface IFetchingStrategy<TRole> {
  TRole Fetch(Guid id, IRepository<TRole> role)
}

您的存储库将通过注入或服务位置获取获取策略并调用 fetch。您可以传入存储库以提供 FetchingStrategy 查询的机制,或者让 FetchingStrategy 注入或定位它需要查询的服务。

当然,具体的查询方式取决于您的 ORM。

但这种建模方式将有助于避免在不同场景下加载实体图时出现许多问题。

【讨论】:

  • Udi Dahan 有一个关于这些概念的幻灯片:cid-c8ad44874742a74d.skydrive.live.com/self.aspx/Blog/…
  • 你知道任何将这种模式与实体框架结合使用的示例项目吗?
  • 我没有。但我认为我可能会将上下文作为存储库implementations 中的内部属性公开,并且我会让 FetchingStrategy 实现将 Repository 接口转换为具体的 impl。并获取该上下文以执行查询。它实际上应该很简单地组合在一起。
  • @qstarin,我不知道把 FetchingStrategy 放在哪里,如何使用它并用这些新信息构建项目。
  • 该接口将与 IRepository 一起成为您的域的一部分。实现类将与您的存储库的具体实现一起使用。你现在就这样分开吗?一个程序集中的领域接口和另一个程序集中的实现?
【解决方案2】:

恕我直言,这属于服务层,而不是存储库层。存储库只是包装了数据库。

public IQueryable<UserData> GetUsersWithPosts()
{
    return from u in UserRepository.GetUsers()
           select new UserData 
           {
               Id = u.Id,
               Name = u.Name
               Posts = from p in u.Posts
                       select new PostData
                       {
                           Id = u.Id,
                           Title = p.Title
                       }
           };

...根据需要添加安全问题、聚合等。

【讨论】:

  • 这意味着每次我收到一个帖子时,由于 POCO,我必须重写从 ORMDomain 到我的域的转换。
  • AutoMapper 让这更容易一些,但我无论如何也不推荐这条路线
  • @Shawn,不,不是那个意思。我只是选择展示我认为正确的方法。无论您使用投影、获取策略、显式预加载还是任何其他方法,都不会改变将业务关注点放在服务中并保持存储库精简的基本方面。 @qstarin,AutoMapper 不理解 IQueryable&lt;T&gt; 并且不是一个合适的工具。
猜你喜欢
  • 1970-01-01
  • 2014-03-03
  • 2015-02-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-02-10
  • 1970-01-01
相关资源
最近更新 更多