我应该如何在多个 LINQ-where 子句之间共享筛选逻辑

本文关键字:共享 之间 筛选 子句 LINQ-where 我应该 | 更新日期: 2023-09-27 18:31:25

A成为一个具有某些属性Hello的类。我想在许多地方通过此属性过滤A实例的集合。因此,我会在某个地方创建一个表示过滤谓词的类型 Expression<Func<A, bool>> 的静态成员,并在我进行过滤的所有地方使用它。(此谓词将由 ORM 转换为一些特定于数据库的具体表达式。

下一个。存在一个具有 A 类型属性的类B。我想通过与第一种情况相同的逻辑(按类 A 的属性Hello)过滤B实例的集合。

问题。实现这一点并减少代码重复的最正确方法是什么?

我的建议。添加三件事:1)具有类型为A的属性的接口IWithA,2)类WithA<T>实现此接口IWithA并提供类型T的属性,3)实现过滤逻辑的一些类型Expression<Func<IWithA, bool>>静态属性。演示代码如下。

    public static void Main() {
        var listOfAs = new List<A>().AsQueryable();
        var query0 = listOfAs
            .Select(a => new WithA<A> {
                A = a,
                Smth = a,
            })
            .Where(Filter);
        var listOfBs = new List<B>().AsQueryable();
        var query1 = listOfBs
            .Select(b => new WithA<B> {
                A = b.A,
                Smth = b,
            })
            .Where(Filter);
    }
    private class A {
        public int Hello { get; set; }
    }
    private class B {
        public A A { get; set; }
    }
    private interface IWithA {
        A A { get; set; }
    }
    private class WithA<T> : IWithA {
        public A A { get; set; }
        public T Smth { get; set; }
    }
    private static readonly Expression<Func<IWithA, bool>> Filter = a => a.A.Hello > 0;

这种方法的问题:1)必须始终Select(x => new WithA<X> { ... }),2)ORM可能不支持这一点。

关于答案。我对接受的答案(伊万·斯托耶夫)感到满意。我认为这是最好的办法。此外,查看Mihail Stancescu的建议也很有帮助(请参阅对问题的评论)。我仍然不明白用户853710的答案;可能它也很有用。

我应该如何在多个 LINQ-where 子句之间共享筛选逻辑

我会创建一个帮助程序函数,使用这样的System.Linq.Expressions将原始Expression<A, bool>转换为Expression<B, bool>

public static class ExpressionUtils
{
    public static Expression<Func<TTarget, bool>> ConvertTo<TSource, TTarget>(this Expression<Func<TSource, bool>> source, Expression<Func<TTarget, TSource>> sourceSelector)
    {
        var body = new ParameterExpressionReplacer { source = source.Parameters[0], target = sourceSelector.Body }.Visit(source.Body);
        var lambda = Expression.Lambda<Func<TTarget, bool>>(body, sourceSelector.Parameters);
        return lambda;
    }
    class ParameterExpressionReplacer : ExpressionVisitor
    {
        public ParameterExpression source;
        public Expression target;
        protected override Expression VisitParameter(ParameterExpression node)
        {
            return node == source ? target : base.VisitParameter(node);
        }
    }
}

示例用法

Expression<Func<A, bool>> filterA = item => item.Hello == 2; // The original logic
var filterB = filterA.ConvertTo((B b) => b.A);

此方法不需要对实体模型进行任何更改。当然,如果您愿意,您可以将过滤器缓存在相应类的静态属性中,但原则仍然是在一个地方编写逻辑,然后在其他地方使用 convert。

我建议你不要重新发明轮子。查看库 LinqKit谓词生成器是你需要的,它给你更多。Albahari(开发人员)拥有令人惊叹的产品和库供共享,并使用threm为您提供很多乐趣