下面代码中的 PublicAuditResultPredicate
Expression
public static IQueryable<Ing> ByPublicAuditResult(this IQueryable<Ing> ings)
{
return ings.Where(PublicAuditResultPredicate);
}
public static Expression<Func<IngInfo, bool>> PublicAuditResultPredicate
{
get
{
return i => i.AuditResult == null;
}
}
如何在下面的代码中重用?
public static IQueryable<Comment> ByPublicAuditResult(this IQueryable<Comment> comments)
{
return comments.Where(
c => c.Ing);
}
参考 github 上 efcore 仓库中的 issue:3.0 regression: Translate Expression.Invoke,通过 Expression.Lambda
解决了。
public static IQueryable<Comment> ByPublicAuditResult(this IQueryable<Comment> comments)
{
return comments.Where(PublicAuditResultPredicate);
}
private static Expression<Func<Comment, bool>> PublicAuditResultPredicate
{
get
{
Expression<Func<IngInfo, bool>> ingPredicate = IngSpecification.PublicAuditResultPredicate;
Expression<Func<Comment, IngInfo>> selectIng = c => c.Ing;
return Expression.Lambda<Func<Comment, bool>>(
Expression.Invoke(ingPredicate, selectIng.Body), selectIng.Parameters);
}
}
重用PublicAuditResultPredicate?
是的
chatgpt回复:
由于传递给Where
方法的参数是一个Expression<Func<Comment, bool>>
类型的lambda表达式,所以您需要根据PublicAuditResultPredicate
创建一个等效的表达式。
您可以使用Invoke
方法将PublicAuditResultPredicate
的参数替换为Comment
类型的参数,并在Where
表达式中使用它。修改后的代码如下:
public static IQueryable<Comment> ByPublicAuditResult(this IQueryable<Comment> comments)
{
var predicate = PublicAuditResultPredicate.Expand<Comment, IngInfo>();
return comments.Where(c => predicate.Invoke(c.Ing));
}
在这个代码中,Expand
方法将PublicAuditResultPredicate
转换为lambda表达式,根据其参数的类型指定了泛型类型参数。然后,predicate.Invoke(c.Ing)
将c.Ing
作为参数传递给PublicAuditResultPredicate
,并返回等效的表达式。最后,Where
方法将此表达式应用于comments
序列中的所有元素。
希望这可以帮到您!
求 Expand
方法的实现
@dudu: 以下为chatgpt回复:
下面代码中的 PublicAuditResultPredicate Expression
public static IQueryable<Ing> ByPublicAuditResult(this IQueryable<Ing> ings)
{
return ings.Where(PublicAuditResultPredicate);
}
public static Expression<Func<IngInfo, bool>> PublicAuditResultPredicate
{
get
{
return i => i.AuditResult == null;
}
}
如何在下面的代码中重用?
public static IQueryable<Comment> ByPublicAuditResult(this IQueryable<Comment> comments)
{
return comments.Where(
c => c.Ing);
}
你的答复是:
由于传递给Where
方法的参数是一个Expression<Func<Comment, bool>>
类型的lambda表达式,所以您需要根据PublicAuditResultPredicate
创建一个等效的表达式。
您可以使用Invoke
方法将PublicAuditResultPredicate
的参数替换为Comment
类型的参数,并在Where
表达式中使用它。修改后的代码如下:
public static IQueryable<Comment> ByPublicAuditResult(this IQueryable<Comment> comments)
{
var predicate = PublicAuditResultPredicate.Expand<Comment, IngInfo>();
return comments.Where(c => predicate.Invoke(c.Ing));
}
在这个代码中,Expand
方法将PublicAuditResultPredicate
转换为lambda表达式,根据其参数的类型指定了泛型类型参数。然后,predicate.Invoke(c.Ing)
将c.Ing
作为参数传递给PublicAuditResultPredicate
,并返回等效的表达式。最后,Where
方法将此表达式应用于comments
序列中的所有元素。
那么,回答中的Expand方法要怎么写
回复为:Expand
方法应该使用System.Linq.Expressions.ExpressionVisitor
类来遍历和修改表达式树,将其中的参数类型IngInfo
替换为Comment
类型。下面是一个可能的实现:
public static Expression<Func<TTo, bool>> Expand<TFrom, TTo>(this Expression<Func<TFrom, bool>> predicate)
{
var visitor = new ParameterTypeVisitor<TFrom, TTo>();
var body = visitor.Visit(predicate.Body);
return Expression.Lambda<Func<TTo, bool>>(body, visitor.NewParameter);
}
public class ParameterTypeVisitor<TFrom, TTo> : ExpressionVisitor
{
public ParameterExpression NewParameter { get; private set; }
protected override Expression VisitParameter(ParameterExpression node)
{
if (node.Type == typeof(TFrom))
{
NewParameter = Expression.Parameter(typeof(TTo), node.Name);
return NewParameter;
}
return base.VisitParameter(node);
}
}
在这个实现中,Expand
方法将predicate.Body
传递给ParameterTypeVisitor
类的实例进行修改。ParameterTypeVisitor
类在重写VisitParameter
方法时,检查参数类型是否为TFrom
类型。如果是,它将创建一个新的参数表达式,类型为TTo
,并将其存储在NewParameter
属性中。在表达式树中遍历时,它会将原始的参数表达式替换为新的参数表达式。最终,Expand
方法返回一个等效的lambda表达式,其中的参数类型为TTo
。
有意思的是,由于之前问的记录可能过期了,我重新问了遍,发现答复是不一样的,第二个答复如下:
可以通过创建一个通用方法来重用 PublicAuditResultPredicate
表达式,如下所示:
public static class PredicateExtensions
{
public static IQueryable<T> ByPublicAuditResult<T>(this IQueryable<T> source, Expression<Func<T, IngInfo>> ingSelector)
{
// Use the ingSelector to access AuditResult property of IngInfo
var predicate = PublicAuditResultPredicate.Compose(ingSelector);
return source.Where(predicate);
}
}
在此方法中,我们扩展了 IQueryable<T>
并将 PublicAuditResultPredicate
表达式作为一个参数组合到一个新的表达式中,以便重用。该方法使用 Compose()
扩展方法将查询可组合成新的表达式,从而使其能够访问 IngInfo
类型的 AuditResult
属性。
然后,可以像下面这样调用该方法:
var query = comments.ByPublicAuditResult(c => c.Ing.IngInfo);
在这个调用中,我们使用了 Ing.IngInfo
属性作为参数,以访问与 PublicAuditResultPredicate
表达式中使用的 IngInfo
属性相同的属性。
然后我又问了Compose()的实现,如下:
下面是一个实现了 Compose()
方法的示例扩展方法:
public static class PredicateExtensions
{
public static Expression<Func<TInput, bool>> Compose<TInput, TMiddle, TOutput>(
this Expression<Func<TInput, TMiddle>> innerSelector,
Expression<Func<TMiddle, TOutput>> outerSelector)
{
var parameter = Expression.Parameter(typeof(TInput));
var inner = innerSelector.Body.ReplaceParameter(innerSelector.Parameters[0], parameter);
var outer = outerSelector.Body.ReplaceParameter(outerSelector.Parameters[0], inner);
var lambda = Expression.Lambda<Func<TInput, bool>>(outer, parameter);
return lambda;
}
private static Expression ReplaceParameter(this Expression expression,
ParameterExpression source,
ParameterExpression target)
{
return new ParameterReplacer { Source = source, Target = target }.Visit(expression);
}
private class ParameterReplacer : ExpressionVisitor
{
public ParameterExpression Source { get; set; }
public ParameterExpression Target { get; set; }
protected override Expression VisitParameter(ParameterExpression node)
{
return node == Source ? Target : base.VisitParameter(node);
}
}
}
Compose()
方法接受两个表达式参数:一个从输入类型到中间类型的,和一个从中间类型到输出类型的。在这个示例中,TInput
等于 Comment
类型,TMiddle
等于 IngInfo
类型,TOutput
等于 bool
类型。
方法首先创建一个新的参数,作为新的表达式树的顶级参数。然后,它使用 innerSelector
和 outerSelector
的 Body
属性访问表达式树,并使用 ReplaceParameter()
方法将引用 innerSelector
参数的所有节点替换为新创建的顶级参数引用。然后它使用 Expression.Lambda()
方法创建新的 LambdaExpression
并返回。
ReplaceParameter()
方法实现了表达式树中的节点替换。它使用 ParameterReplacer
访问者类将 Source
参数引用替换为 Target
参数引用。在此过程中,我们使用递归遍历 Expression
树的节点,并使用 VisitParameter()
方法来替换原始参数。
请注意,这只是一种用于组合 Expression
树的方法,且在不同的情况下可能需要进行调整。例如,您可能需要将该方法调整为支持更多或不同类型的参数。
以上,
希望可以帮到你。
要在下面的代码中重用 PublicAuditResultPredicate 表达式,你可以使用 LINQ 的 Compose 方法来组合表达式。首先,你需要将 PublicAuditResultPredicate 转换为一个可调用的表达式,然后将其组合到新的表达式中。
以下是一个示例代码,演示如何重用 PublicAuditResultPredicate 表达式:
csharp
Copy code
public static IQueryable<Comment> ByPublicAuditResult(this IQueryable<Comment> comments)
{
var predicate = PublicAuditResultPredicate.Compile(); // 将 PublicAuditResultPredicate 转换为可调用的表达式
Expression<Func<Comment, bool>> combinedPredicate = c => predicate(c.Ing); // 组合表达式
return comments.Where(combinedPredicate);
}
在上面的示例中,首先使用 PublicAuditResultPredicate.Compile() 方法将 PublicAuditResultPredicate 转换为可调用的表达式。然后,使用 combinedPredicate 来组合新的表达式,其中使用了 predicate(c.Ing) 来调用之前转换的表达式,并将其作为新表达式的一部分。
这样,你就可以在 ByPublicAuditResult 方法中重用 PublicAuditResultPredicate 表达式,并将其应用于 comments 查询。
运行时报错:
Where(c => __Compile_2.Invoke(c.Outer.Inner))' could not be translated
相关链接:Reusable expression
– dudu 1年前