代码之家  ›  专栏  ›  技术社区  ›  Ocelot20

多个表的干式LINQ谓词筛选器

  •  2
  • Ocelot20  · 技术社区  · 14 年前

    假设我有两张桌子,A桌和B桌。A中的每个记录在B中都有一个或多个相关记录。假设我想要使用谓词的可重用过滤器。我可能会这样做(顺便说一下,linq to sql):

    private Expression<Func<ARecord, bool>> FilterPredicate()
    {
        return x => x.Name == "Test";
    }
    
    private IQueryable<ARecord> GetRecords()
    {
        return DataContext.TableA.Where(FilterPredicate());
    }
    

    这很好,但是我想搜索表B,但是在外键上使用相同的“过滤器”。我想完成下面的查询,而不必为它与表B的关系重写filterPredicate。

    var query = from b in DataContext.B
                where b.A.Name == "Test"
                select b;
    

    我只是想知道是否有任何创建可重用的“where”子句的最佳实践可以帮助跨多个表。

    编辑-为了澄清,我不想寻找一种方法将谓词应用于记录类型和布莱考德类型。我正在寻找一种方法(任何方法,不一定是按照我已经想到的方法)来防止需要这个谓词:

    private Expression<Func<BRecord, bool>> FilterPredicate2()
    {
        return x => x.A.Name == "Test";
    }
    

    事先谢谢。

    2 回复  |  直到 11 年前
        1
  •  3
  •   jeroenh    14 年前

    您可以通过在A和B上定义接口来实现这一点。

    public interface IHasName // contrived, I know
    {
        string Name {get;}
    }
    

    linq-to-sql类是部分类,因此在部分类定义中,可以像这样添加接口:

    public partial class A : IHasName {}
    public partial class B : IHasName {}
    

    如您所见,不需要实现,因为name属性是在linq to sql生成的部分中实现的。

    现在,将谓词约束到实现iHasName接口的类型,您就可以设置:

    private Expression<Func<T, bool>> FilterPredicate(string name) where T : IHasName
    {
        return x => x.Name == name;
    }
    

    现在您甚至可以像这样定义iQueryable上的扩展方法:

    public static T GetByName<T>(this IQueryable<T> queryable, 
                                 string name) where T : IHasName
    {
        return queryable.Where(FilterPredicate(name)).SingleOrDefault();
    }
    

    小警告:当然,接口(“name”)中的属性必须与实现类中的属性名完全匹配。假设您有一个具有属性“myname”的类C。您可能会尝试像这样实现iHasName接口:

    public partial class C : IHasName
    {
        public string Name {return MyName;} 
    } 
    

    这当然不起作用,因为Linq to SQL表达式分析器将使用“name”而不是实际属性“myname”,因此它将无法将此表达式映射到有效的SQL。

        2
  •  0
  •   Ocelot20    14 年前

    我想多想想,这是个有点愚蠢的问题。我希望能够使用更清晰的查询:

    var query = from b in DataContext.B 
                select b;
    

    并将其应用于:

    x => x.A.Name == "Test"
    

    不必重复这个谓词,我在开始查询时使用它 A表:

    x => x.Name == "Test"
    

    因此,我认为解决方案是从表开始“反向”查询,如下所示:

    var query = from a in DataContext.A
                join b in B on a equals b.A
                select b;
    
    query = query.Where(FilterPredicate());
    

    我想它可能会无效地重写查询,但情况似乎并非如此。