2013-10-17 5 views
14
Expression<Func<MyObject, string>> fn1 = x => x.PossibleSubPath.MyStringProperty; 

Expression<Func<string, bool>> fn2 = x => x.Contains("some literal"); 

वहाँ एक नया लैम्ब्डा अभिव्यक्ति जो मूल रूप से fn1 के उत्पादन में उपयोग करता है और fn2 के लिए इनपुट के रूप में इसे इस्तेमाल करता है बनाने के लिए एक तरीका है?कम्बाइन दो Linq लैम्ब्डा भाव

Expression<Func<MyObject, bool>> fnCombined = ... 

मुझे पता है कि मैं समारोह में एक बार बना सकते हैं, लेकिन समस्या यह है कि मैं कुछ सामान्य कोड बना रहा हूं और इसलिए वास्तव में एक ऐसे में अलग से इन दोनों कार्यों को बनाने के लिए, तो उन्हें गठबंधन में सक्षम होने की जरूरत है जिस तरह से लिंकक मेरे डेटाबेस ऑब्जेक्ट्स (एंटिटी फ्रेमवर्क) पर उनका उपयोग कर सकता है।

+1

अभिव्यक्ति हेरफेर/संयोजन दिखाते हुए एक उत्तर दिया गया है। http://stackoverflow.com/a/9683506/8155 –

+0

@ डेविड बी लिंक के लिए धन्यवाद; मुझे एक अभिव्यक्ति के सभी उदाहरणों को दूसरे के साथ बदलने के तरीके को समझने में परेशानी हो रही थी। – Servy

+0

बहुत बहुत धन्यवाद, दोस्तों! –

उत्तर

22

तो तार्किक रूप से हम जो करने में सक्षम होना चाहते हैं वह एक नया लैम्ब्डा बना रहा है जिसमें इसमें पहले फ़ंक्शन में इनपुट का पैरामीटर होता है, और वह शरीर जो उस पैरामीटर के साथ पहला फ़ंक्शन कॉल करता है और फिर परिणाम को पास करता है दूसरे फ़ंक्शन के पैरामीटर, और फिर उसे वापस कर देता है।

हम दोहराने कर सकते हैं कि आसानी से पर्याप्त Expression वस्तुओं का उपयोग कर:

public static Expression<Func<T1, T3>> Combine<T1, T2, T3>(
    Expression<Func<T1, T2>> first, 
    Expression<Func<T2, T3>> second) 
{ 
    var param = Expression.Parameter(typeof(T1), "param"); 
    var body = Expression.Invoke(second, Expression.Invoke(first, param)); 
    return Expression.Lambda<Func<T1, T3>>(body, param); 
} 

दुःख की बात है एफई और अधिकांश अन्य क्वेरी प्रदाताओं वास्तव में क्या है कि के साथ क्या करना पता नहीं होगा और ठीक ढंग से काम नहीं करेगा। जब भी उन्होंने अभिव्यक्ति को मारा तो वे आम तौर पर किसी प्रकार का अपवाद फेंक देते हैं। कुछ हालांकि इसे संभाल सकते हैं। सिद्धांत रूप में उन्हें आवश्यक सभी जानकारी वहां है, अगर वे इसे पाने के लिए मजबूती से लिखे गए हैं।

हालांकि हम क्या कर सकते हैं, एक वैचारिक दृष्टिकोण से, उस लैम्ब्डा के शरीर में पहले लैम्ब्डा के पैरामीटर के प्रत्येक उदाहरण को प्रतिस्थापित करने वाले नए लैम्ब्डा के पैरामीटर के साथ प्रतिस्थापित करें, और फिर दूसरे लैम्ब्डा के पैरामीटर के सभी उदाहरणों को प्रतिस्थापित करें पहले लैम्ब्डा के नए शरीर के साथ दूसरे लैम्ब्डा में। तकनीकी रूप से, यदि इन अभिव्यक्तियों के दुष्प्रभाव होते हैं, और इन पैरामीटरों का एक से अधिक बार उपयोग किया जाता है, तो वे समान नहीं होंगे, लेकिन इन्हें ईएफ क्वेरी प्रदाता द्वारा पार्स किया जा रहा है, उन्हें वास्तव में कभी भी दुष्प्रभाव नहीं होने चाहिए।

