gpt4 book ai didi

c# - 使用自定义表达式扩展 EF Core 'where' 子句

转载 作者:行者123 更新时间:2023-11-30 17:28:00 26 4
gpt4 key购买 nike

我有一堆实体,它们的事件周期定义为“StartDate”和“EndDate”字段。大多数时候我需要查询他们根据一些自定义值检查他们的活跃期。代码大致如下所示:

public static Expression<Func<T, bool>> IsPeriodActive<T>(DateTime checkPeriodStart, DateTime checkPeriodEnd, Func<T, DateTime> entityPeriodStart, Func<T, DateTime> entityPeriodEnd) =>
entity =>
(checkPeriodEnd >= entityPeriodStart(entity) && checkPeriodEnd <= entityPeriodEnd(entity))
|| (checkPeriodStart >= entityPeriodStart(entity) && checkPeriodEnd <= entityPeriodEnd(entity))
|| (entityPeriodStart(entity) >= checkPeriodStart && entityPeriodStart(entity) <= checkPeriodEnd)
|| (entityPeriodEnd(entity) >= checkPeriodStart && entityPeriodEnd(entity) <= checkPeriodEnd)
|| (entityPeriodStart(entity) >= checkPeriodStart && entityPeriodStart(entity) <= checkPeriodEnd);

问题是 Func.Invoke() 无法转换为 SQL,这是显而易见的。我如何扩展 EF Core 来为任何实体类型添加这种“where”条件?我不能使用过滤器,因为有时我需要查询原始数据或只需要一次周期检查(而不是两者),而且一些实体对这些字段的命名不同。

最佳答案

您需要更改 Func<T, DateTime> Expression<Func<T, DateTime>> 的参数并将它们合并到所需的表达式中。

不幸的是,C# 编译器和 BCL 都无法帮助完成后面的任务(来自其他表达式的表达式组合)。有一些第三方包,如 LinqKit , NeinLinq等解决了这个问题,所以如果您打算大量使用表达式组合,您可以考虑使用这些库之一。

但原理是一样的。在某个时候自定义 ExpressionVisitor用于用另一个表达式替换原始表达式的一部分。例如,我在这种简单场景中使用的是创建编译时 lambda 表达式,并将附加参数用作占位符,然后用与 string.Replace 几乎相同的方式替换为实际表达式。 .

为此,我使用以下辅助方法将 lambda 表达式参数替换为另一个表达式:

public static partial class ExpressionUtils
{
public static Expression ReplaceParameter(this Expression expression, ParameterExpression source, Expression target)
{
return new ParameterReplacer { Source = source, Target = target }.Visit(expression);
}

class ParameterReplacer : ExpressionVisitor
{
public ParameterExpression Source;
public Expression Target;
protected override Expression VisitParameter(ParameterExpression node)
=> node == Source ? Target : base.VisitParameter(node);
}
}

有问题的方法可能是这样的:

public static Expression<Func<T, bool>> IsPeriodActive<T>(
DateTime checkPeriodStart,
DateTime checkPeriodEnd,
Expression<Func<T, DateTime>> entityPeriodStart,
Expression<Func<T, DateTime>> entityPeriodEnd)
{
var entityParam = Expression.Parameter(typeof(T), "entity");
var periodStartValue = entityPeriodStart.Body
.ReplaceParameter(entityPeriodStart.Parameters[0], entityParam);
var periodEndValue = entityPeriodEnd.Body
.ReplaceParameter(entityPeriodEnd.Parameters[0], entityParam);

Expression<Func<DateTime, DateTime, bool>> baseExpr = (periodStart, periodEnd) =>
(checkPeriodEnd >= periodStart && checkPeriodEnd <= periodEnd)
|| (checkPeriodStart >= periodStart && checkPeriodEnd <= periodEnd)
|| (periodStart >= checkPeriodStart && periodStart <= checkPeriodEnd)
|| (periodEnd >= checkPeriodStart && periodEnd <= checkPeriodEnd)
|| (periodStart >= checkPeriodStart && periodStart <= checkPeriodEnd);

var periodStartParam = baseExpr.Parameters[0];
var periodEndParam = baseExpr.Parameters[1];

var expr = baseExpr.Body
.ReplaceParameter(periodStartParam, periodStartValue)
.ReplaceParameter(periodEndParam, periodEndValue);

return Expression.Lambda<Func<T, bool>>(expr, entityParam);
}

请注意,您需要重新绑定(bind)(使用相同的 ReplaceParameter 辅助方法)传递的 Expression<Func<T, DateTime>> 的正文表达式到要在结果表达式中使用的公共(public)参数。

可以通过添加更多辅助方法来简化代码,例如此处 Entity Framework + DayOfWeek ,但同样,如果您打算大量使用它,更好的选择是使用一些现成的库,因为最后您将开始重新发明这些库的功能。

关于c# - 使用自定义表达式扩展 EF Core 'where' 子句,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53835948/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com