用于多个表的DRY LINQ谓词过滤器

时间:2010-07-28 20:51:35

标签: .net linq linq-to-sql predicate

假设我有两个表,TableA和TableB。 B中的每条记录在B中都有一条或多条相关记录。说我想要一个使用谓词的可重用过滤器。我可能会做这样的事情(顺便说一下Linq-to-SQL):

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

private IQueryable<ARecord> GetRecords()
{
    return DataContext.TableA.Where(FilterPredicate());
}

工作正常,但是我想要搜索TableB,但在外键上使用相同的“过滤器”。我想完成下面的查询,而不必重写FilterPredicate以了解它与TableB的关系。

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

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

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

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

提前致谢。

2 个答案:

答案 0 :(得分:3)

您可以通过在A和B上定义接口来完成此操作。

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

LINQ-To-SQL类是部分的,因此在您的部分类定义中,您可以像这样添加接口:

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

如您所见,由于在Linq-To-Sql生成的部分中实现了Name属性,因此不需要实现。

现在将谓词约束为实现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。

答案 1 :(得分:0)

我想更多地考虑它,这有点像一个愚蠢的问题。我希望能够使用更清晰的查询:

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

并将其应用于此:

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

不必在启动查询时使用重复的此谓词 A表:

x => x.Name == "Test"

所以我认为解决方案是通过启动A表来“反向”查询,如下所示:

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

query = query.Where(FilterPredicate());

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