2015-11-05 13 views
9

Ciao Sto usando MVC 5 e Entity Framework 6 per il mio progetto. Ho un modello come nel seguente diagramma:Ricerca per entità correlata

diagram

E devo interrogare il prodotto soggetto a partire da un insieme di oggetti Tag. Si noti che l'oggetto Tag è una classe astratta che viene effettivamente mappata utilizzando l'ereditarietà della strategia Table-Per-Entity.

questa è la firma del mio metodo funzione

public IEnumerable<Product> SerachByTag(IEnumerable<Tag> tagList); 

Nel parametro taglist in realtà ci saranno casi concreti di Tag attuazione.

Come posso fare questa richiesta?

Ad esempio posso ricevere in ingresso la seguente struttura dati

[ 
    { tagType: 1, stringProperty: "abc" }, 
    { tagType: 2, intProperty: 9 } 
] 

e così via. Quale sarebbe il modo migliore per filtrare i prodotti? Per esempio potrei certamente prima applico un elenco di prodotti per ogni singolo criterio e quindi si intersecano questi risultati come nel seguente esempio:

var p1 = ctx.Tags 
      .OfType<FirstTagType>() 
      .Where(x => x.StringProperty.Equals("abc")) 
      .Select(x => x.Products); 
var p2 = ctx.Tags 
      .OfType<SecondTagType>() 
      .Where(x => x.IntProperty == 9) 
      .Select(x => x.Products); 
var results = p1.Intersect(p2); 

Ma la mia domanda in questo caso è sulle prestazioni. Come si comporta questa query con molti filtri?

+0

Se si sta solo eseguendo la query per il prodotto (e non si cura del tipo concreto di tag), si interroga semplicemente il tag (abstract) Tag dbset. –

+0

@AlexKrupka: Grazie per la risposta. Purtroppo ho bisogno di prendere in considerazione il tipo concreto – Lorenzo

risposta

5

Se si controlla il codice SQL generato per la tua ricerca, troverete qualcosa di simile:

SELECT 
[Intersect1].[ProductId] AS [C1], 
[Intersect1].[ProductName] AS [C2] 
FROM (SELECT 
    [Extent3].[ProductId] AS [ProductId], 
    [Extent3].[ProductName] AS [ProductName] 
    FROM [dbo].[FirstTag] AS [Extent1] 
    INNER JOIN [dbo].[Tag] AS [Extent2] ON [Extent1].[TagId] = [Extent2].[TagId] 
    LEFT OUTER JOIN [dbo].[Product] AS [Extent3] ON [Extent2].[Product_ProductId] = [Extent3].[ProductId] 
    WHERE N'aaaa-9' = [Extent1].[StringProperty] 
INTERSECT 
    SELECT 
    [Extent6].[ProductId] AS [ProductId], 
    [Extent6].[ProductName] AS [ProductName] 
    FROM [dbo].[SecondTag] AS [Extent4] 
    INNER JOIN [dbo].[Tag] AS [Extent5] ON [Extent4].[TagId] = [Extent5].[TagId] 
    LEFT OUTER JOIN [dbo].[Product] AS [Extent6] ON [Extent5].[Product_ProductId] = [Extent6].[ProductId] 
    WHERE -9 = [Extent4].[IntProperty]) AS [Intersect1] 

Qui, si può vedere che le query di selezione interni stanno facendo esattamente quello che ci si aspetta di fare. I join si basano su chiavi esterne e dovrebbero essere veloci con indici sulle colonne. Quindi se hai molti filtri, devi solo assicurarti che funzionino tutti su colonne indicizzate correttamente.

Il LINQ Intersect viene convertito in SQL INTERSECT, che funziona su tutte le colonne della tabella "prodotto". Potresti voler controllare il piano di esecuzione effettivo dalla tua parte, potrebbe dipendere da molte cose.

Da parte mia quello che vedo è che SQL Server esegue la prima query, quindi sul risultato chiama "Distinct Sort", e quindi per eseguire l'intersezione effettiva esegue un "Left Semi Join" con lo ProductId e ProductName (quindi tutte le colonne nella tabella Prodotto). Questo potrebbe non essere il migliore, perché la mia ipotesi è che non si dispone di un indice su tutte le colonne.

Un modo per ottimizzare questo è quello di fare solo l'Intersect sulla chiave primaria (che dovrebbe essere veloce), e quindi recuperare tutti i dati di prodotto in base agli ID:

var p1 = ctx.Tags 
    .OfType<FirstTag>() 
    .Where(x => x.StringProperty.Equals("aaaa-9")) 
    .Select(x => x.Product.ProductId); 
var p2 = ctx.Tags 
    .OfType<SecondTag>() 
    .Where(x => x.IntProperty == -9) 
    .Select(x => x.Product.ProductId); 

var query = ctx.Products.Where(p => p1.Intersect(p2).Contains(p.ProductId)); 

Gli usi di query SQL sottostante generati EXISTS e il piano di esecuzione di quello utilizza un inner join (su una chiave primaria).

Ma, in realtà non avviare questo processo di ottimizzazione senza prima verificare se si verifica un problema di prestazioni.

Problemi correlati