【问题标题】:Repository Base Or Specific Method?存储库基础或特定方法?
【发布时间】:2011-07-15 18:09:35
【问题描述】:

我一直在开发一个应用程序,我正在尝试应用 DDD 和其他工具(Nhibernate 和 asp.net MVC)。

在这个应用程序中,我们需要在存储库的 Person 实体中按名称实现“搜索”。所以,我们有一个存储库库(RepositoryBase 类),在这个实现中,我有一个适用于这个项目的通用方法。

public IEnumerable<T> FindAll(Expression<Func<T, bool>> condition) {
  return Session.QueryOver<T>().Where(condition);
}

在我的 asp.net mvc 应用程序中,我们可以像这样使用它:

var list = _repositoryPerson.FindAll(x => x.Name == "Felipe");

另一方面,我们可以为此任务创建一个特定的方法(在 RepositroyPerson 类中),例如:

public IEnumerable<Person> FindByName(string name) {
  return Session.QueryOver<Person>().Where(x => x.Name == name);
}

在我的 asp.net mvc 应用程序中,我们可以像这样使用它:

var list = _repositoryPerson.FindByName("Felipe");

我的问题是:

1 - 根据 DDD,满足此要求的推荐方法是什么?具体还是基类?

2 - 有人推荐了使用 Nhibernate QueryOver 的 Repository base (generic) 的一些不错的实现?

如果有人可以帮助我,我将不胜感激! 谢谢


更新:

例如,如果我需要一个复杂的搜索,例如组合条件...例如:姓名(可选)和年龄(可选)和城市(可选)和其他字段...每个字段都是可选的并且组合与其他领域!是否推荐使用表达式?您将如何实现此代码?

PS:对不起我的英语!

谢谢

【问题讨论】:

    标签: .net nhibernate design-patterns domain-driven-design repository


    【解决方案1】:

    为了两全其美,您可以像在基类中一般实现FindByName方法,将其标记为protectedprotected internal(取决于您是否要允许其他程序集定义存储库实现),然后从您的特定存储库对象中调用它。

    protected internal IEnumerable<T> FindAll(Expression<Func<T, bool>> condition) {
      return Session.QueryOver<T>().Where(condition);
    }
    
    public IEnumerable<Person> FindByName(string name) {
      return base.FindAll(x => x.Name == name);
    }
    

    这将允许您为将要使用该方法的情况编写特定的测试,同时还允许您更改 ORM 实现,而无需在很多地方进行更改。

    更新: 为了在 FindAll 中组合多个条件,您可以使用 Aggregate 简单地组合它们(我还没有用 NHibernate 测试过,但如果它坏了,你可以用 foreach 替换它)。

    public IEnumerable<T> FindAll(IEnumerable<Expression<Func<T, bool>>> conditions)
    {
        return conditions.Aggregate(Session.QueryOver<T>(), (current, condition) => current.Where(condition)).List();
    }
    

    然后方法可以采用可选参数,并创建一个传递给 FindAll 的条件列表。

       public IEnumerable<T> FindByParams(string name=null, string city=null)
       {
           var wheres = new List<Expression<Func<T, bool>>>();
    
           if (name != null)
           {
               wheres.Add(x => x.Name == name);
           }
           if (city != null)
           {
               wheres.Add(x => x.City == city);
           }
           return base.FindAll(wheres);
       }
    

    【讨论】:

    • 是的,Ryan,我也认为在这个解决方案中并且我做到了,但是看看这段代码(我的意思是......你选择做'受保护的内部'),我在想是否有任何问题保持“公开”,有什么问题吗?也许这听起来像是做某事的另一种选择,但我知道这不是推荐的。但是您的解决方案很好,谢谢:D
    • 我选择 protected 或 protected internal 的原因是因为这样可以确保存储库的客户端不会直接调用 FindAll 方法。如果您将其公开,并且客户端使用它,则需要您从客户端的单元测试中测试存储库的功能,这将违反 DDD。
    • 很好地解释了 Ryan,我明白了,这听起来像是在存储库中返回 IEnumerable insted IList 的原因,用户可以执行与所讨论的目标不同的其他任务,例如在 IList ,此存储库的用户可以调用“添加”方法,例如。谢谢:D
    • Ryan,您将如何在 ddd 中实现可选过滤器,就像我在第一篇文章的“更新”中所说的那样?谢谢大佬!
    【解决方案2】:

    这两种方法都有效,我将使用通用方法,但 findmyname 可以为您提供更好、更简洁的代码。 我认为这是一个品味问题,以及你想成为 DDD 的纯粹主义者。我将为更复杂的需求创建特定的方法,例如搜索或其他东西,但保持简单是我的方法

    【讨论】:

    • 谢谢奥斯卡,我也是这么想的!但是看看我的更新。在这种情况下你会怎么做?
    【解决方案3】:

    我同意 Oscar 的观点 - 我更喜欢具体的方法,主要原因是它们更易于测试。围绕 IQueryable 和 Expression 对象构建合理的测试非常具有挑战性。

    【讨论】:

    • 谢谢,我也同意!但是看看我的更新!在这种情况下你会怎么做?
    【解决方案4】:

    其他注意事项:

    谁是您的存储库代码的消费者?如果您将您的存储库暴露给 gui 或其他可能会挣扎或滥用更抽象接口的开发人员,那么就有额外的动力来提供更清晰的特定接口。

    界面有多臃肿?仅仅因为表达式 can 几乎是任何东西并不意味着它通常是。但在这种情况下,也许是因为您可以像在更新中一样组合搜索条件,然后这会倾向于使用更抽象的方法,至少作为帮助程序。

    我个人倾向于尽可能使用更具体的界面

    干杯,
    浆果

    【讨论】:

    • 我的 GUI 将使用它(asp.net mvc 应用程序)。你将如何实现一个带有可选参数的方法来制作过滤器? (就像我在更新中所说的那样)。感谢您的 awser Berryl
    • @Felipe。一般来说,我会使用扩展。这是the general idea
    猜你喜欢
    • 2019-08-02
    • 1970-01-01
    • 1970-01-01
    • 2016-03-12
    • 2020-07-15
    • 1970-01-01
    • 1970-01-01
    • 2011-11-10
    • 1970-01-01
    相关资源
    最近更新 更多