在寫商業層(Business Logic Layer)或資料存取層(Data Access Layer),最覺得麻煩的是,明明是同樣的資料要出輸,要為個個不同的需求做條件篩選,每一個條件都要寫成一個參數,然後一堆的if判斷,而以多一個條件又要多一個參數,或是寫一個新的Method,不小心還會影響其他地方,我覺得是一個焦油坑,就在思考有什麼方式比較好,就讓我想到Linq的方式,用Expression。
在寫商業層(Business Logic Layer)或資料存取層(Data Access Layer),最覺得麻煩的是,明明是同樣的資料要出輸,要為個個不同的需求做條件篩選,每一個條件都要寫成一個參數,然後一堆的if判斷,而以多一個條件又要多一個參數,或是寫一個新的Method,不小心還會影響其他地方,我覺得是一個焦油坑,就在思考有什麼方式比較好,就讓我想到Linq的方式,用Expression。
以前的寫法
GetProducts1("Code", "Wade", "C#", "Name", "ASC", 0, 20);
//如果有新的欄位又要多一個參數,麻煩!!
public IEnumerable<Product> GetProducts1(string name, string owner, string catalog, string orderby = "Name", string orderOriented = "ASC", int skip = 0, int take = 20)
{
var query = _proRepository.AsQueryable();
if (string.IsNullOrWhiteSpace(name))
{
query = query.Where(q => q.Name.Contains(name));
}
//......忽略部分Code。
if (orderOriented == "ASC")
{
query = query.OrderBy(orderby); //有寫以字串OrderBy的Extension
}
else
{
query = query.OrderByDescending(orderby);
}
query = query.Skip(0);
query = query.Take(take);
return query.ToArray();
}
前些日子的嘗試
GetProducts2(q => q.Name.Contains("Code") || q.EndDate > DateTime.Today);
//後來直接把Where抽出去,由呼叫端自己去決定要篩選什麼。
public IEnumerable<Product> GetProducts2(Expression<Func<Product, bool>> where, string orderby = "Name", string orderOriented = "ASC", int skip = 0, int take = 20)
{
var query = _proRepository.AsQueryable();
if (where != null)
{
//由呼叫端自己去決定要篩選什麼。
query = _proRepository.Where(where);
}
//......忽略部分Code。
if (orderOriented == "ASC")
{
query = query.OrderBy(orderby); //有寫以字串OrderBy的Extension
}
else
{
query = query.OrderByDescending(orderby);
}
query = query.Skip(0);
query = query.Take(take);
return query.ToArray();
}
最近的嘗試
GetProducts3(q => q.OrderBy(x => x.ListPrice), q => q.Where(x => x.Status == "Active"), q => q.Take(500));
//更後來還嘗試,所有條件由呼叫端決定。
public IEnumerable<Product> GetProducts3(params Expression<Func<IQueryable<Product>, IQueryable<Product>>>[] exprs)
{
bool orderby = false, take = false;
int takeAmount = 20;
var query = _proRepository.AsQueryable();
if (exprs != null)
{
foreach (var expr in exprs)
{
//做一個簡單的檢查有沒有呼叫orderby, take
if (expr.Body is MethodCallExpression)
{
var mce = expr.Body as MethodCallExpression;
switch (mce.Method.Name)
{
case "OrderBy":
orderby = true;
break;
case "OrderByDescending":
orderby = true;
break;
case "Take":
var takeArg = mce.Arguments[1] as ConstantExpression;
if ((int)takeArg.Value > 1000)
{
//最大只能1000
takeAmount = 1000;
continue;
}
take = true;
break;
default:
break;
}
}
//將Expression轉成Delegate
query = expr.Compile().Invoke(query);
}
}
//一定要排序
if (!orderby)
{
query = query.OrderBy(x => x.Name);
}
//一定要限制取的量
if (!take)
{
query = query.Take(takeAmount);
}
return query.ToArray();
}
我個人覺得,像Select部分保留較大彈性,讓呼叫端可以客製他要的東西,多一個條件要篩選,呼叫端自己決定就可以了,不用改BLL或DAL,事實上我不知道這樣寫是好還是壞,不過挺方便的就是了。