我有一個函數我想轉換成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中引用的。
也許是這樣的:
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()
};
對於您的原始方法,這可能會更好:
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()
};