Ho una funzione che mi piacerebbe trasformare in un'espressione LINQ to SQL, ma non riesco a capire come. Questa funzione viene chiamata da una query LINQ, una volta per ogni riga nel set di risultati. Il productAreaId che viene passato può o non può fare riferimento a dati validi, quindi devo controllare, e quindi filtrare solo per productAreaId se esistono delle righe dopo aver applicato il filtro:
//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;
}
Non voglio farlo in questo modo. Ho bisogno di una funzione che restituisca un'espressione senza codice arbitrario, quindi sarà componibile. La funzione precedente viene visualizzata solo perché è l'unico modo in cui so come incapsulare questo in una funzione.
Mi piacerebbe fare qualcosa di simile, con l'inventato "ApplyFilterIfAnyResultExists" sostituito con qualcosa che funziona davvero:
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)
);
}
C'è un modo per applicare quel tipo di filtro all'interno di un'espressione LINQ a SQL? Se no, qualche suggerimento?
Grazie!
Roy
EDIT: Questa è la query principale come vorrei che guardasse:
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()
};
Vale anche la pena notare che gli ordini e i clienti si trovano in due diversi database sul server del database, ma sono entrambi referenziati dallo stesso DataContext qui.
Forse qualcosa del genere:
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()
};
Per il tuo metodo originale, questo potrebbe funzionare meglio:
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;
}
Questo rende così stai facendo solo un singolo roundtrip del database. Se viene fornito l'ID dell'area del prodotto, si restituiranno gli ordini nei seguenti casi:
Rende la query più complessa, quindi proverei un po 'per vedere se ti dà davvero dei guadagni in termini di prestazioni.
Questo non si tradurrà molto bene nella funzione che stai suggerendo, ma se condividi più informazioni su come viene chiamato questo codice, potrei probabilmente darti alcuni consigli su come evitare di chiamare questa funzione più di 20 volte.
modificare
Qualcosa di simile dovrebbe funzionare:
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()
};