giovedì 18 settembre 2014

Tabelle Dividere in Entity Framework 6 per migliorare le prestazioni

ICi sono alcune occasioni quando si utilizza Entity Framework può davvero farti del male: Quando si dispone di tabelle con centinaia di colonne o tabelle con grandi carichi utili. Ecco come ottenere EF6 di fare la cosa giusta.

By Peter Vogel2014/09/17

La prima regola di accelerare l'applicazione è "Gestione accesso al database." Purtroppo, utilizzando Entity Framework (EF) significa rinunciare a qualche controllo intorno gestire l'accesso - in particolare, nel generare SQL. Come lettore ha sottolineato in un recente articolo , prendendo tutte le impostazioni predefinite di EF può risultare in un'applicazione che viene eseguita molto lentamente, anzi.

La questione sollevata è il lettore che, per impostazione predefinita, Entity Framework genera le istruzioni SQL per recuperare tutte le colonne che corrispondono alle proprietà sulle vostre classi di entità. Spesso, che saranno più dati del necessario. Ci stanno per essere momenti in cui, per esempio, invece si è dati da prima e l'ultima colonna di un cliente, ma l'entità che si sta utilizzando ha molte più proprietà che solo quei due colonne. Entity Framework, non essendo in grado di leggere la tua mente, recupererà le colonne della tabella che corrispondono a ogni proprietà sulla vostra entità, non solo i nomi e cognomi.

Mentre, sulla faccia di esso, che sembra inefficiente, il risultato di solito cade in un "nessun danno, nessun fallo" zona: Probabilmente non può misurare la differenza tra recupero solo le due colonne che si desidera e recuperare tutte le colonne.

Quando si potrebbe avere un problema
, ma per fare che "nessun danno, nessun fallo" reclamo, devo fare tre ipotesi. In primo luogo, sto supponendo che si sta costruendo un'applicazione online transazionale dove non recuperare molte righe alla volta (tipicamente, meno di una decina di righe) - cioè, "valore di transazione" una di righe. Poiché il numero di righe aumenta, alla fine la differenza tra ottenere due colonne e ottenere "tutte le colonne" recuperate diventerà abbastanza grande per fare la differenza. Tuttavia, la mia ipotesi sta per essere vero in quasi tutti i casi: Entity Framework è rivolto verso applicazioni online transazionali - non dovrebbe essere utilizzato per l'elaborazione in batch o applicazioni dove stai di elaborazione di centinaia o migliaia di righe alla volta di reporting .



Le mie altre due ipotesi sono ragionevoli, ma, a differenza mia prima ipotesi, non è sempre vero. Il secondo presupposto è che "tutte le colonne" è un numero piccolo - un paio di dozzine di colonne, per esempio. Se si dispone di una tabella con centinaia di colonne, allora è certamente possibile che si noterà la differenza tra il recupero di alcune colonne e il recupero "tutte le colonne," anche se si desidera recuperare solo un "valore di transazione" di righe.

