[英]Combining two Linq Expressions
使用各種SO帖子中的信息,尤其是這個博客 (已更正為使用AndAlso
而不是And
),我設法將類型相似的linq表達式組合為一個謂詞。 但是現在我想合並兩個表達式,其中一個是另一個的輸入。 這是完全擴展的原始Expression
;
private Expression<Func<T, bool>> ExpressionIsNamed(IEnumerable<EntityName> AccessorNames)
{
// works
Expression<Func<T, bool>> Texpr = x => x.Security.Readers.Any(n => AccessorNames.ToStringArray().Contains(n.Text));
return Texpr;
}
請注意,至關重要的是,我需要將這些作為表達式進行管理,因為我的數據庫驅動程序需要遍歷樹並將其轉換為本地調用,因此不能選擇使用Compile()進行合並。
所以下面是我想與上面的Any()
調用結合的函數。 最終的輸出Expression必須為Expression<Func<T, bool>>
,我需要將x.Security.Readers
傳遞給該x.Security.Readers
。
public static Expression<Func<IEnumerable<EntityName>,bool>> AccessCheckExpression(IEnumerable<EntityName> AccessorNames)
{
return accessList => accessList.Any(n => AccessorNames.ToStringArray().Contains(n.Text));
}
我已經盡力了,但是我正在努力找出如何從accessCheck
換出accessList =>
,並使其在單個Expression中使用accessList
。 到目前為止,我有這個;
private Expression<Func<T, bool>> ExpressionIsNamed(IEnumerable<EntityName> AccessorNames)
{
Expression<Func<T, IEnumerable<EntityName>>> accessList = (T x) => x.Security.Readers;
Expression<Func<IEnumerable<EntityName>, bool>> accessCheck = SecurityDescriptor.AccessCheckExpression(AccessorNames);
// Combine?
Expression<Func<T, bool>> Texpr = ??? accessCheck + accessList ???
return Texpr;
}
[更新]
所以我還有一點。
class ParameterUpdateVisitor : System.Linq.Expressions.ExpressionVisitor
{
private ParameterExpression _oldParameter;
private ParameterExpression _newParameter;
public ParameterUpdateVisitor(ParameterExpression oldParameter, ParameterExpression newParameter)
{
_oldParameter = oldParameter;
_newParameter = newParameter;
}
protected override Expression VisitParameter(ParameterExpression node)
{
if (object.ReferenceEquals(node, _oldParameter))
return _newParameter;
return base.VisitParameter(node);
}
}
static Expression<Func<T, bool>> UpdateParameter<T>(
Expression<Func<T, IEnumerable<EntityName>>> expr,
ParameterExpression newParameter)
{
var visitor = new ParameterUpdateVisitor(expr.Parameters[0], newParameter);
var body = visitor.Visit(expr.Body);
return Expression.Lambda<Func<T, bool>>(body, newParameter);
}
然后我可以編譯;
UpdateParameter(accessList, accessCheck.Parameters[0]);
感謝所有這些Invoke()
建議,但是我的猜測是,當他們到達MongoDB驅動程序時,它不會喜歡InvocationExpression
。 但是, Invoke
和上面的代碼現在都以完全相同的方式失敗。 即;
System.ArgumentException: Expression of type
'System.Func`2[MyLib.Project,System.Collections.Generic.IEnumerable`1[MyLib.EntityName]]'
cannot be used for parameter of type
'System.Collections.Generic.IEnumerable`1[MyLib.EntityName]'
因此,隱式參數x.Security.Readers
似乎與普通的舊IEnumerable<EntityName>
VisitorExpression
是您的朋友在這里。 這是合並類似內容的簡化但完整的示例:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
class Source {
public List<Value> Values {get;set;}
}
class Value {
public int FinalValue {get;set;}
}
static class Program {
static void Main() {
Expression<Func<Source, IEnumerable<Value>>> f1 =
source => source.Values;
Expression<Func<IEnumerable<Value>, bool>> f2 =
vals => vals.Any(v => v.FinalValue == 3);
// change the p0 from f2 => f1
var body = SwapVisitor.Swap(f2.Body, f2.Parameters[0], f1.Body);
var lambda = Expression.Lambda<Func<Source, bool>>(body,f1.Parameters);
// which is:
// source => source.Values.Any(v => (v.FinalValue == 3))
}
}
class SwapVisitor : ExpressionVisitor {
private readonly Expression from, to;
private SwapVisitor(Expression from, Expression to) {
this.from = from;
this.to = to;
}
public static Expression Swap(Expression body,
Expression from, Expression to)
{
return new SwapVisitor(from, to).Visit(body);
}
public override Expression Visit(Expression node)
{
return node == from ? to : base.Visit(node);
}
}
編輯:在您的情況下,這將是:
private Expression<Func<T, bool>> ExpressionIsNamed(
IEnumerable<EntityName> AccessorNames)
{
Expression<Func<T, IEnumerable<EntityName>>> accessList =
(T x) => x.Security.Readers;
Expression<Func<IEnumerable<EntityName>, bool>> accessCheck =
SecurityDescriptor.AccessCheckExpression(AccessorNames);
var body = SwapVisitor.Swap(accessCheck.Body,
accessCheck.Parameters[0], accessList.Body);
return Expression.Lambda<Func<T, bool>>(body, accessList.Parameters);
}
我不確定它是否會起作用,但是請嘗試以下操作:
private Expression<Func<T, bool>> ExpressionIsNamed(IEnumerable<EntityName> AccessorNames)
{
Expression<Func<T, IEnumerable<EntityName>>> accessList = (x) => x.Security.Readers;
Expression<Func<IEnumerable<EntityName>, bool>> accessCheck = AccessCheckExpression(AccessorNames);
var result = Expression.Lambda<Func<T, bool>>(
Expression.Invoke(accessCheck, accessList.Body), // make invokation of accessCheck, and provide body of accessList (x.Security.Readers) as parameter
accessList.Parameters.First() // parameter
);
return result;
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.