2017-07-07 34 views
1

我試圖創建一個WhereLike擴展到IQueryable,但我無法知道屬性的類型在運行時。IQueryable WhereLike擴展與未知的屬性類型

這裏是我的代碼:

public static IQueryable WhereLike(this IQueryable source, string propertyName, string pattern) 
    { 
     if (source == null) throw new ArgumentNullException("source"); 
     if (propertyName == null) throw new ArgumentNullException("propertyName"); 

     var a = Expression.Parameter(typeof(object), "a"); 
     var prop = Expression.Property(a, propertyName); 

     return source.Provider.CreateQuery(
      Expression.Call(
       typeof(SqlMethods), "Like", 
       null, 
       prop, Expression.Constant(pattern))); 
    } 

我得到異常:實例屬性「富」沒有爲類型定義「System.Object的」

你知道一種方法來處理屬性設置不在編譯時知道目標類型?

+0

並不像運營商的SQL只對字符串的工作? – Fran

+0

@Fran爲什麼這很重要? – Servy

+0

@Servy因爲如果他無法確定屬性的類型,他可以嘗試在不支持它的屬性上使用WhereLike(DateTime,Double,....),那麼他會在運行時發生異常它發生。也許他試圖抓住源ElementType並使用反射來查找屬性類型。 – Fran

回答

1

如果你能夠使用通用IQueryable<T>變異,這將成爲一個更簡單的問題,因爲你不再需要CreateQuery並可以對直接執行IQueryable<T>來源。

public static IQueryable<T> WhereLike<T>(this IQueryable<T> source, string propertyName, 
    string pattern) 
{ 
    if (source == null) throw new ArgumentNullException("source"); 
    if (propertyName == null) throw new ArgumentNullException("propertyName"); 

    var a = Expression.Parameter(typeof(T), "a"); 
    var prop = Expression.PropertyOrField(a, propertyName); 

    var expr = Expression.Call(
      typeof(SqlMethods), "Like", 
      null, 
      prop, Expression.Constant(pattern)); 

    var lambda = Expression.Lambda<Func<T, bool>>(expr, a); 
    return source.Where(lambda); 
} 

注意兩個關鍵點:

而不是僅僅抓住屬性,如果我們使用PropertyOrField我們可以適當支持可以暴露領域的Linq-2-SQL生成的代碼。

另外,由於我們針對IQueryable<T>源執行,我們需要從我們的「Like」MethodCallExpression的結果創建lambda表達式。


如果您需要非一般的變種,你仍然可以完成同樣的事情,但你需要換你像MethodCallExpression在哪裏MethodCallExpression,以便它被正確的結構:

public static IQueryable WhereLike(this IQueryable source, string propertyName, 
    string pattern) 
{ 
    if (source == null) throw new ArgumentNullException("source"); 
    if (propertyName == null) throw new ArgumentNullException("propertyName"); 

    var a = Expression.Parameter(source.GetType().GetGenericArguments().First(), "a"); 
    var prop = Expression.PropertyOrField(a, propertyName); 

    var expr = Expression.Call(
      typeof(SqlMethods), "Like", 
      null, 
      prop, Expression.Constant(pattern)); 

    MethodCallExpression whereCallExpression = Expression.Call(
      typeof(Queryable), 
      "Where", 
      new Type[] { source.ElementType }, 
      source.Expression, 
      Expression.Lambda(expr, a)); 

    return source.Provider.CreateQuery(whereCallExpression); 
} 

您可以調用任何使用通配符變種:

var data = source.WhereLike("ColumnName", "%o%"); 
-1

確定特定字符串是否與指定模式匹配。模式可以包含常規字符和通配符。在模式匹配期間,常規字符必須與字符串中指定的字符完全匹配。但是,通配符可以與字符串的任意片段匹配。使用通配符使得LIKE運算符比使用=和!=字符串比較運算符更靈活。如果任何一個參數不是字符串數據類型,則SQL Server數據庫引擎會將其轉換爲字符串數據類型(如果可能)。 MSDN

像運營商將只在string類型的工作。如果這就是你想要做的,你只能用Contains方法實現,也有StartsWithEndsWith等價。

您可以使用Where方法只有在這種擴展方法

var containsParam = Expression.Parameter(typeof(T), "p"); 
MemberExpression multiSelectmember = Expression.Property(containsParam,propertyname); 
    var lstValues = stringvalue; 

ConstantExpression multiSelectConstant = Expression.Constant(stringvalue); 
var callExpression = Expression.Call(typeof(String), "Contains", 
new[] { typeof(string) }, multiSelectConstant, multiSelectmember); 
var containsexp = Expression.Lambda<Func<T, bool>>(callExpression, 
               containsParam); 
+0

我也嘗試過使用StartsWith,EndsWith等...但複雜的模式(「%AA_BB%CC%」),這個解決方案似乎有限 – eli0tt