我正在编写LINQ 2 SQL扩展方法的集合。现在,我有一堆重复的方法,一个在IEnumerable集合上工作,另一个在IQueryable集合上。例如
public static IQueryable<X> LimitVisible(this IQueryable<X> items) {
return items.Where(x => !x.Hidden && !x.Parent.Hidden);
}
public static IEnumerable<X> LimitVisible(this IEnumerable<X> items) {
return items.Where(x => !x.Hidden && !x.Parent.Hidden);
}
我认为我需要这种重复,因为我希望能够在某些情况下保留IQueryable行为,以便将执行推迟到数据库。 这是一个正确的假设还是单个IEnumerable方法(加上将输出转换为IQueryable)有效?如果我确实需要两者,我不喜欢重复,所以我想将两者结合使用通用方法
public static T LimitVisible<T>(this T items) where T : IEnumerable<X> {
return (T)items.Where(x => !x.Hidden && !x.Parent.Hidden);
}
除了我有其他类型而不是X(比如说Y和Z)之外,我还想编写LimitVisible函数。但我不能写
public static T LimitVisible<T>(this T items) where T : IEnumerable<Y> {
return (T)items.Where(y => !y.Hidden && !y.Parent.Hidden);
}
因为您可以基于泛型过载。我可以将这些方法放在不同的类中,但这似乎不是正确的解决方案。
有什么建议吗?也许我做出了错误的假设,并且首先不需要IQueryable特定版本。
修改:另一个选项
这是我过去用来避免重复的另一种模式
private static readonly Expression<Func<X, bool>> F = x => !x.Hidden && !x.Parent.Hidden;
public static IEnumerable<X> LimitVisible(this IEnumerable<X> e) {
return e.Select(W.Compile());
}
public static IQueryable<X> LimitVisible(this IQueryable<X> q) {
return q.Select(W);
}
这有什么危险吗?
答案 0 :(得分:2)
真的有两种方法没有好办法。 IQueryable
Where
将Expression<Func<T,bool>>
作为参数,而IEnumerable
将Func<T,bool>
作为参数。由于lambdas的神奇之处,代码看起来对于调用者来说是相同的,但编译器实际上使用两个不同的代码来完成不同的事情。
答案 1 :(得分:1)
答案取决于您的用法:如果您不介意将数据集放入内存以执行查询,那么只保留IEnumerable<T>
覆盖就可以了。
但是,这个决定的必然结果是,在您使用LimitVisible<T>
时,您的查询将被强制进入内存。这可能是你想要的,也可能不是,你可能会强迫你编写你宁愿避免的模式。例如,
var firstHundred = ctx
.SalesOrders
.LimitVisible() // Happens in memory
.Where(ord => ord.UserId == myUser) // Also happens in memory
.Take(100);
单个IEnumerable<T>
比使用IQueryable<T>
覆盖可用性会更糟糕,因为所有销售订单将在确定之前逐个检索到内存中他们的可见性,而不是检查服务器端的条件。如果按用户ID过滤可以消除服务器端的大部分销售订单,则此等效查询的性能差异可能会更好几个数量级:
var firstHundred = ctx
.SalesOrders
.Where(ord => ord.UserId == myUser) // Happens in RDBMS
.LimitVisible() // Happens in memory
.Take(100);
如果您打算仅自己使用您的代码,并且您不介意在没有明显原因导致性能不佳的情况下注意,您可以保持一次超载。如果您打算让其他人使用您的图书馆,我强烈建议保留两个替代。
编辑:要加快替代实施,请预先编译您的表达式,如下所示:
private static readonly Expression<Func<X, bool>> F = x => !x.Hidden && !x.Parent.Hidden;
private static readonly Predicate<X> CompiledF = (Predicate<X>)F.Compile();
public static IEnumerable<X> LimitVisible(this IEnumerable<X> e) {
return e.Select(CompiledF);
}
public static IQueryable<X> LimitVisible(this IQueryable<X> q) {
return q.Select(F);
}