2012-11-22 16 views
5

विभिन्न एसओ पदों से जानकारी का उपयोग करना और विशेष रूप से blog (के बजाय AndAlso का उपयोग करने के लिए संशोधित) मैंने समान टाइप किए गए लिनक्स अभिव्यक्तियों को एक ही पूर्वानुमान में संयोजित करने में कामयाब रहा है। लेकिन अब मैं दो अभिव्यक्तियों को जोड़ना चाहता हूं जहां एक दूसरे के लिए इनपुट है। यहां पूरी तरह से विस्तारित मूल 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; 
    } 

ध्यान दें कि महत्वपूर्ण है, मैं इन के रूप में भाव) गठबंधन करने के लिए क्योंकि मेरी डीबी चालक पेड़ चलना और एक देशी कॉल तो संकलित (का उपयोग कर में तब्दील करने की जरूरत है प्रबंधन करने की आवश्यकता एक विकल्प नहीं है।

तो नीचे दिया गया फ़ंक्शन मैं ऊपर Any() कॉल के साथ गठबंधन करना चाहता हूं। अंतिम आउटपुट अभिव्यक्ति को 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)); 
    } 

मैं जहाँ तक इस रूप में मिल गया है, लेकिन मैं बाहर काम accessCheck से बाहर accessList => स्वैप करने के लिए कैसे और यह एक एकल अभिव्यक्ति में 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>

+0

आपने 'ExpressionIsNamed' परिभाषा में टाइप पैरामीटर छोड़ा। –

+0

@ हैमलेट हाकोबियन: जरूरी नहीं। वे जेनेरिक पैरामीटर को इस निजी विधि वाले वर्ग पर परिभाषित किया जा सकता है। –

+0

@DanielHilgarth ठीक है। धन्यवाद! –

उत्तर

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

धन्यवाद। मेरे पास कुछ समान काम है (मेरी अपरिपक्व पोस्ट देखें), लेकिन मुझे बेमेल किए गए आईनेमरेबल प्रकारों से संबंधित एक अलग समस्या है। – cirrus

+1

@ कैरसस आपने उन्हें सही ढंग से विलय नहीं किया है; मुझे अपने कोड के संदर्भ में उपरोक्त दिखाने के लिए संपादित करें ... –

+0

बहुत बढ़िया, धन्यवाद!न केवल संकलन और दौड़ने के लिए, यह डीबी चालक में बार्फ़ नहीं था (जो अभिव्यक्ति को गठबंधन करने के लिए आवश्यक कारण था) और मेरे पास यह साबित करने के लिए पास किए गए परीक्षणों का एक स्तंभ है। – cirrus

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; 
} 
+1

आमंत्रित अविश्वसनीय है; यह सभी प्रदाताओं पर काम नहीं करता है - ईएफ कुख्यात रूप से नफरत करता है, और संभवतः अभी भी करता है। –

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