2014-02-27 90 views
0

原諒我,我不完全確定我的問題措辭正確。 我正在創建一個搜索組件,用戶可以使用不同的運算符搜索不同的字段。 description.contains(關鍵字)和measurement.startsWith(碼).....將屬性/字段名傳遞給謂詞

因此,這裏是我:

void SearchDescription(IQueryable<MyClass> results, string keywords) 
{ 
    switch(operator) 
    { 
     case "Contains": 
      results=results.Where(ele => ele.description.Contains(keywords)); 
      break; 
     case "StartsWith": 
      results = results.Where(ele => ele.description.StartsWith(keywords)); 
      break; 
     ... and so on..... 
    } 
} 

目前,我有一個方法,上述只是每場... SearchDescription(),SearchID(),SearchMeasure()等。唯一的區別是字段/屬性名稱。

UPDATE

在進一步的研究可能是這樣的:

void Search(IQueryable<Entity> results, string keywords, Expression<Func<Entity>,object>> predicate) 
{ 
    results = results.Where(ele => predicate.Contains(keywords)); 
} 

可稱爲像:

Search(results, "my search terms", ele => ele.description); 

這顯然是目前的形式是不行的,但也許這是對我所追求的內容的更清晰的描述。

感謝迄今爲止的所有答覆。

+0

看看這裏http://stackoverflow.com/a/10283288/1300049。或者,您可以定義一些操作來獲取所需的屬性並將它們傳遞給您的搜索方法 – JleruOHeP

+0

您可以查看一些動態Linq選項。有些使用反射和一些構建表達樹。據我所知,這並不像你希望的那樣直截了當。 –

回答

-1

這可以通過實施Compose方法,將採取兩個表達式,並返回充當如果它會調用第一表達式來完成,然後提供作爲參數傳遞給第二:

void Search(IQueryable<Entity> results, string keywords, 
    Expression<Func<Entity, string>> selector) 
{ 
    results = results.Where(selector.Compose(obj => obj.Contains(keywords))); 
} 

要實現我們想要使用一個輔助方法,讓我們用另一個替換一個表達的所有實例開始:

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

使用該工具是釀回到一起爲lambda更換了一把簡單:

public static Expression<Func<TFirstParam, TResult>> 
    Compose<TFirstParam, TIntermediate, TResult>(
    this Expression<Func<TFirstParam, TIntermediate>> first, 
    Expression<Func<TIntermediate, TResult>> second) 
{ 
    var param = Expression.Parameter(typeof(TFirstParam), "param"); 

    var newFirst = first.Body.Replace(first.Parameters[0], param); 
    var newSecond = second.Body.Replace(second.Parameters[0], newFirst); 

    return Expression.Lambda<Func<TFirstParam, TResult>>(newSecond, param); 
} 

它也似乎很奇怪的查詢過濾項,其中一個字符串與所有的單詞包含在給定的字段中。您似乎更希望獲得包含任意字符串列表的任何的項目。這是不同的,只需要更多的工作。

我們可以使用一個新類,我們將其稱爲PredicateBuilder來構建一個篩選器,該篩選器採用一堆其他篩選器的邏輯OR

void Search(IQueryable<Entity> results, IEnumerable<string> keywords, 
    Expression<Func<Entity, string>> selector) 
{ 
    var finalFilter = keywords.Aggregate(
     PredicateBuilder.False<Entity>(), 
     (filter, keyword) => filter.Or(
      selector.Compose(obj => obj.Contains(keyword)))); 
    results = results.Where(finalFilter); 
} 

我們可以使用之前,像這樣定義的Replace方法實現這個類:

public static class PredicateBuilder 
{ 
    public static Expression<Func<T, bool>> True<T>() { return f => true; } 
    public static Expression<Func<T, bool>> False<T>() { return f => false; } 

    public static Expression<Func<T, bool>> Or<T>(
     this Expression<Func<T, bool>> expr1, 
     Expression<Func<T, bool>> expr2) 
    { 
     var secondBody = expr2.Body.Replace(expr2.Parameters[0], expr1.Parameters[0]); 
     return Expression.Lambda<Func<T, bool>> 
       (Expression.OrElse(expr1.Body, secondBody), expr1.Parameters); 
    } 

    public static Expression<Func<T, bool>> And<T>(
     this Expression<Func<T, bool>> expr1, 
     Expression<Func<T, bool>> expr2) 
    { 
     var secondBody = expr2.Body.Replace(expr2.Parameters[0], expr1.Parameters[0]); 
     return Expression.Lambda<Func<T, bool>> 
       (Expression.AndAlso(expr1.Body, secondBody), expr1.Parameters); 
    } 
} 
+0

謝謝。這很有幫助。 – Matt

-1

您可以使用System.Reflection使用所需屬性的名稱檢索PropertyInfo。請注意,反射速度有點慢,如果在一秒鐘內多次使用,它會嚴重降低程序速度。

void Search(IQueryable<MyClass> results, string keywords, string propertyName) 
{ 
    PropertyInfo elePropInfo = ele.GetType().GetProperty(propertyName); 
    string elePropValue = (string)elePropInfo.GetValue(ele, null); // the second argument should be null for non-indexed properties 
    switch(operator) 
    { 
     case "Contains": 
      results = results.Where(ele => elePropValue.Contains(keywords)); 
      break; 
     case "StartsWith": 
      results = results.Where(ele => elePropValue.StartsWith(keywords)); 
      break; 
     // etc 
    } 
} 
GetProperty()

更多信息可以在這裏找到在MSDN:http://msdn.microsoft.com/en-us/library/kz0a8sxy(v=vs.110).aspx

+0

有沒有一種方法可以做到這一點,使屬性名仍然是強類型而不是字符串? – Matt

+0

@Matt我不確定,但我不這麼認爲。 – Spans

+0

elePropInfo返回空....想法? – Matt