5

さまざまな SO 投稿、特にこのブログAndAlso(ではなく使用するように修正And) からの情報を使用して、同様に型指定された linq 式を 1 つの述語に組み合わせることができました。しかし今、一方が他方への入力である 2 つの式を結合したいと考えています。これが完全に展開されたオリジナル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;
    }

DB ドライバーはツリーをたどってネイティブ呼び出しに変換する必要があるため、Compile() を使用結合することはできません。

Any()以下は、上記の呼び出しと組み合わせたい関数です。最終出力 Expression は型Expression<Func<T, bool>>である必要があり、これに渡す必要がありx.Security.Readersます。

    public static Expression<Func<IEnumerable<EntityName>,bool>> AccessCheckExpression(IEnumerable<EntityName> AccessorNames)
    {
        return accessList => accessList.Any(n => AccessorNames.ToStringArray().Contains(n.Text));
    }

私はこれまでのところ持っていますが、スワップアウトaccessList =>して単一の式accessCheckで使用する方法を見つけるのに苦労しています。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>

4

2 に答える 2

5

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);
}
于 2012-11-22T13:11:56.097 に答える
0

うまくいくかどうかはわかりませんが、次のことを試してください。

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;
}
于 2012-11-22T13:13:12.200 に答える