Sök…


Använda AsNoTracking

Dåligt exempel:

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

return location;

Eftersom koden ovan bara returnerar en enhet utan att ändra eller lägga till den, kan vi undvika spårningskostnader.

Bra exempel:

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

return location;

När vi använder funktionen AsNoTracking() säger vi uttryckligen Entity Framework att enheterna inte spåras av sammanhanget. Detta kan vara särskilt användbart när du hämtar stora mängder data från din datalager. Om du vill göra ändringar i ospårade enheter men du måste komma ihåg att bifoga dem innan du ringer SaveChanges .

Laddar endast obligatoriska data

Ett problem som man ofta ser i koden är att ladda alla data. Detta kommer att öka belastningen på servern kraftigt.

Låt oss säga att jag har en modell som heter "plats" som har 10 fält i den, men inte alla fält krävs samtidigt. Låt oss säga att jag bara vill ha parametern "LocationName" för den modellen.

Dåligt exempel

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

return location.Name;

Bra exempel

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

return location;

Koden i "bra exempel" hämtar bara "Platsnamn" och inget annat.

Observera att eftersom ingen enhet är materialiserad i det här exemplet är AsNoTracking() inte nödvändigt. Det finns inget att spåra ändå.

Hämtar fler fält med anonyma typer

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;

Samma som exemplet tidigare, bara fälten 'LocationName' och 'LocationArea' kommer att hämtas från databasen, den anonyma typen kan innehålla så många värden du vill ha.

Kör frågor i databasen när det är möjligt, inte i minnet.

Anta att vi vill räkna hur många län som finns i Texas:

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

Frågan är korrekt, men ineffektiv. States.Single(…) laddar ett tillstånd från databasen. Därefter laddar Counties alla 254 län med alla sina fält i en andra fråga. .Count() utförs sedan i minnet på den laddade Counties samlingen.
Vi har laddat mycket data som vi inte behöver, och vi kan göra bättre:

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

Här gör vi bara en fråga, som i SQL översätter till en räkning och en sammanfogning. Vi returnerar bara antalet från databasen - vi har sparat återkommande rader, fält och skapande av objekt.

Det är lätt att se var frågan görs genom att titta på samlingstypen: IQueryable<T> vs. IEnumerable<T> .

Kör flera frågor async och parallellt

När du använder asyncfrågor kan du köra flera frågor samtidigt, men inte i samma sammanhang. Om exekveringstiden för en fråga är 10s kommer tiden för det dåliga exemplet att vara 20s, medan tiden för det goda exemplet är 10s.

Dåligt exempel

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

Bra exempel

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;

Inaktivera ändringsspårning och proxygenerering

Om du bara vill hämta data, men inte ändra någonting, kan du stänga av ändringsspårning och skapa proxy. Detta kommer att förbättra din prestanda och också förhindra lat laddning.

Dåligt exempel:

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

Bra exempel:

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

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

Det är särskilt vanligt att stänga av dessa från konstruktören för ditt sammanhang, särskilt om du vill att dessa ska ställas in i din lösning:

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

    //snip
}

Arbeta med stubenheter

Säg att vi har Product och Category i ett förhållande mellan många och många:

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

Om vi vill lägga till en Category till en Product måste vi ladda produkten och lägga till kategorin i dess Categories , till exempel:

Dåligt exempel:

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

(där db är en DbContext underklass).

Detta skapar en post i korsningstabellen mellan Product och Category . Denna tabell innehåller emellertid bara två Id värden. Det är slöseri med resurser att ladda två fullständiga enheter för att skapa en liten post.

Ett mer effektivt sätt är att använda stubenheter , det vill säga entitetsobjekt, skapade i minnet, som endast innehåller ett minimum av data, vanligtvis bara ett Id värde. Så här ser det ut:

Bra exempel:

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

Slutresultatet är detsamma, men det undviker två rundturer till databasen.

Förhindra dubbletter

Det du vill kontrollera om föreningen redan existerar räcker med en billig fråga. Till exempel:

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

Återigen kommer detta inte att ladda fullständiga enheter i minnet. Den frågar effektivt korsningstabellen och returnerar bara en boolean.



Modified text is an extract of the original Stack Overflow Documentation
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow