Ricerca…


Utilizzando AsNoTracking

Cattivo esempio:

var location =  dbContext.Location
                     .Where(l => l.Location.ID == location_ID)
                     .SingleOrDefault();

return location;

Poiché il codice sopra riportato restituisce semplicemente un'entità senza modificarla o aggiungerla, possiamo evitare il costo di tracciamento.

Buon esempio:

var location =  dbContext.Location.AsNoTracking()
                     .Where(l => l.Location.ID == location_ID)
                     .SingleOrDefault();

return location;

Quando usiamo la funzione AsNoTracking() stiamo esplicitamente dicendo a Entity Framework che le entità non sono tracciate dal contesto. Questo può essere particolarmente utile quando si recuperano grandi quantità di dati dall'archivio dati. Tuttavia, se si desidera apportare modifiche a entità non tracciabili, è necessario ricordarsi di collegarle prima di chiamare SaveChanges .

Caricamento solo dati richiesti

Un problema spesso visto nel codice è il caricamento di tutti i dati. Ciò aumenterà notevolmente il carico sul server.

Diciamo che ho un modello chiamato "posizione" che contiene 10 campi, ma non tutti i campi sono richiesti allo stesso tempo. Diciamo che voglio solo il parametro "LocationName" di quel modello.

Cattivo esempio

var location =  dbContext.Location.AsNoTracking()
                         .Where(l => l.Location.ID == location_ID)
                         .SingleOrDefault();

return location.Name;

Buon esempio

var location =  dbContext.Location
                          .Where(l => l.Location.ID == location_ID)
                          .Select(l => l.LocationName);
                          .SingleOrDefault();

return location;

Il codice nel "buon esempio" recupererà solo "LocationName" e nient'altro.

Si noti che dal momento che nessuna entità è materializzata in questo esempio, AsNoTracking() non è necessario. Non c'è niente da rintracciare comunque.

Recupero di più campi con tipi anonimi

var location = dbContext.Location
                    .Where(l => l.Location.ID == location_ID)
                    .Select(l => new { Name = l.LocationName, Area = l.LocationArea })
                    .SingleOrDefault();

return location.Name + " has an area of " + location.Area;

Come nell'esempio precedente, solo i campi "LocationName" e "LocationArea" verranno recuperati dal database, il tipo anonimo può contenere tutti i valori desiderati.

Esegui query nel database quando possibile, non in memoria.

Supponiamo di voler contare quante contee ci sono in Texas:

var counties = dbContext.States.Single(s => s.Code == "tx").Counties.Count();

La query è corretta, ma inefficiente. States.Single(…) carica uno stato dal database. Successivamente, Counties carica tutte le 254 contee con tutti i loro campi in una seconda query. .Count() viene quindi eseguito in memoria nella raccolta Counties caricata.
Abbiamo caricato molti dati di cui non abbiamo bisogno e possiamo fare di meglio:

var counties = dbContext.Counties.Count(c => c.State.Code == "tx");

Qui facciamo una sola query, che in SQL si traduce in un conteggio e un join. Restituiamo solo il conteggio dal database: abbiamo salvato righe di ritorno, campi e creazione di oggetti.

È facile vedere dove viene eseguita la query osservando il tipo di raccolta: IQueryable<T> vs. IEnumerable<T> .

Esegui più query asincrone e in parallelo

Quando si utilizzano query asincrone, è possibile eseguire più query contemporaneamente, ma non nello stesso contesto. Se il tempo di esecuzione di una query è 10 secondi, il tempo per l'esempio errato sarà 20 secondi, mentre il tempo per il buon esempio sarà 10 secondi.

Cattivo esempio

IEnumerable<TResult1> result1;
IEnumerable<TResult2> result2;

using(var context = new Context())
{
    result1 = await context.Set<TResult1>().ToListAsync().ConfigureAwait(false);
    result2 = await context.Set<TResult1>().ToListAsync().ConfigureAwait(false);
}

Buon esempio

public async Task<IEnumerable<TResult>> GetResult<TResult>()
{
    using(var context = new Context())
    {
        return await context.Set<TResult1>().ToListAsync().ConfigureAwait(false);
    }
}



IEnumerable<TResult1> result1;
IEnumerable<TResult2> result2;

var result1Task = GetResult<TResult1>();
var result2Task = GetResult<TResult2>();

await Task.WhenAll(result1Task, result2Task).ConfigureAwait(false);

var result1 = result1Task.Result;
var result2 = result2Task.Result;

Disabilitare il rilevamento delle modifiche e la generazione del proxy

Se desideri solo ottenere dati, ma non modificare nulla, puoi disattivare il rilevamento delle modifiche e la creazione del proxy. Ciò migliorerà le prestazioni e impedirà anche il caricamento lento.

Cattivo esempio:

using(var context = new Context())
{
    return await context.Set<MyEntity>().ToListAsync().ConfigureAwait(false);
}

Buon esempio:

using(var context = new Context())
{
    context.Configuration.AutoDetectChangesEnabled = false;
    context.Configuration.ProxyCreationEnabled = false;

    return await context.Set<MyEntity>().ToListAsync().ConfigureAwait(false);
}

È particolarmente comune disattivarle all'interno del costruttore del contesto, specialmente se si desidera che queste siano impostate sulla propria soluzione:

public class MyContext : DbContext
{
    public MyContext()
        : base("MyContext")
    {
        Configuration.AutoDetectChangesEnabled = false;
        Configuration.ProxyCreationEnabled = false;
    }

    //snip
}

Lavorare con entità stub

Supponiamo di avere Product e Category in una relazione molti-a-molti:

public class Product
{
    public Product()
    {
        Categories = new HashSet<Category>();
    }
    public int ProductId { get; set; }
    public string ProductName { get; set; }
    public virtual ICollection<Category> Categories { get; private set; }
}

public class Category
{
    public Category()
    {
        Products = new HashSet<Product>();
    }
    public int CategoryId { get; set; }
    public string CategoryName { get; set; }
    public virtual ICollection<Product> Products { get; set; }
}

Se vogliamo aggiungere una Category a un Product , dobbiamo caricare il prodotto e aggiungere la categoria alle sue Categories , ad esempio:

Cattivo esempio:

var product = db.Products.Find(1);
var category = db.Categories.Find(2);
product.Categories.Add(category);
db.SaveChanges();

(dove db è una sottoclasse DbContext ).

Questo crea un record nella tabella di giunzione tra Product e Category . Tuttavia, questa tabella contiene solo due valori Id . È uno spreco di risorse caricare due entità complete per creare un record minuscolo.

Un modo più efficiente è utilizzare le entità stub , cioè gli oggetti entità, creati in memoria, contenenti solo il minimo di dati, di solito solo un valore Id . Questo è quello che sembra:

Buon esempio:

// Create two stub entities
var product = new Product { ProductId = 1 };
var category = new Category { CategoryId = 2 };

// Attach the stub entities to the context
db.Entry(product).State = System.Data.Entity.EntityState.Unchanged;
db.Entry(category).State = System.Data.Entity.EntityState.Unchanged;

product.Categories.Add(category);
db.SaveChanges();

Il risultato finale è lo stesso, ma evita due roundtrip al database.

Prevenire i duplicati

Se vuoi verificare se l'associazione esiste già, è sufficiente una query economica. Per esempio:

var exists = db.Categories.Any(c => c.Id == 1 && c.Products.Any(p => p.Id == 14));

Di nuovo, questo non caricherà le entità complete nella memoria. Effettua query sulla tabella di giunzione e restituisce solo un valore booleano.



Modified text is an extract of the original Stack Overflow Documentation
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow