2012-05-15 24 views
5

Ho utilizzato Entity Framework con il primo approccio POCO. Ho praticamente seguito lo schema descritto da Steve Sanderson nel suo libro 'Pro ASP.NET MVC 3 Framework', utilizzando un contenitore DI e una classe DbContext per connettersi a SQL Server.Migliorare l'efficienza con Entity Framework

Le tabelle sottostanti nel server SQL contengono set di dati molto grandi utilizzati da diverse applicazioni. A causa di questo ho dovuto creare una vista per gli enti di cui ho bisogno nella mia domanda:

class RemoteServerContext : DbContext 
{ 
    public DbSet<Customer> Customers { get; set; } 
    public DbSet<Order> Orders { get; set; } 
    public DbSet<Contact> Contacts { get; set; } 
    ... 

    protected override void OnModelCreating(DbModelBuilder modelBuilder) 
    { 
     modelBuilder.Entity<Customer>().ToTable("vw_Customers"); 
     modelBuilder.Entity<Order>().ToTable("vw_Orders"); 
     ... 
    } 
} 

e questo sembra funzionare bene per la maggior parte dei miei bisogni.

Il problema che ho è che alcuni di questi punti di vista hanno una grande quantità di dati in modo che quando chiamo qualcosa come:

var customers = _repository.Customers().Where(c => c.Location == location).Where(...); 

sembra essere riportare l'intero set di dati, che possono Prenditi un po 'di tempo prima che la query LINQ riduca il set a quelli di cui ho bisogno. Questo sembra molto inefficiente quando i criteri sono applicabili solo a pochi record e sto recuperando l'intero set di dati dal server SQL.

ho cercato di ovviare a questo utilizzando stored procedure, come ad esempio

public IEnumerable<Customer> CustomersThatMatchACriteria(string criteria1, string criteria2, ...) //or an object passed in! 
{ 
    return Database.SqlQuery<Customer>("Exec pp_GetCustomersForCriteria @crit1 = {0}, @crit2 = {1}...", criteria1, criteria2,...); 
} 

mentre questo è molto più veloce, il problema qui è che non restituisce un DbSet e così ho perso tutta la connettività tra i miei oggetti, ad es Non posso fare riferimento ad alcun oggetto associato come ordini o contatti anche se includo i loro ID perché il tipo restituito è una raccolta di "Clienti" piuttosto che un DbSet di essi.

Qualcuno ha un modo migliore per far sì che il server SQL esegua l'interrogazione in modo da non passare in giro carichi di dati inutilizzati?

risposta

4
var customers = _repository.Customers().Where(c => c.Location == location).Where(... 

Se Customers() rendimenti IQueryable, questa dichiarazione da sola non sarà effettivamente 'riportando' nulla - ti chiama Where su un IQueryable dà un altro IQueryable, ed è fino a quando non si fa qualcosa che causa l'esecuzione di query (come ToList o FirstOrDefault) che qualsiasi cosa verrà effettivamente eseguita e i risultati restituiti.

Se tuttavia questo metodo Customers restituisce una raccolta di oggetti istanziati, allora sì, poiché si richiedono tutti gli oggetti che si ottengono tutti.

Non ho mai utilizzato il pattern code-first o addirittura anche il repository, quindi non so cosa consigliarlo, oltre a rimanere nel regno di IQueryable il più a lungo possibile, e solo eseguendo la query una volta sola hai applicato tutti i filtri pertinenti.

+0

+1. Per ulteriori "approccio estensibile" è possibile scrivere una funzione che richiede un predicato e restituisce '_repository.Customers(). Dove (predicato)' o (se non è IQueryable più) scrivere una funzione separata con 'context.CreateQuery ("Clienti"). Dove (predicato) ', con possibilità di chiamare' .ToList() 'alla fine. Dovrebbe costruire un'espressione piacevole e ottimizzata. –

+1

Ciao. Sei stato azzeccato con il tuo suggerimento di rimanere nel regno di IQueryable. Stavo usando un oggetto IEnumerable che non passa la query al server ma ottiene tutti i record e quindi li filtra. Vedi l'articolo qui: http://www.fascinatedwithsoftware.com/blog/post/2011/06/27/IEnumerable-IQueryable-and-the-Entity-Framework-40.aspx Ho controllato con SQL Profiler e ha ragione, IQueryable passa i parametri come una query – GrahamJRoy

0

È necessaria la query LINQ per restituire meno dati come sql paging come la funzione top in sql o eseguire query manuali utilizzando stored procedure. In entrambi i casi, è necessario riscrivere il meccanismo di query. Questo è uno dei motivi per cui non ho usato EF, perché non hai molto controllo sul codice che sembra.

2

Cosa avrei fatto per restituire solo un insieme di dati sarebbe stata la seguente:

var customers = (from x in Repository.Customers where <boolean statement> &&/|| <boolean statement select new {variableName = x.Name , ...).Take(<integer amount for amount of records you need>); 

così per esempio:

var customers = (from x in _repository.Customers where x.ID == id select new {variableName = x.Name}).take(1000); 

quindi scorrere i risultati per ottenere i dati: (ricordate, la dichiarazione LINQ restituisce un IQueryable) ...

foreach (var data in customers) 
{ 
    string doSomething = data.variableName; //to get data from your query. 
} 

speranza che questo aiuta, non esattamente il stessi metodi, ma trovo questo utile nel mio codice

1

Probabilmente è perché il metodo Cusomters() nel tuo repository sta facendo un oggetto GetAll() e recupera prima l'intero elenco. Ciò impedisce a LINQ e SQL Server di creare query intelligenti.

Non so se c'è una buona soluzione per il repository, ma se volete fare qualcosa di simile:

using(var db = new RemoteServerContext()) 
{ 
    var custs = db.Customers.Where(...); 
} 

penso che sarà molto più veloce. Se il tuo progetto è abbastanza piccolo, puoi fare a meno di un repository. Certo, perderai un livello di astrazione, ma con piccoli progetti questo potrebbe non essere un grosso problema.

D'altra parte, è possibile caricare tutti i clienti nel repository una sola volta e utilizzare direttamente la raccolta risultante (anziché la chiamata al metodo che riempie l'elenco). Attenzione però all'aggiunta, alla rimozione e alla modifica dei clienti.