Ho utilizzato LINQ to SQL & per entità per un po 'e sono complessivamente molto soddisfatto di loro. Tuttavia, conosco i loro limiti e uno in particolare sta diventando un grosso problema per me. Quando si esegue una query complessa nidificato sotto forma diConsiglia un provider LINQ adatto (server SQL, query complesse)
MyContext.SomeTable
.Select(item=>new{
item.SomeProperty1,
item.SomeProperty2,
item.NavigationProperty1
.Select(nav1=> new {// retrieve some properties}), // This triggers a single query as long as don't have more than one subquery
item.NavigationProperty2
.Select(nav2=> new {// retrieve some properties}) // This triggers one query PER ROW in the original query
});
I fornitori Ho testato sono LINQ to SQL/LINQ to Entities (e, peggio ancora, Devart LinqConnect che le tariffe peggio e genera 1 per riga sulla prima proprietà di navigazione)
quello che ottengo ora che è generato (pseudocodice):
select t1.a,t1.b,t2.c,t2.d from mytable as t1
join navproperty1table as t2
e 1 milioni (se non v'è di 1 milione risultati nel primo set) di query come questo: .210 (Xchanging su richiesta X all'elemento successivo restituito dalla prima query)
quello che voglio:
select t1.a,t1.b,t2.c,t2.d,t3.e,t3.f from mytable as t1
join navproperty1table as t2
join navproperty2table as t3
Ora, naturalmente, se ci fossero 3 righe della tabella originale non sarebbe un problema, ma ho 10 o migliaia di milioni di righe nelle mie tabelle "e" ho bisogno di una query molto più complessa in una singola selezione (voglio ottenere un grafico complesso in una volta). Pensa a 20 + tavoli con 3-6 livelli di nidificazione accedendo ad altri 2-5 tavoli ciascuno.
Il mio server SQL può perfettamente far fronte, non mi interessa neanche la larghezza di banda, è su un'istanza collegata da una connessione gigabit, non riesco a ottenere quei dati in modo differito, in realtà "uso" tutti immediatamente, quindi non è solo pigrizia. In questo momento per motivi di prestazioni ho dovuto dividere la query in molte piccole query e unirle manualmente sul LINQ alla dimensione dell'oggetto, che fornisce un codice veramente sgradevole per chiunque lo mantenga, ma era l'unica soluzione effettiva che avevo, quindi includendo tutto il piccole query e l'unione finale, sono a oltre 600 righe di codice non ordinate in un unico metodo che è totalmente non-mantenibile.
Esistono attualmente "qualsiasi" produzione di provider LINQ pronte prima che io vada a valutare tutti quelli che funzionano in questo modo o sono meglio codificare e commercializzare il mio? (Sono molto sorpreso che in realtà non funzionino tutti in questo modo, non riesco a vedere una singola istanza dove starai meglio con il caso foreach e quelli che ho provato a dichiarare di sbarazzarsi di n +1 con loadwith, non liberartene dato che continuano a fare n + 1 query, ma lo fanno in una sola chiamata, 1 andata e ritorno & n + 1 query non è soddisfacente quando 1 è 10 000 e 10 000 000 e poi 10 000 000 000)
- (nota che sto speculando su ciò che fa scattare esattamente questo, ma non è il problema, non importa quello che fa scattare questa "esattamente" sono sicuro di colpire in il mio contesto attuale)
PS: si noti che sto eseguendo .NET 4.0 full profi su un server Windows 2008 o versioni successive e su server SQL 2008 o versioni successive, un provider che non supporta nient'altro andrebbe bene, ho zero requisiti per migrazione, portabilità, versioni .net inferiori, supporto server SQL inferiore ecc. a versioni ancora più recenti è un'opzione, se necessario.Inoltre non ho prerequisiti per la modellazione o le funzionalità avanzate, il DB è già lì, voglio solo interrogare le tabelle, quindi qualcosa che non supporta la modellazione/viste/DML/stored procedure/funzioni va bene, il mio unico requisito è la generazione SQL sensibile sulle query complesse e grafici di oggetti
EDIT: per chiarimenti ecco un esempio reale del problema su un DB tutti possono ottenere, AdventureWorks
Interrogazione dipendenti per ogni contatto
Contacts
.Select(cont=>new
{
cont.EmailAddress,
cont.EmailPromotion,
Employees = cont.Employees
.Select(emp=>new
{
emp.Gender,
emp.HireDate
}).ToList()
}).ToList()
Genera
SELECT [t0].[EmailAddress], [t0].[EmailPromotion], [t1].[Gender], [t1].[HireDate], (
SELECT COUNT(*)
FROM [HumanResources].[Employee] AS [t2]
WHERE [t2].[ContactID] = [t0].[ContactID]
) AS [value]
FROM [Person]. [Contattare] AS [T0] LEFT OUTER JOIN [HumanResources]. [Employee] AS [t1] ON [t1]. [ContactID] = [t0]. [ContactID] ORDINE BY [t0]. [ContactID], [t1]. [EmployeeID]
Ora l'interrogazione solo fornitori per ogni contatto Contatti .Selezionare (cont => nuova { cont.EmailAddress, cont.EmailPromotion, Fornitori = cont.VendorContacts.Select (vend => new { vend.ContactTypeID, vend.ModifiedDate..}) ToList() }) ToList()
ancora ok:.
SELECT [t0].[EmailAddress], [t0].[EmailPromotion], [t1].[ContactTypeID], [t1].[ModifiedDate], (
SELECT COUNT(*)
FROM [Purchasing].[VendorContact] AS [t2]
WHERE [t2].[ContactID] = [t0].[ContactID]
) AS [value]
FROM [persona] [Contattare] AS [T0] LEFT OUTER JOIN [Acquisti] [VendorContact. ] AS [t1] ON [t1]. [ContactID] = [t0]. [ContactID] ORDER BY [T0]. [ContactID], [t1]. [VendorID]
Ora interrogazione entrambi contemporaneamente (trigger X row query)
Contacts
.Select(cont=>new
{
cont.EmailAddress,
cont.EmailPromotion,
Employees = cont.Employees
.Select(emp=>new
{
emp.Gender,
emp.HireDate
}).ToList(),
Vendors = cont.VendorContacts.Select(vend=>new
{
vend.ContactTypeID,
vend.ModifiedDate
}).ToList()
}).ToList()
Genera il brutto e lento (non incollare tutto per ovvie ragioni, ma si ottiene il punto):
SELECT [t0].[EmailAddress], [t0].[EmailPromotion], [t1].[Gender], [t1].[HireDate], (
SELECT COUNT(*)
FROM [HumanResources].[Employee] AS [t2]
WHERE [t2].[ContactID] = [t0].[ContactID]
) AS [value], [t0].[ContactID]
FROM [Person].[Contact] AS [t0]
LEFT OUTER JOIN [HumanResources].[Employee] AS [t1] ON [t1].[ContactID] = [t0].[ContactID]
ORDER BY [t0].[ContactID], [t1].[EmployeeID]
GO
-- Region Parameters
DECLARE @x1 Int = 1
-- EndRegion
SELECT [t0].[ContactTypeID], [t0].[ModifiedDate]
FROM [Purchasing].[VendorContact] AS [t0]
WHERE [t0].[ContactID] = @x1
GO
-- Region Parameters
DECLARE @x1 Int = 2
-- EndRegion
SELECT [t0].[ContactTypeID], [t0].[ModifiedDate]
FROM [Purchasing].[VendorContact] AS [t0]
WHERE [t0].[ContactID] = @x1
GO
-- Region Parameters
DECLARE @x1 Int = 3
-- EndRegion
SELECT [t0].[ContactTypeID], [t0].[ModifiedDate]
FROM [Purchasing].[VendorContact] AS [t0]
WHERE [t0].[ContactID] = @x1
GO
-- Region Parameters
DECLARE @x1 Int = 4
-- EndRegion
SELECT [t0].[ContactTypeID], [t0].[ModifiedDate]
FROM [Purchasing].[VendorContact] AS [t0]
WHERE [t0].[ContactID] = @x1
GO
-- Region Parameters
DECLARE @x1 Int = 5
-- EndRegion
SELECT [t0].[ContactTypeID], [t0].[ModifiedDate]
FROM [Purchasing].[VendorContact] AS [t0]
WHERE [t0].[ContactID] = @x1
GO
-- Region Parameters
DECLARE @x1 Int = 6
-- EndRegion
SELECT [t0].[ContactTypeID], [t0].[ModifiedDate]
FROM [Purchasing].[VendorContact] AS [t0]
WHERE [t0].[ContactID] = @x1
GO
-- Region Parameters
DECLARE @x1 Int = 7
-- EndRegion
SELECT [t0].[ContactTypeID], [t0].[ModifiedDate]
FROM [Purchasing].[VendorContact] AS [t0]
WHERE [t0].[ContactID] = @x1
GO
-- Region Parameters
DECLARE @x1 Int = 8
-- EndRegion
SELECT [t0].[ContactTypeID], [t0].[ModifiedDate]
FROM [Purchasing].[VendorContact] AS [t0]
WHERE [t0].[ContactID] = @x1
GO
-- Region Parameters
DECLARE @x1 Int = 9
-- EndRegion
SELECT [t0].[ContactTypeID], [t0].[ModifiedDate]
FROM [Purchasing].[VendorContact] AS [t0]
WHERE [t0].[ContactID] = @x1
GO
-- Region Parameters
DECLARE @x1 Int = 10
-- EndRegion
SELECT [t0].[ContactTypeID], [t0].[ModifiedDate]
FROM [Purchasing].[VendorContact] AS [t0]
WHERE [t0].[ContactID] = @x1
GO
quello che mi aspetto/vorrebbero vedere generato:
SELECT [t0].[EmailAddress], [t0].[EmailPromotion], [t1].[Gender], [t1].[HireDate], [t2].[ContactTypeID], [t2].[ModifiedDate] ,[t0].[ContactID]
FROM [Person].[Contact] AS [t0]
LEFT OUTER JOIN [HumanResources].[Employee] AS [t1] ON [t1].[ContactID] = [t0].[ContactID]
LEFT OUTER JOIN [Purchasing].[VendorContact] AS [t2] ON [t2].[ContactID] = [t0].[ContactID]
GO
In realtà attiva una query per riga oppure genera una sottoquery correlata che SQL Server potrebbe ottimizzare per un piano di esecuzione univoco? –
Genera una sottoquery (che va bene) quando c'è al massimo 1 proprietà nav, dopo di che genera una query effettiva "per" riga, che è folle, come in select bla da t1 dove id = qualche numero effettivo non una condizione , questa riga si è ripetuta 1 milione di volte –
Immagino che tu stia cercando una soluzione che produca una query con 1 join per il primo 'Select()' su una proprietà di navigazione, seguita da 1 query per successiva 'Select()' (con una clausola WHERE sull'ID genitore)? Immagino che tu non voglia una sola query con un prodotto cartesiano inevitabile. –