Vraag Uitzondering met behulp van OrElse en AndAlso-expressiemethoden


Ik probeer een uitdrukkingsboom programmatisch te bouwen.

Ik heb in mijn input een lijst met conditieklassen die de volgende vorm hebben:

public class Filter
{
    public string field { get; set; }
    public string operator { get; set; }
    public string value { get; set; }
}

Wanneer ik de Expression object maak ik een Expression voor elke conditie op de volgende manier

foreach ( Filter sf in rules ) {
    Expression ex = sf.ToExpression( query );
    if ( mainExpression == null ) {
        mainExpression = ex;
    }
    else {
        if ( logicalCondition == "AND" ) {
            mainExpression = Expression.And( mainExpression, ex );
        }
        else if ( logicalCondition == "OR" ) {
            mainExpression = Expression.Or( mainExpression, ex );
        }
    }
}

De methode Filter.ToExpression () is op deze manier geïmplementeerd

public override Expression ToExpression( IQueryable query ) {
    ParameterExpression parameter = Expression.Parameter( query.ElementType, "p" );
    MemberExpression memberAccess = null;
    foreach ( var property in field.Split( '.' ) )
        memberAccess = MemberExpression.Property( memberAccess ?? ( parameter as Expression ), property );
    ConstantExpression filter = Expression.Constant( Convert.ChangeType( value, memberAccess.Type ) );
    WhereOperation condition = (WhereOperation)StringEnum.Parse( typeof( WhereOperation ), operator );
    LambdaExpression lambda = BuildLambdaExpression( memberAccess, filter, parameter, condition, value );
    return lambda;
}

Alles werkt als ik een enkele voorwaarde heb, maar als ik expressies probeer te combineren met een van de And, Or, AndAlso, OrElse statische methoden ontvang ik een InvalidOperationException dat zegt:

De binaire operator Of is niet gedefinieerd voor de typen   'System.Func2[MyObject,System.Boolean]' and 'System.Func2 [MijnObj, System.Boolean].

Ik raak een beetje in de war. Kan iemand de redenen van de uitzondering beter uitleggen en een oplossing voorstellen?

Heel erg bedankt!


11
2018-02-10 16:54


oorsprong


antwoorden:


Je bent aan het combineren a => a == 3 en a => a == 4 in (a => a == 3) || (a => a == 4), maar in plaats daarvan zou je het moeten proberen te maken a => (a == 3 || a == 4). Dit is niet zo moeilijk om handmatig te doen, maar iemand heeft het al voor je gedaan. Zoek naar "Combining Expressions".

Bewerk: zoals gevraagd, een eenvoudig voorbeeld van hoe dit handmatig te doen.

Bewerk 2: het gebruikt ExpressionVisitor wat nieuw is voor .NET 4, maar op MSDN vindt u een bruikbare implementatie voor eerdere versies. Ik veronderstel dat MSDN-code niet kwalificeert als "derde partij" voor uw doeleinden. U hoeft alleen de protected virtual Expression Visit(Expression exp) methode om public. En als Enumerable.Zip is niet beschikbaar voor u en het is niet nodig, het is nu weg.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;

namespace DemoApp
{
    <include ExpressionVisitor definition here for .NET 3.5>

    public class ExpressionParameterReplacer : ExpressionVisitor
    {
        public ExpressionParameterReplacer(IList<ParameterExpression> fromParameters, IList<ParameterExpression> toParameters)
        {
            ParameterReplacements = new Dictionary<ParameterExpression, ParameterExpression>();
            for (int i = 0; i != fromParameters.Count && i != toParameters.Count; i++)
                ParameterReplacements.Add(fromParameters[i], toParameters[i]);
        }
        private IDictionary<ParameterExpression, ParameterExpression> ParameterReplacements
        {
            get;
            set;
        }
        protected override Expression VisitParameter(ParameterExpression node)
        {
            ParameterExpression replacement;
            if (ParameterReplacements.TryGetValue(node, out replacement))
                node = replacement;
            return base.VisitParameter(node);
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Expression<Func<int, bool>> exprA = a => a == 3;
            Expression<Func<int, bool>> exprB = b => b == 4;
            Expression<Func<int, bool>> exprC =
                Expression.Lambda<Func<int, bool>>(
                    Expression.OrElse(
                        exprA.Body,
                        new ExpressionParameterReplacer(exprB.Parameters, exprA.Parameters).Visit(exprB.Body)),
                    exprA.Parameters);
            Console.WriteLine(exprA.ToString());
            Console.WriteLine(exprB.ToString());
            Console.WriteLine(exprC.ToString());
            Func<int, bool> funcA = exprA.Compile();
            Func<int, bool> funcB = exprB.Compile();
            Func<int, bool> funcC = exprC.Compile();
            Debug.Assert(funcA(3) && !funcA(4) && !funcA(5));
            Debug.Assert(!funcB(3) && funcB(4) && !funcB(5));
            Debug.Assert(funcC(3) && funcC(4) && !funcC(5));
        }
    }
}

25
2018-02-10 17:29