为 EntitySet<T>、IQueryable<T> 和 IEnumerable<T> 创建可重用的谓词

12
在我的LINQ to SQL设置中,我有多个表映射到类,这些类基本上支持相同的接口以支持版本控制,即:
public interface IValid
{
    int? validTo { get; }
    int validFrom { get; }
}

LINQ to SQL 类从这个接口派生,如下所示:

public partial class representationRevision : IValid
{
}

现在我想定义一种DRY(不要重复自己)的过滤EntitySet<T>IEnumerable<T>IQueryable<T>的方法,以便最终列表适用于特定版本。我尝试过以下方法:
public static class ExtensionMethods
{

    public static IQueryable<T> ValidFor<T>(this IQueryable<T> v, int? revision)
        where T : IValid
    {
        return v.Where(cr => ((cr.validFrom <= revision) &&
            ((cr.validTo == null) || (cr.validTo > revision)))
            || ((revision == null) && (cr.validTo == null))
            );
    }
}

但是这会给 EntitySet<T> 带来问题。我为 EntitySet 添加了一个特殊的实现,它首先调用 AsQueryable(),但这会抛出一个异常。尽管如此,我还是试图创建一个谓词,以便可以使用 Where(predicate) 方法:

    public static Expression<Func<contentRevision, bool>> IsValidFor(int? revision)
    {
        return ((cr) => ((cr.validFrom <= revision) &&
            ((cr.validTo == null) || (cr.validTo > revision)))
            || ((revision == null) && (cr.validTo == null)));
    }

当与 .Where<contentRevision>(IsValidFor(revision)) 一起使用时,会出现以下错误:

Error 5 'System.Data.Linq.EntitySet' does not contain a definition for 'Where' and the best extension method overload
'System.Linq.Enumerable.Where (System.Collections.Generic.IEnumerable, System.Func)' has some invalid arguments

请注意,即使没有使用 IValid 接口,也是如此... 我已经尝试了各种变化(比如添加 int 参数),但它们似乎都失败了。有什么指针可以让我朝着正确的方向前进呢?

在您上面的静态表达式函数中,第一个类型参数是contentRevision,这是打字错误吗?我希望它应该是IValid。此外,您在底部显示的错误似乎是由于传递了具有3个类型参数的谓词所导致的。如果按照所示(Expression<Func<IValid, bool>>)传递谓词会发生什么情况?我经常使用它,它可以正常工作,我无法从您的帖子中弄清楚为什么您的代码无法编译。 - Jon M
它在代码下面的注释中,“请注意,这甚至没有使用IValid接口”。基本上,我首先尝试在不使用IValid的情况下使其工作,但目前行为没有任何区别。我认为这与EntitySet<T>有关:如果我重写我的代码以执行表连接,则一切似乎都可以正常工作。 - Matthijs P
1个回答

6

关于EntitySet<T>我不确定,所以将重点放在IQueryable<T>IEnumerable<T>上。

为了让LINQ提供程序评估IQueryable<T>用于其表达式参数的表达式树,需要确保保留底层接口。否则,一切都可以使用IEnumerable<T>(即如果您愿意将整个数据集拉入本地内存,然后在那里处理它,而不是在数据库中,只需使用LINQ to Objects)。

另一个选项是AsQueryable扩展方法(Queryable类的一部分),它将IEnumerable<T>转换为IQueryable<T>,然后您可以共享其余代码。

IQueryable<T> SomeSharedQuery(this IQueryable<T> source) {
    return source.(LINQ query operators...);
}
IQueryable<T> SomeSharedQuery(this IEnumerable<T> source) {
    return source.AsQueryable().SomeSharedQuery();
}

所以你有共享的代码,其中包含一个适配器方法。


1
这个方法非常好用,但是对于EntitySet<T>却不行。AsQueryable()方法没有SQL翻译(很奇怪)。 - Matthijs P
@Matthijs:根据文档,EntitySet<T> 实现了 IEnumerable<T>,所以你会得到一个匹配。鉴于 EntitySet<T> 是关于现有查询的部分惰性加载,你不会从它的 IQueryable<T> 实现中获得任何好处……如果你想让数据库跨关联执行工作,请在原始查询中包含它。 - Richard

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接