我有一个函数我想转换成LINQ to SQL表达式,但我无法弄清楚如何。在LINQ查询中调用此函数,对结果集中的每一行调用一次。传入的productAreaId可能引用也可能不引用有效数据,因此我必须检查,然后仅在应用过滤器后存在任何行时按productAreaId过滤:
//Forgive the contrived example...
public static IQueryable<Order> GetOrders(int orderNumber, int? productAreaId,
OSDataContext db)
{
var orders = db.Orders.Where(o => o.OrderNumber == orderNumber &&
o.Group.GroupTypeId != (int)GroupTypeId.INTERNAL &&
!o.Deleted);
if (productAreaId != null)
{
var orders2 = orders.Where(o => o.ProductAreaId == productAreaId);
if (orders2.Any()) return orders2;
}
return orders;
}
我不想这样做。我需要一个函数,它返回一个没有任意代码的表达式,因此它是可组合的。仅显示上述函数,因为这是我知道如何将其封装在函数中的唯一方法。
我想做这样的事情,将人为的“ApplyFilterIfAnyResultExists”替换为实际有用的东西:
public static Expression<Func<Order,bool>>
GetOrdersExpr(int orderNumber, int? productAreaId)
{
return o => o.OrderNumber == orderNumber &&
o.Group.GroupTypeId != (int)GroupTypeId.INTERNAL &&
!o.Deleted && (productAreaId == null ||
//Making up a fictional function. Is there a real way to do this?
o.ApplyFilterIfAnyResultExists(row =>
row.ProductAreaId == productAreaId)
);
}
有没有办法在LINQ to SQL表达式中应用这种过滤器?如果没有,有什么建议吗?
谢谢!罗伊
修改: 这是我想看的主要查询:
var customerData =
from c in db.Customers
select new
{
id = c.Id,
name = c.Name,
lastOrder =
db.Orders
.Where(GetOrdersExpr(c.LastOrderNumber,
c.PreferredProductAreaId))
.FirstOrDefault(),
allOrders = c.OrderForms
.Select(form =>
db.Orders
.Where(GetOrdersExpr(form.OrderNumber,
c.PreferredProductAreaId))
.FirstOrDefault()
)
.Where(o => o != null)
//How lastOrder used to be queried
//lastOrder =
// GetOrders(c.LastOrderNumber, c.PreferredProductAreaId, db)
// .FirstOrDefault()
};
值得注意的是,Orders和Customers位于数据库服务器上的两个不同的数据库中,但它们都是在同一个DataContext中引用的。
答案 0 :(得分:1)
对于您的原始方法,这可能会更好:
public static IQueryable<Order> GetOrders(int orderNumber, int? productAreaId,
OSDataContext db)
{
var orders = db.Orders.Where(o => o.OrderNumber == orderNumber &&
o.Group.GroupTypeId != (int)GroupTypeId.INTERNAL &&
!o.Deleted);
if(productAreaId != null)
{
orders = orders.Where(
o => !orders.Any(o2 => o2.ProductAreaId == productAreaId) ||
o.ProductAreaId == productAreaId);
}
return orders;
}
这使得您只进行单个数据库往返。如果提供了产品区域ID,您将返回以下任何一个订单:
它确实使查询更复杂,所以我会稍微测试一下它是否真的能给你带来任何性能提升。
这不会很好地转换为您建议的功能,但如果您分享有关如何调用此代码的更多信息,我可能会给您一些关于如何避免调用此函数20次以上的建议
修改强>
这样的事情应该有效:
var customerData =
from c in db.Customers
let productAreaId = c.PreferredProductAreaId
let orders =
db.Orders
.Where(o => o.OrderNumber == c.LastOrderNumber &&
o.Group.GroupTypeId != (int)GroupTypeId.INTERNAL &&
!o.Deleted)
.OrderBy(o => o.Date)
let lastOrderInProductArea = productAreaId != null
? orders.FirstOrDefault(o => o.ProductAreaId == productAreaId)
: null
select new
{
id = c.Id,
name = c.Name,
lastOrder = lastOrderInProductArea != null
? lastOrderInProductArea
: orders.FirstOrDefault()
};
答案 1 :(得分:1)
也许是这样的:
var customerData =
from c in db.Customers
let orders = db.Orders.Where(o => o.OrderNumber == c.orderNumber &&
o.Group.GroupTypeId != (int)GroupTypeId.INTERNAL &&
!o.Deleted)
let orders2 = orders.Where(o => o.ProductAreaId == c.productAreaId)
select new
{
id = c.Id,
name = c.Name,
lastOrder = c.productAreaId != null && orders2.Any() ?
orders2.FirstOrDefault() :
orders.FirstOrDefault()
};
答案 2 :(得分:0)
public static IQueryable<Order> GetOrders(int orderNumber, int? productAreaId, OSDataContext db)
{
var orders = db.Orders.Where(o => o.OrderNumber == orderNumber &&
o.Group.GroupTypeId != (int)GroupTypeId.INTERNAL &&
!o.Deleted);
if (productAreaId != null)
{
var orders2 = orders.Where(o => o.ProductAreaId == productAreaId);
return orders2.Select(x => new {x, Type = 2 }).Concat(orders.Select(x => new {x, Type = 1 })).OrderBy(x => x.Type);
}
return orders;
}
这将返回两个连接的结果。首先来自orders2的结果然后来自orders2。这可能会对你有所帮助。
如果您真的只想要一组的订单,那么
GetOrders().Where(x => x.Type == GetOrders().Max(x => x.Type))
以便将查询限制为最高优先级的订单。这将是次优的表现。