[C#.NET 拾遺補漏]13:動態構建LINQ查詢表示式
阿新 • • 發佈:2020-11-26
最近工作中遇到一個這樣的需求:在某個列表查詢功能中,可以選擇某個數字列(如商品單價、當天銷售額、當月銷售額等),再選擇 `小於或等於` 和 `大於或等於` ,再填寫一個待比較的數值,對資料進行查詢過濾。
如果只有一兩個這樣的數字列,那麼使用 Entity Framework Core 可以這麼寫 LINQ 查詢:
```cs
public Task
- > GetProductsAsync(string propertyToFilter, MathOperator mathOperator, decimal value)
{
var query = _context.Products.AsNoTracking();
query = propertyToFilter switch
{
"Amount1" when mathOperator == MathOperator.LessThanOrEqual => query.Where(x => x.Amount1 <= value),
"Amount1" when mathOperator == MathOperator.GreaterThanOrEqual => query.Where(x => x.Amount1 >= value),
"Amount2" when mathOperator == MathOperator.LessThanOrEqual => query.Where(x => x.Amount2 <= value),
"Amount2" when mathOperator == MathOperator.GreaterThanOrEqual => query.Where(x => x.Amount2 >= value),
_ => throw new ArgumentException($"不支援 {propertyToFilter} 列作為數字列查詢", nameof(propertyToFilter))
};
return query.ToListAsync();
}
```
如果固定只有一兩個數字列且將來也不會再擴充套件,這樣寫簡單粗暴,也沒什麼問題。
但如果有幾十個數字列,這樣使用 `swith` 模式匹配的寫法就太恐怖了,程式碼大量重複。很自然地,我們得想辦法根據屬性名動態建立 `Where` 方法的引數。它的引數型別是:`Expression
- > GetProductsAsyncV2(string propertyToFilter, MathOperator mathOperator, decimal value)
{
var query = _context.Products.AsNoTracking();
var paramExp = Expression.Parameter(typeof(Product));
var memberExp = Expression.PropertyOrField(paramExp, propertyToFilter);
var valueExp = Expression.Constant(value);
var compareExp = mathOperator == MathOperator.LessThanOrEqual ?
Expression.LessThanOrEqual(memberExp, valueExp) :
Expression.GreaterThanOrEqual(memberExp, valueExp);
var lambda = Expression.Lambda