Linq 用表达式查询抽象,哪种方式更好?

Linq Queries abstraction with expressions, which way is better?

我们一直在寻找一种很好的方法来查询我们的数据库,同时关注开发的便利性、可读性和业务规则的合规性。

我在 Internet 上发现了两篇有趣的文章,它们描述了类似但略有不同的方法,一篇是使用查询对象和表达式,另一篇主要是使用具有业务相关意图和最终表达式的扩展方法。

  1. Query Object approach
  2. Extension Method approach

以下是我们可以(在存储库中)针对这两种情况的一些示例代码:

public IEnumerable<ControlOverview> GetRoutineControlOverviewsForTest(string testId, DateTime dateOfInterest)
    {
        var controlOverviewQuery = new ControlOverviewQuery()
                                           .ForTest(testId)
                                           .ForLotUsage(ControlLotUsageStatus.Routine)
                                           .ForControlStatusAt(dateOfInterest)
                                           .ForControlConfigurationAt(dateOfInterest)
                                           .ForControlLotConfigurableAt(dateOfInterest);

        return this.Context.ControlOverviews.Where(controlOverviewQuery.AsExpression());;
    }

第二种情况:

public IEnumerable<ControlOverview> GetRoutineControlOverviewsForTest(string testId, DateTime dateOfInterest)
    {
        return this.Context.ControlOverviews.ForTest(testId)
                                            .ForLotUsage(ControlLotUsageStatus.Routine)
                                            .ForControlStatusAt(dateOfInterest)
                                            .ForControlConfigurationAt(dateOfInterest)
                                            .ForControlLotConfigurableAt(dateOfInterest);
    }

如您所见,从开发人员的角度来看,它或多或少是相同的。可读性相同。唯一不同的是实现细节(扩展方法与查询对象)

在我的团队中,对于使用其中一种方法存在分歧,我找不到一套好的论据 (pros/cons) 来支持一种方法或另一种方法。

我个人更喜欢第一种方法(查询对象),因为我是 OOP 专家并且喜欢它使用的构建器模式,但是为了不使用扩展方法,我找不到比这更好的论据。 我非常感谢这里的一些见解。也许这只是一种编码偏好,很难或根本不可能通过强有力的论据说一个肯定比另一个更好

我确实更喜欢第一种方法,因为它没有隐藏存在 IQueryable<> 的事实(this.Context.ControlOverviews 不是 "hidden" 通过调用链其他方法),所以如果你想链接一个加法 .Where() 或一个额外的 .Select() 很明显你可以这样做。

在我看来,这些是重要的优点

查询方式

  • 由于这些方法实际上是 class 方法,它们可以访问内部,特别是可以限制访问。因此,您可以强制开发人员使用查询对象而不是自定义 LINQ。这确保了一致性和可维护性,但降低了灵活性
  • 您获得了使用多态性的灵活性。在您链接的文章中,这不是特色,但一旦您开始使用不同的查询 class 实现,它可能会变得很重要,例如统一访问具有不同模式的不同来源的数据

扩展方法

  • 您可以将其与接口一起使用。事实上,我最常使用扩展方法来实现接口的标准成员,我认为这是它们最有用的地方。但是,这只有在您有一些预定义的接口时才有意义,例如已经在平台中可用,例如 IQueryable。如果您使用的是自己的 classes,您可以完全控制它,这一点就很薄弱。
  • 稍微简洁一点(我知道,不是那么多,但我会说这很重要)