Linq 用表达式查询抽象,哪种方式更好?
Linq Queries abstraction with expressions, which way is better?
我们一直在寻找一种很好的方法来查询我们的数据库,同时关注开发的便利性、可读性和业务规则的合规性。
我在 Internet 上发现了两篇有趣的文章,它们描述了类似但略有不同的方法,一篇是使用查询对象和表达式,另一篇主要是使用具有业务相关意图和最终表达式的扩展方法。
以下是我们可以(在存储库中)针对这两种情况的一些示例代码:
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,您可以完全控制它,这一点就很薄弱。
- 稍微简洁一点(我知道,不是那么多,但我会说这很重要)
我们一直在寻找一种很好的方法来查询我们的数据库,同时关注开发的便利性、可读性和业务规则的合规性。
我在 Internet 上发现了两篇有趣的文章,它们描述了类似但略有不同的方法,一篇是使用查询对象和表达式,另一篇主要是使用具有业务相关意图和最终表达式的扩展方法。
以下是我们可以(在存储库中)针对这两种情况的一些示例代码:
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,您可以完全控制它,这一点就很薄弱。 - 稍微简洁一点(我知道,不是那么多,但我会说这很重要)