this related question पर एक लिंक प्रदान करने के लिए डेविड बी को धन्यवाद जो ReplaceVisitor कार्यान्वयन प्रदान करता है। हम अभिव्यक्ति के पूरे पेड़ के माध्यम से जाने के लिए ReplaceVisitor का उपयोग कर सकते हैं और एक अभिव्यक्ति को दूसरे के साथ प्रतिस्थापित कर सकते हैं। उस प्रकार के कार्यान्वयन है:

class ReplaceVisitor : ExpressionVisitor 
{ 
    private readonly Expression from, to; 
    public ReplaceVisitor(Expression from, Expression to) 
    { 
     this.from = from; 
     this.to = to; 
    } 
    public override Expression Visit(Expression node) 
    { 
     return node == from ? to : base.Visit(node); 
    } 
} 

और अब हम हमारे उचितCombine विधि लिख सकते हैं:

public static Expression<Func<T1, T3>> Combine<T1, T2, T3>(
    Expression<Func<T1, T2>> first, 
    Expression<Func<T2, T3>> second) 
{ 
    var param = Expression.Parameter(typeof(T1), "param"); 

    var newFirst = new ReplaceVisitor(first.Parameters.First(), param) 
     .Visit(first.Body); 
    var newSecond = new ReplaceVisitor(second.Parameters.First(), newFirst) 
     .Visit(second.Body); 

    return Expression.Lambda<Func<T1, T3>>(newSecond, param); 
} 

और एक साधारण परीक्षण का मामला, बस प्रदर्शित करने के लिए क्या हो रहा है:

Expression<Func<MyObject, string>> fn1 = x => x.PossibleSubPath.MyStringProperty; 
Expression<Func<string, bool>> fn2 = x => x.Contains("some literal"); 

var composite = Combine(fn1, fn2); 

Console.WriteLine(composite); 

जो प्रिंट करेगा:

परम => param.PossibleSubPath.MyStringProperty.Contains ("कुछ शाब्दिक")

है कौन सा हम वास्तव में क्या चाहते हैं; एक प्रश्न प्रदाता को पता चलेगा कि इस तरह कुछ कैसे पार्स करना है।

+0

यदि 'fn2' एक बार से अधिक पैरामीटर का उपयोग करता है, तो 'fn1' एक से अधिक बार दोहराया जाएगा। उम्मीद है कि यह या तो मामूली है या क्वेरी प्रदाता द्वारा बुद्धिमानी से संभाला जाता है, लेकिन यह कई बार महत्वपूर्ण काम करने के लिए समाप्त हो सकता है (स्थानीय चर की तरह कुछ काम करना चाहिए, क्वेरी प्रदाताओं को छोड़कर शायद इसका समर्थन नहीं करेगा)। –

+0

@TimS। हाँ, मैंने अपने जवाब में थोड़ा सा चर्चा की। ईमानदार होने के लिए, मुझे उम्मीद नहीं है कि यह एक बड़ा मुद्दा होगा क्योंकि मैं अपेक्षा करता हूं कि डीबी अंत में क्वेरी ऑप्टिमाइज़र ऐसे मामलों को प्रभावी ढंग से संभालने में सक्षम हो। यह भी प्रकट होगा कि ओपी की तरह की अभिव्यक्तियों की तरह कोई समस्या नहीं होगी। य़ह कहना कठिन है। जैसा कि आपने बताया है, एक स्थानीय चर लगभग निश्चित रूप से सही ढंग से पार्स नहीं किया जा सका। – Servy

+0

@ सर्वी, क्या आप प्रतिबिंबित करने के लिए अपना उत्तर अपडेट कर सकते हैं, पहले शरीर से पैरामीटर पास करना, काम नहीं करेगा क्योंकि वह पैरामीटर एक अलग अभिव्यक्ति के दायरे में परिभाषित किया गया है, इसलिए जब आप अपनी अभिव्यक्ति को जोड़ते हैं 'x => x.condition = = true && x.anotherCondition == false' दूसरा x पहले में एक ही एक्स नहीं है। (आप शायद वह शब्द कर सकते हैं जो मैं बेहतर कहने की कोशिश कर रहा हूं) –

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