2013-11-01 5 views
11

मैं सामान्य रूप से फ़िल्टरिंग को संभालने के लिए एक एक्सटेंशन विधि बनाकर थोड़ा सा कोड साफ़ करने की कोशिश कर रहा हूं।क्या इकाई से linq के साथ प्रतिबिंब का उपयोग करना संभव है?

यहां वह कोड है जिसे मैं साफ़ करने की कोशिश कर रहा हूं।

var queryResult = (from r in dc.Retailers select r); 
if (!string.IsNullOrEmpty(firstName)) 
    queryResult = queryResult.Where(ex => SqlFunctions.PatIndex(firstName.Trim(), ex.FirstName.Trim()) > 0); 
if (!string.IsNullOrEmpty(lastName)) 
    queryResult = queryResult.Where(ex => SqlFunctions.PatIndex(lastName.Trim(), ex.LastName.Trim()) > 0); 
if (!string.IsNullOrEmpty(companyName)) 
    queryResult = queryResult.Where(ex => SqlFunctions.PatIndex(companyName.Trim(), ex.CompanyName.Trim()) > 0); 
if (!string.IsNullOrEmpty(phone)) 
    queryResult = queryResult.Where(ex => SqlFunctions.PatIndex(phone.Trim(), ex.Phone.Trim()) > 0); 
if (!string.IsNullOrEmpty(email)) 
    queryResult = queryResult.Where(ex => SqlFunctions.PatIndex(email.Trim(), ex.Email.Trim()) > 0); 
if (!string.IsNullOrEmpty(city)) 
    queryResult = queryResult.Where(ex => SqlFunctions.PatIndex(city.Trim(), ex.City.Trim()) > 0); 
if (!string.IsNullOrEmpty(zip)) 
    queryResult = queryResult.Where(ex => SqlFunctions.PatIndex(zip.Trim(), ex.Zip.Trim()) > 0); 
if (!string.IsNullOrEmpty(country)) 
    queryResult = queryResult.Where(ex => SqlFunctions.PatIndex(country.Trim(), ex.Country.Trim()) > 0); 
if (!string.IsNullOrEmpty(state)) 
    queryResult = queryResult.Where(ex => SqlFunctions.PatIndex(state.Trim(), ex.State.Trim()) > 0); 

यह स्पष्ट रूप से बहुत ही दोहरावदार है। तो मैंने एक विस्तार विधि बनाने की कोशिश की जो प्रतिबिंब का उपयोग कर संपत्ति द्वारा फ़िल्टर किया गया। विधि यहाँ है।

public static void FilterByValue<T>(this IQueryable<T> obj, string propertyName, string propertyValue) 
{ 
    if (!string.IsNullOrEmpty(propertyValue)) 
    { 
     obj = 
      obj.Where(
       ex => 
        SqlFunctions.PatIndex(propertyValue.Trim(), (string)typeof(T).GetProperty(propertyName, 
         BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase).GetValue(ex)) > 0 
        ); 
    } 
} 

और यह है, इसलिए की तरह कहा जाता है के लिए किया जा:

var queryResult = (from r in dc.Retailers select r); 
queryResult.FilterByValue("firstname", firstName); 

लेकिन, जब LINQ कार्यान्वित मैं कोई त्रुटि मिलती है, उन्होंने कहा कि "getValue" संस्था के लिए LINQ में मान्यता प्राप्त नहीं है।

तो, क्या इसे साफ़ करने का कोई और तरीका है, या क्या मुझे इसे बदसूरत छोड़ना है?

उत्तर

15

तकनीकी रूप से, हाँ, आप इसे कर सकते हैं, लेकिन आपको Expression को Where पर जाने के लिए स्वयं को बनाना होगा।

कहा यही कारण है, बल्कि एक स्ट्रिंग मान के रूप में संपत्ति को स्वीकार करने से आप के बजाय एक पैरामीटर के रूप में एक Expression<Func<T, string>> को स्वीकार करने, ताकि आप यह सत्यापित करते हुए चयनित ऑब्जेक्ट मान्य है के लिए समय समर्थन संकलन है विचार करना चाहिए।

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

public static IQueryable<T> FilterByValue<T>(
    this IQueryable<T> obj, 
    Expression<Func<T, string>> propertySelector, 
    string propertyValue) 
{ 
    if (!string.IsNullOrEmpty(propertyValue)) 
    { 
     Expression<Func<T, string, bool>> expression = 
      (ex, value) => SqlFunctions.PatIndex(propertyValue.Trim(), 
       value.Trim()) > 0; 

     var newSelector = propertySelector.Body.Replace(
      propertySelector.Parameters[0], 
      expression.Parameters[0]); 

     var body = expression.Body.Replace(expression.Parameters[1], 
      newSelector); 
     var lambda = Expression.Lambda<Func<T, bool>>(
      body, expression.Parameters[0]); 

     return obj.Where(lambda); 
    } 
    else 
     return obj; 
} 

और यह विधि एक दिए गए अभिव्यक्ति में दूसरे के साथ एक अभिव्यक्ति के सभी उदाहरणों को बदलने के लिए एक समारोह का उपयोग करता है। इस बात का दिया गया है:

public 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); 
    } 
} 

public static Expression Replace(this Expression expression, 
    Expression searchEx, Expression replaceEx) 
{ 
    return new ReplaceVisitor(searchEx, replaceEx).Visit(expression); 
} 

तुम सच में स्ट्रिंग के रूप संपत्ति नाम को स्वीकार करना चाहते तो बस निम्नलिखित के साथ newSelector की परिभाषा बदल देते हैं:

var newSelector = Expression.Property(expression.Parameters[0], propertyName); 
+1

हां, यही किया था। धन्यवाद। अब मुझे इसे समझने के लिए बस कुछ और बार जवाब देना होगा। – Smeegs

+1

@ सेमेग्स यह कुछ वास्तविक मूल्यों के साथ इसे डीबग करने में मदद कर सकता है और बनाए गए अभिव्यक्तियों पर एक नज़र डालने में मदद कर सकता है (और संभावित रूप से कुछ इंटरमीडिएट वैल्यू जो मैं चर में स्टोर नहीं करता)। यह इसे देखने में मदद करता है। अभिव्यक्तियों में आमतौर पर समझदार 'ToString' कार्यान्वयन होते हैं। – Servy

+0

आप कौन सी असेंबली/नामस्थान का उपयोग कर रहे हैं? मैं देखता हूं कि SQLFunctions 'EntityFramework.SqlServer' और' System.Data.Entity' दोनों में है ... मुझे लगता है कि ये अलग हैं, है ना? – David

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