Zoeken…


AsNoTracking gebruiken

Slecht voorbeeld:

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

return location;

Aangezien de bovenstaande code eenvoudigweg een entiteit retourneert zonder deze te wijzigen of toe te voegen, kunnen we trackingkosten vermijden.

Goed voorbeeld:

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

return location;

Wanneer we de functie AsNoTracking() , vertellen we expliciet Entity Framework dat de entiteiten niet worden gevolgd door de context. Dit kan vooral handig zijn bij het ophalen van grote hoeveelheden gegevens uit uw gegevensarchief. Als u echter wijzigingen wilt aanbrengen in niet-gevolgde entiteiten, moet u eraan denken deze te koppelen voordat u SaveChanges .

Alleen vereiste gegevens laden

Een probleem dat vaak in code wordt gezien, is het laden van alle gegevens. Dit zal de belasting van de server aanzienlijk verhogen.

Laten we zeggen dat ik een model met de naam "Locatie" heb dat 10 velden bevat, maar niet alle velden zijn tegelijkertijd vereist. Laten we zeggen dat ik alleen de parameter 'LocationName' van dat model wil.

Slecht voorbeeld

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

return location.Name;

Goed voorbeeld

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

return location;

De code in het "goede voorbeeld" haalt alleen 'LocationName' op en verder niets.

Merk op dat aangezien er in dit voorbeeld geen entiteit wordt gematerialiseerd, AsNoTracking() niet nodig is. Er valt toch niets te volgen.

Meer velden ophalen met anonieme typen

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;

Hetzelfde als het voorbeeld hiervoor, alleen de velden 'LocationName' en 'LocationArea' worden opgehaald uit de database. Het anonieme type kan zoveel waarden bevatten als u wilt.

Voer indien mogelijk query's in de database uit, niet in het geheugen.

Stel dat we willen tellen hoeveel provincies er in Texas zijn:

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

De zoekopdracht is correct, maar inefficiënt. States.Single(…) laadt een status uit de database. Vervolgens Counties laadt alle 254 gemeenten met al hun velden in een tweede query. .Count() wordt vervolgens uitgevoerd in het geheugen van de geladen Counties verzameling.
We hebben veel gegevens geladen die we niet nodig hebben en we kunnen het beter doen:

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

Hier doen we slechts één query, die in SQL wordt vertaald in een telling en een join. We retourneren alleen de telling uit de database - we hebben terugkerende rijen, velden en het maken van objecten opgeslagen.

Het is gemakkelijk om te zien waar de zoekopdracht wordt uitgevoerd door te kijken naar het type verzameling: IQueryable<T> versus IEnumerable<T> .

Voer meerdere query's asynchroon en parallel uit

Wanneer u asynchrone query's gebruikt, kunt u meerdere query's tegelijkertijd uitvoeren, maar niet in dezelfde context. Als de uitvoeringstijd van een query 10 seconden is, is de tijd voor het slechte voorbeeld 20 seconden, terwijl de tijd voor het goede voorbeeld 10 seconden is.

Slecht voorbeeld

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);
}

Goed voorbeeld

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;

Schakel het bijhouden van wijzigingen en het genereren van proxy's uit

Als u alleen gegevens wilt ophalen, maar niets wilt wijzigen, kunt u het bijhouden van wijzigingen en het maken van proxy's uitschakelen. Dit verbetert uw prestaties en voorkomt ook lui laden.

Slecht voorbeeld:

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

Goed voorbeeld:

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

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

Het is vooral gebruikelijk om deze uit te schakelen vanuit de constructor van uw context, vooral als u wilt dat deze in uw oplossing worden ingesteld:

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

    //snip
}

Werken met stub-entiteiten

Stel dat we Product en Category in een veel-op-veel-relatie:

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; }
}

Als we een Category aan een Product willen toevoegen, moeten we het product laden en de categorie aan de Categories , bijvoorbeeld:

Slecht voorbeeld:

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

(waarbij db een DbContext subklasse is).

Hiermee maakt u één record in de kruistabel tussen Product en Category . Deze tabel bevat echter slechts twee Id waarden. Het is een verspilling van middelen om twee volledige entiteiten te laden om één klein record te maken.

Een efficiëntere manier is om stub-entiteiten te gebruiken, dat wil zeggen entiteitsobjecten, gemaakt in het geheugen, die alleen het absolute minimum aan gegevens bevatten, meestal alleen een Id waarde. Zo ziet het eruit:

Goed voorbeeld:

// 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();

Het eindresultaat is hetzelfde, maar het vermijdt twee retourreizen naar de database.

Voorkom duplicaten

Als u wilt controleren of de koppeling al bestaat, volstaat een goedkope zoekopdracht. Bijvoorbeeld:

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

Nogmaals, dit zal geen volledige entiteiten in het geheugen laden. Het ondervraagt effectief de junctietabel en retourneert alleen een Boolean.



Modified text is an extract of the original Stack Overflow Documentation
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow