2013-11-01 21 views
11

Genel olarak filtrelemeyi işlemek için bir uzantı yöntemi oluşturarak kodumu biraz temizlemeye çalışıyorum.Linq ile nesnelere yansıma kullanmak mümkün mü?

Temizlemeye çalıştığım kod İşte.

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

Açıkçası çok tekrarlı. Bu yüzden yansıma kullanarak bir özellik tarafından filtrelenen bir uzantı yöntemi oluşturmaya çalıştım. İşte yöntem. "GetValue" varlığa linq tanınan olmadığını belirterek, linq çalıştırıldığında bir hata alıyorum,

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

Ama:

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

ve bu kadar

şöyle çağrılacak.

Bunu temizlemek için başka bir yol var mı, yoksa çirkin mi bırakmak zorundayım?

cevap

15

Teknik olarak, evet, bunu yapabilirdiniz, ancak Expression'u Where'a geçmek için kendiniz oluşturmanız gerekir.

Bu, özelliği bir dize değeri olarak kabul etmek yerine, seçilen nesnenin geçerli olduğunu doğrulamak için zaman desteğini derlemeniz için bir parametre olarak bir Expression<Func<T, string>> kabul etmeyi düşünmeniz gerektiğini söyledi.

Genel bölümü temsil eden bir ifadeyle başlayacağız; * iki * parametresi, nesne ve verilen özelliğin değeri ile bir işlevi temsil eder. Daha sonra, bu ikinci parametrenin tüm örneklerini, gerçek yöntemin parametrelerinde tanımladığımız özellik seçiciyle değiştirebiliriz.

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

Ve bu yöntem, bir ifadenin tüm örneklerini, belirli bir ifadede bir diğeriyle değiştirmek için bir işlev kullanır. Bunun uygulamasıdır: Eğer gerçekten bir dize olarak özellik adını kabul etmek istiyorum Eğer

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

sonra sadece aşağıdaki newSelector tanımını değiştirin:

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

Yep bunu yaptığıma. Teşekkürler. Şimdi sadece cevabınızı anlamak için birkaç kez daha tekrar okumalıyım. – Smeegs

+1

@Smeegs Bazı gerçek değerler ile hata ayıklamaya yardımcı olabilir ve oluşturulan ifadelere (ve muhtemelen değişkenlerde saklanmadığım ara değerlerin bir kısmına) bakın. Onu görselleştirmeye yardımcı olur. İfadeler genellikle duyarlı "ToString" uygulamalarına sahiptir. – Servy

+0

Hangi montajları/adlarını kullanıyorsunuz? SqlFunctions hem "EntityFramework.SqlServer" hem de "System.Data.Entity" içinde olduğunu görüyorum ... Bunların farklı olduğunu varsayıyorum, doğru mu? – David

İlgili konular