Szukaj…


Korzystanie z AsNoTracking

Zły przykład:

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

return location;

Ponieważ powyższy kod po prostu zwraca jednostkę bez modyfikacji lub dodawania, możemy uniknąć kosztów śledzenia.

Dobry przykład:

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

return location;

Kiedy używamy funkcji AsNoTracking() , wyraźnie mówimy Entity Framework, że jednostki nie są śledzone przez kontekst. Może to być szczególnie przydatne podczas pobierania dużych ilości danych z magazynu danych. Jeśli jednak chcesz wprowadzić zmiany w SaveChanges , musisz pamiętać o dołączeniu ich przed wywołaniem SaveChanges .

Ładowanie tylko wymaganych danych

Jednym z problemów często spotykanych w kodzie jest ładowanie wszystkich danych. To znacznie zwiększy obciążenie serwera.

Załóżmy, że mam model o nazwie „lokalizacja”, który zawiera 10 pól, ale nie wszystkie pola są wymagane w tym samym czasie. Powiedzmy, że chcę tylko parametr „LocationName” tego modelu.

Zły przykład

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

return location.Name;

Dobry przykład

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

return location;

Kod w „dobrym przykładzie” pobierze tylko „LocationName” i nic więcej.

Zauważ, że ponieważ w tym przykładzie nie AsNoTracking() , AsNoTracking() nie jest konieczny. Zresztą i tak nie ma nic do śledzenia.

Pobieranie większej liczby pól za pomocą Anonimowych typów

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;

Tak jak w poprzednim przykładzie, tylko pola „LocationName” i „LocationArea” zostaną pobrane z bazy danych, typ anonimowy może przechowywać tyle wartości, ile chcesz.

W miarę możliwości wykonuj zapytania w bazie danych, a nie w pamięci.

Załóżmy, że chcemy policzyć, ile hrabstw jest w Teksasie:

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

Zapytanie jest poprawne, ale nieefektywne. States.Single(…) ładuje stan z bazy danych. Następnie Counties ładują wszystkie 254 hrabstwa ze wszystkimi swoimi polami w drugim zapytaniu. .Count() jest następnie wykonywane w pamięci załadowanej kolekcji Counties .
Załadowaliśmy wiele danych, których nie potrzebujemy, i możemy to zrobić lepiej:

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

Tutaj wykonujemy tylko jedno zapytanie, które w SQL przekłada się na liczbę i sprzężenie. Zwracamy tylko liczbę z bazy danych - zapisaliśmy zwracane wiersze, pola i tworzenie obiektów.

Łatwo jest zobaczyć, gdzie powstaje zapytanie, patrząc na typ kolekcji: IQueryable<T> vs. IEnumerable<T> .

Wykonaj wiele zapytań asynchronicznie i równolegle

Korzystając z zapytań asynchronicznych, możesz wykonywać wiele zapytań jednocześnie, ale nie w tym samym kontekście. Jeśli czas wykonania jednego zapytania wynosi 10 s, czas dla złego przykładu wyniesie 20 s, a dla dobrego przykładu 10 s.

Zły przykład

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

Dobry przykład

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;

Wyłącz śledzenie zmian i generowanie proxy

Jeśli chcesz tylko pobierać dane, ale niczego nie modyfikować, możesz wyłączyć śledzenie zmian i tworzenie proxy. Poprawi to twoją wydajność, a także zapobiegnie leniwemu ładowaniu.

Zły przykład:

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

Dobry przykład:

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

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

Szczególnie często wyłącza się je z poziomu konstruktora kontekstu, szczególnie jeśli chcesz ustawić je w swoim rozwiązaniu:

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

    //snip
}

Praca z elementami pośredniczącymi

Powiedzmy, że mamy Product i Category w relacji wiele do wielu:

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

Jeśli chcemy dodać Category do Product , musimy załadować produkt i dodać kategorię do jego Categories , na przykład:

Zły przykład:

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

(gdzie db jest podklasą DbContext ).

Spowoduje to utworzenie jednego rekordu w tabeli połączeń między Product a Category . Jednak ta tabela zawiera tylko dwie wartości Id . Załadowanie dwóch pełnych podmiotów w celu utworzenia jednego małego rekordu to marnotrawstwo zasobów.

Bardziej efektywnym sposobem jest użycie encji pośredniczących , tj. Obiektów encji, utworzonych w pamięci, zawierających jedynie absolutne minimum danych, zwykle tylko wartość Id . Tak to wygląda:

Dobry przykład:

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

Wynik końcowy jest taki sam, ale unika się dwóch powrotów do bazy danych.

Zapobiegaj duplikatom

Jeśli chcesz sprawdzić, czy powiązanie już istnieje, wystarczy tanie zapytanie. Na przykład:

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

Ponownie, to nie załaduje pełnych bytów do pamięci. Skutecznie odpytuje tablicę połączeń i zwraca tylko wartość logiczną.



Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow