2012-02-10 11 views
11

मैं प्रोग्रामिंग रूप से एक अभिव्यक्ति वृक्ष बनाने की कोशिश कर रहा हूं।OrElse और AndAlso अभिव्यक्ति विधियों का उपयोग करके अपवाद

मैं अपने इनपुट में है, हालत वर्ग है जो निम्न प्रपत्र की एक सूची:

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

जब मैं Expression वस्तु का निर्माण मैं हर हालत के लिए एक Expression निम्नलिखित तरीके

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); 
     } 
    } 
} 
में बना

फ़िल्टर। ToExpression() विधि इस

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; 
} 
जैसे लागू की गई है

सब कुछ काम करता है जब मैं एक ही शर्त है, लेकिन मैं एक InvalidOperationException जिसमें यह कहा जाता है जब मैं And, Or, AndAlso, OrElse स्थिर तरीकों में से एक का उपयोग कर भाव गठबंधन करने के लिए प्रयास करें:

द्विआधारी ऑपरेटर या परिभाषित नहीं है 'सिस्टम.फनक 2[MyObject,System.Boolean]' and 'System.Func 2 [माइओब्जेक्ट, सिस्टम.बोलियन]' के लिए।

मुझे थोड़ा उलझन मिल रहा है। क्या कोई अपवाद के कारणों को बेहतर तरीके से समझा सकता है और समाधान का सुझाव दे सकता है?

बहुत बहुत धन्यवाद!

उत्तर

23

आप a => a == 3 और a => a == 4 को (a => a == 3) || (a => a == 4) में जोड़ रहे हैं, लेकिन आपको इसे a => (a == 3 || a == 4) बनाने की कोशिश करनी चाहिए। मैन्युअल रूप से करना मुश्किल नहीं है, लेकिन someone has done it for you already। "अभिव्यक्तियों को जोड़ना" की तलाश करें।

संपादित करें: अनुरोध के अनुसार, इसे मैन्युअल रूप से कैसे करें इसका एक सरल उदाहरण है।

संपादित 2: यह ExpressionVisitor जो .NET 4, लेकिन at MSDN you can find a usable implementation for earlier versions के लिए नई है उपयोग करता है। मुझे लगता है कि एमएसडीएन कोड आपके उद्देश्यों के लिए "तीसरी पार्टी" के रूप में योग्य नहीं है। आपको केवल protected virtual Expression Visit(Expression exp) विधि public में बदलने की आवश्यकता है। और Enumerable.Zip आपके लिए अनुपलब्ध है और यह आवश्यक नहीं है, अब यह चला गया है।

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)); 
     } 
    } 
} 
+0

हाय, आपके उत्तर के लिए धन्यवाद। मैं इस समस्या को हल करने के लिए तीसरे पक्ष कोड का उपयोग नहीं कर सकता। क्या आप बेहतर तरीके से समझा सकते हैं कि 'इसे मैन्युअल रूप से करने का तरीका क्या होगा? एक बार फिर धन्यवाद! – Lorenzo

+0

@Lorenzo निश्चित रूप से, मैंने एक उदाहरण के रूप में उपयोग किए गए दो अभिव्यक्तियों के आधार पर एक प्रोग्राम जोड़ा है। – hvd

+0

हाय, मैंने आपके समाधान को लागू करने का प्रयास किया है। मुझे समझ में आया कि 'अभिव्यक्तिविज्ञानी' लिंककिट स्रोतों से आता है और मैं यह देखने में सक्षम हूं कि यह कैसे काम करता है। अब सवाल यह है: जहां 'IEnumerable 'की' ज़िप 'विधि से आता है? मैं .NET 3.5 का उपयोग कर रहा हूं और मैं उस विधि को नहीं ढूंढ पा रहा हूं :( – Lorenzo

संबंधित मुद्दे