La mia terza ipotesi è che la tabella non include alcun oggetto di grandi dimensioni (BLOB) colonne binarie (in Microsoft SQL Server, queste sono le colonne con i tipi varbinary, di immagine e di dati xml). Se si dispone di una colonna blob definito nella classe entità e stanno recuperando quella colonna quando non si desidera che allora si potrebbe facilmente avere un problema di prestazioni (anche se molto dipenderà anche da quanto è grande l'oggetto memorizzato nella colonna blob è) . E 'anche possibile che si può avere un problema se si dispone di grandi colonne di caratteri abbastanza oggetti (clob: testo, ntext, nvarchar (max), testo, varchar (max)).

In questi scenari, tuttavia, la correzione al vostro problema è sufficientemente semplice e probabilmente sufficientemente localizzate che non vorrei applicarla. Invece, mi piacerebbe costruire l'applicazione con un oggetto entità semplice che recupera tutte le colonne e vedere se il rendimento è accettabile. Solo se non stai ricevendo le prestazioni che si desidera si deve applicare la seguente correzione alle parti interessate della vostra applicazione.

Risolvere il problema
in modo efficace, si desidera implementare due modelli di recupero dei dati quando ottenere i dati dalle colonne che compongono le tabelle problema. Per la maggior parte delle proprietà sul vostro oggetto entità, si desidera che il valore predefinito Entity Framework: eager loading. Con eager loading, i dati della colonna vengono recuperati quando si elabora l'oggetto entità.

Per le colonne BLOB (o per le centinaia di colonne non è necessario) che si desidera un approccio diverso: lazy loading. Con lazy loading, i dati vengono recuperati dalla colonna quando si leggono le proprietà corrispondenti, non quando si tocca l'entità. Purtroppo, Entity Framework non dispone di un attributo LazyLoading che è possibile applicare alle proprietà di rinviare il recupero dei loro dati.

Tuttavia, Entity Framework supporta il caricamento lazy tra le entità di business che sono collegati attraverso una associazione. Sfruttando questa caratteristica è possibile ottenere il risultato desiderato, dividendo il vostro tavolo tra due o più entità unite da una associazione.

Con il vostro tavolo diviso in due (o più) entità collegate è possibile recuperare le proprietà desiderate ardentemente e pigramente rinviare recuperare le proprietà che non si desidera fino a quando ne avete bisogno. Avrete bisogno di almeno due soggetti: uno con le colonne che si desidera recuperare avidamente; e uno sull'altro lato di una associazione, con le proprietà Potrai recuperare pigramente. Per le "centinaia di colonne" problema, è necessario guardare la vostra applicazione per decidere quali colonne si utilizza insieme e gruppo di proprietà tuoi entità 'a sostegno di tale uso.

Per questo esempio, ho intenzione di prendere il più facile dei due problemi e guardare una tabella che ha una colonna blob. Ho modificato il database di Microsoft AdventureWorks quindi la tabella Customers contiene una colonna chiamata Picture immagine di tipo (presumibilmente questo vale una foto del cliente). In questa colonna, ho intenzione di implementare questa soluzione Codice First utilizzando EF6. (Tornerò a questo problema a fine mese di risolverlo utilizzando il progettista EF.)

Un codice prima soluzione
Nel codice prima soluzione scrivo le mie due classi di entità Cliente e segregare mia proprietà Picture (che voglio caricare pigramente) dalle proprietà che voglio caricare con entusiasmo. Entrambe le classi devono avere proprietà ID del cliente, che identifica un cliente. Le due classi hanno questo aspetto, inizialmente:

Public Class CustomerEager
  <Key>
  ID proprietà pubblica As Integer
  Public Property Cognome As String
  ... Resto di proprietà ...
End Class

Public Class CustomerLazy
  <Key>
  ID proprietà pubblica As Integer
  Proprietà pubblica Picture As Byte ()
End Class
Il passo successivo è quello di estendere la classe CustomerEager (la classe con le proprietà che voglio maggior parte del tempo), con una proprietà di navigazione che collega all'entità voglio caricare "pigramente". In questo esempio, ho chiamato i CustomerBLOBs proprietà e il tipo di dati restituisce è la mia classe CustomerLazy:

Public Class CustomerEager
  <Key>
  ID proprietà pubblica As Integer
  Public Property Cognome As String
  ... Resto di proprietà ...

  Public Overridable Property CustomerBLOBS Come CustomerLazy
End Class
Nel mio oggetto DbContext, ho intenzione di assumere io accedo solo le mie entità CustomerLazy attraverso la proprietà di navigazione sul mio entità CustomerEager. Come risultato, ho solo bisogno di impostare una proprietà per la mia collezione CustomersEager entità nel mio oggetto DbContext:

Public Class AdventureWorksLTEntitiesRevised
  DbContext Inherits

Public Property CustomersEager () Come DbSet (Of CustomerEager)
Il vero lavoro è nello stabilire il rapporto tra le due entità e la tabella che rappresentano. Lo faccio nel mio metodo DbContext OnModelCreating. In primo luogo, dico Entity Framework per generare CustomerLazy e CustomerEager oggetti entità della stessa tabella: Clienti. Questo codice è simile al seguente:

Protected Sub OnModelCreating override (ModelBuilder Come DbModelBuilder)
  MyBase.OnModelCreating (ModelBuilder)

  modelBuilder.Entity (Of CustomerLazy) (). ToTable ("Clienti")
  modelBuilder.Entity (Of CustomerEager) (). ToTable ("Clienti")
Ma, nello stesso metodo, ho anche bisogno di stabilire che l'associazione nell'entità CustomerEager fatta tramite la proprietà CustomerBLOBs ha due caratteristiche. In primo luogo, si tratta di una relazione uno-a-uno con l'oggetto all'altra estremità del rapporto. In secondo luogo, l'oggetto all'estremità dell'altro rapporto è sempre presente. Questo è ciò che fa questo codice:

  modelBuilder.Entity (Of CustomerEager).
    HasRequired (Function (c) c.CustomerBLOBs).
    WithRequiredPrincipal ()
End Sub
Caricamento di impazienza e Pigramente
Con i cambiamenti in atto, ora posso trattare tutti i clienti nel mio database con entusiasmo e recuperare l'immagine solo quando ne ho bisogno (pigramente). Codice come il seguente elaborerà tutti i clienti, solo occasionalmente recuperare l'entità con la proprietà blob:

Dim db As New AdventureWorksLTEntitiesRevised
Per ogni c Come CustomerEager In db.CustomersEager
  '... le proprietà di processo CustomerEager ...
  Se c.LastName = "Vogel" Allora
    '... Processo c.CustomerBLOBs.Photo
  End If
Prossimo
Tuttavia, questo codice si tradurrà in due viaggi al database: uno per recuperare l'entità CustomerEager e uno per recuperare i dati CustomerLazy. Questo ha senso quando è necessario solo l'entità pigro rado.

Tuttavia, quando si sa che avete bisogno di i dati provenienti da entrambe le entità, vuoi per evitare che il secondo viaggio al database. In tale scenario, si desidera recuperare il vostro soggetto pigro con entusiasmo (ovviamente, se avete bisogno di tutti i dati per tutto il tempo, non avresti dovuto dividere le tabelle). Entity Framework sarà avidamente caricare il soggetto pigro se si utilizza Entity Framework Includi metodo quando il recupero dell'oggetto ansioso. Includi cause Entity Framework per eseguire un carico desideroso dell'oggetto all'altra estremità della struttura di navigazione il cui nome si passa al metodo Includi. Questo esempio fa un carico desideroso dell'entità

indicato da un terreno CustomerBLOBs dell'entità ansioso:
Dim cust = (da C In db.CustomersEager.Include ("CustomerBLOBs")
            Dove c.LastName = "Vogel"
            Selezionare c) .Prima
... processo sia CustomerEager e CustomerLazy proprietà
Questi passaggi sono necessari solo se si è in uno dei due scenari che ho descritto all'inizio di questa colonna - e, anche allora, solo se effettivamente avete un problema di prestazioni. E se lo fai, le tabelle di divisione e con il supporto Entity Framework per eager loading e pigro attraverso le associazioni vi darà eager loading dei dati che si desidera e lazy loading dei dati che altrimenti potrebbero danneggiare la vostra performance. In altre parole: tutto quello che volete.

Nessun commento:

Posta un commento