Lambda動態(tài)附加條件和排序語句 (可用于linq to sql)
一種常見的情況, 使用linq-to-sql 或者lambda(Entity framework)去數(shù)據(jù)庫取條件的時候需要附加多個條件或者排序語句
以 Name="John" Age=23 為例
先明確一個概念 對于Lambda來說 以下兩個表達(dá)式是等價的
1. list.Where(p=>p.Name=="John" && p.Age==23)
2. list.Where(p=>p.Name=="John" ).Where(p=>p.Age==23)
lambda表達(dá)式只是創(chuàng)建了一個方法的定義 實際上并沒有執(zhí)行
真正的執(zhí)行一般等到ToList()方法(內(nèi)部其實是IEnumerable<T>)被執(zhí)行的時候才真正的去取數(shù)據(jù)
而且lambda表達(dá)式支持如上所示使用多個小表達(dá)式的串聯(lián)操作
那么如果有多個條件只要一直增加Where表達(dá)式就ok了
第二個問題, 以上的代碼都是寫死的 我們?nèi)绾瓮ㄟ^一般web上提交的string類型的鍵值對來過濾信息呢?
例如我們收到的數(shù)據(jù)往往是 "Name":"John","Age"="23"
總不能寫以下代碼吧:
代碼
var list = context;
if (forms.ContainsKey("Name"))
{
list.Where(p => p.Name == forms["Name"]);
}
if (forms.ContainsKey("Age"))
{
list.Where(p => p.Age == Convert.ToInt32(forms["Age"]));
}
var data = list.ToList();
這些代碼不僅僅擴展性不好.....而且加一個條件你就要加一個代碼快,工作量大又容易出Bug
理想的做法應(yīng)該想下面一樣(注意 用where條件過濾的時候條件經(jīng)常復(fù)雜 例如Age=10 , Age>10 , Name.Startwith("張") 所以實際上過濾條件一般不用下面的做法
真正用下面的做法的往往都是OrderBy age desc,name asc 這樣的排序條件)
foreach (var item in forms.Keys)
{
list.Where(build condition.....);//在這里動態(tài)附加所有的條件
}
那么如何根據(jù)已知的string類型的屬性名稱和值 來創(chuàng)建表達(dá)式呢?以下是動態(tài)創(chuàng)建表達(dá)式的代碼,以排序為例
代碼
var property = typeof(TSource).GetProperty(item);
var parameter = Expression.Parameter(typeof(TSource), "p");
var propertyAccess = Expression.MakeMemberAccess(parameter, property);
var orderByExp = Expression.Lambda(propertyAccess, parameter);
if (message.Sort[item] == PageSortDirection.Ascending)//判斷是升序還是降序 如果是應(yīng)用到搜索條件 那么就是 判斷應(yīng)該用= > < like 還是別的什么
{
MethodCallExpression resultExp = Expression.Call(typeof(Queryable), "OrderBy", new Type[] { typeof(TSource), property.PropertyType }, list.Expression, Expression.Quote(orderByExp));
list = list.Provider.CreateQuery<TSource>(resultExp);
}
else
{
MethodCallExpression resultExp = Expression.Call(typeof(Queryable), "OrderByDescending", new Type[] { typeof(TSource), property.PropertyType }, list.Expression, Expression.Quote(orderByExp));
list = list.Provider.CreateQuery<TSource>(resultExp);
}
千萬不要直接用list=list.OrderBy(p=>orderByExp) 而應(yīng)該用Expression.Call來組合語句
雖然兩者最終出來的結(jié)果是一樣的, 不過前者會把數(shù)據(jù)load到本地以后再過濾, 因為entity framework沒法把這種動態(tài)的表達(dá)式直接解析為sql.
真正的邏輯就是根據(jù)每一個條件和排序表達(dá)式 構(gòu)建一個Where 或者Order的表達(dá)式 然后逐個附加到現(xiàn)在的lambda表達(dá)式上
性能問題:
考慮到Entity framework的應(yīng)用場景, 還有權(quán)重問題(實際上數(shù)據(jù)庫操作比這邊的動態(tài)表達(dá)式耗性能多了) 這樣的性能損耗是非常理想的
異常:
如果傳入的string實際上不是類型的屬性,例如傳了一個LastName 而class實際上沒有這個屬性 , 會拋出異常

浙公網(wǎng)安備 33010602011771號