首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> C# >> 關於C#

趣味編程:C# Specification 模式實戰(下)—— 表達式樹組合支援 LINQ 查詢

     編輯:關於C#

場景升級:從委托到表達式樹

上篇實現基於 Func<T,bool> 委托,適合記憶體內業務邏輯。但若需傳遞給 LINQ Provider(如 Entity Framework),必須構建 Expression<Func<T,bool>> 表達式樹。

Not 擴展:最簡單

public static Expression<Func<T, bool>> Not<T>(
    this Expression<Func<T, bool>> expr) {
    var param = expr.Parameters[0];
    var body = Expression.Not(expr.Body);
    return Expression.Lambda<Func<T, bool>>(body, param);
}

測試:f = i => i % 2 == 0; f.Not() 產生奇數過濾。

And/Or 挑戰:參數實例不一致

直接合併 Expression.And(left.Body, right.Body) 會失敗,因兩棵樹的 ParameterExpression 不是同一物件。

解決方案:ExpressionVisitor 統一參數

internal class ParameterReplacer : ExpressionVisitor {
    private readonly ParameterExpression _target;
    public ParameterReplacer(ParameterExpression target) => _target = target;

    public Expression Replace(Expression expr) => Visit(expr);

    protected override Expression VisitParameter(ParameterExpression p) => _target;
}

And/Or 完整實現

public static Expression<Func<T, bool>> And<T>(
    this Expression<Func<T, bool>> left,
    Expression<Func<T, bool>> right) {
    var p = Expression.Parameter(typeof(T), "candidate");
    var replacer = new ParameterReplacer(p);
    var newLeft = replacer.Replace(left.Body);
    var newRight = replacer.Replace(right.Body);
    return Expression.Lambda<Func<T, bool>>(
        Expression.AndAlso(newLeft, newRight), p);
}

// Or 同理,用 Expression.OrElse

組合測試

Expression<Func<int, bool>> f = i => i % 2 == 0;
f = f.Not().And(i => i % 3 == 0).Or(i => i % 4 == 0);
// 對應邏輯:(非偶數 且 3的倍數) 或 4的倍數
// 1..6 結果:3, 4

架構建議:Query Object 限制自由度

DAO 直接暴露 Expression<Func<T,bool>> 過於自由,易傳入不支援或低效表達式。建議封裝 Criteria 基類:

public abstract class ProductCriteria {
    protected ProductCriteria(Expression<Func<Product,bool>> q) => Query = q;
    public Expression<Func<Product,bool>> Query { get; }
}

public class ProductIdEqCriteria : ProductCriteria {
    public ProductIdEqCriteria(int id) : base(p => p.ProductID == id) { }
}

public class ProductViewRangeCriteria : ProductCriteria {
    public ProductViewRangeCriteria(int min, int max) 
        : base(p => p.ViewCount > min && p.ViewCount < max) { }
}

配合擴展方法 And/Or/Not,兼具類型安全與靈活組合。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved