我一直在将一个长期的,粗糙的sproc转换成EF,以使其进入一个更加可测试和可维护的位置。大部分都进展顺利,除了这部分:
select p.idProduct from products p
where
// {some random filtering bringing us down to a manageable number of rows}
AND p.idProduct NOT IN (SELECT idProduct from productsShipped)
我已将其转换为:
var results = dbc.products.Where(p =>
p.warehouse == warehouse
&& p.BarConversion.Bar.BarDate > minDate
&& !dbc.productsShipped.Any(ps => ps.idInventory == p.idInventory)
//&& p.productsShipped == null
&& p.OPR.Order.Payment != null
&& !(p.OPR.Order.PaymentType == 5 &&
(p.OPR.Order.Payment.paymentStatus == null ||
p.OPR.Order.Payment.paymentStatus != "accepted"))
&& p.OPR.Order.OrderSla.expectedShipDate <= dueDateCutoff);
我遇到的问题是productsShipped表绝对是巨大的。在原始SQL中,where子句必须理解它不需要提取整个productsShipped表,而只需获取与先前查询相关的产品。 EF等价物将其分解为子查询并询问productsShipped表中的每个条目,导致查询花费超过五分钟,而不是在没有此过滤器的情况下运行几秒钟。我尝试在两个实体之间添加关系,结果相似。
有没有办法可以强制Entity进行正确的左外部独占连接而不是子查询,或类似地提高性能,或者我被迫要么将性能提升或将部分逻辑推入难以实现的状态测试sproc?
答案 0 :(得分:0)
以下是左反半连接的扩展方法(来自MSDN):
public static IQueryable<TLeft> LeftAntiSemiJoin<TLeft, TRight>(this IQueryable<TLeft> left, IQueryable<TRight> right, Expression<Func<TLeft, TRight, bool>> predicate) {
var leftPrm = predicate.Parameters[0];
var rightPrm = predicate.Parameters[1];
// retrieve methods
var anyMethod = ((Func<IQueryable<TRight>, bool>)Queryable.Any).Method;
var whereMethod = ((Func<IQueryable<TRight>, Expression<Func<TRight, bool>>, IQueryable<TRight>>)Queryable.Where).Method;
// l => !right.Where(r => predicate(l, r)).Any()
var leftPredicate = Expression.Lambda<Func<TLeft, bool>>(
Expression.Not(
Expression.Call(anyMethod,
Expression.Call(whereMethod,
Expression.Constant(right),
Expression.Lambda<Func<TRight, bool>>(predicate.Body, rightPrm)))),
leftPrm);
return left.Where(leftPredicate);
}
您可以这样使用:
var results2 = dbc.products.LeftAntiSemiJoin(dbc.productsShipped, (p, ps) => p.idInventory == ps.idInventory)
.Where(p =>
p.warehouse == warehouse &&
p.BarConversion.Bar.BarDate > minDate &&
p.OPR.Order.Payment != null &&
!(p.OPR.Order.PaymentType == 5 &&
(p.OPR.Order.Payment.paymentStatus == null ||
p.OPR.Order.Payment.paymentStatus != "accepted")) &&
p.OPR.Order.OrderSla.expectedShipDate <= dueDateCutoff);
也许会更快?
答案 1 :(得分:0)
虽然我最终无法让Entity以我想要的方式生成SQL,但我发现我能够运行单独的查询以从productsShipped获取必要的数据,将其放入字典中,然后从那里进行查找