Suche…


AsNoTracking verwenden

Schlechtes Beispiel:

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

return location;

Da der obige Code lediglich eine Entität zurückgibt, ohne sie zu ändern oder hinzuzufügen, können wir die Kosten für die Nachverfolgung vermeiden.

Gutes Beispiel:

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

return location;

Wenn wir die Funktion AsNoTracking() verwenden, AsNoTracking() wir Entity Framework explizit mit, dass die Entitäten nicht durch den Kontext verfolgt werden. Dies kann besonders nützlich sein, wenn große Datenmengen aus Ihrem Datenspeicher abgerufen werden. Wenn Sie jedoch Änderungen an nicht nachverfolgten Entitäten vornehmen möchten, müssen Sie diese vor dem Aufruf von SaveChanges .

Nur benötigte Daten werden geladen

Ein häufig beim Code auftretendes Problem ist das Laden aller Daten. Dadurch wird die Belastung des Servers erheblich erhöht.

Angenommen, ich habe ein Modell mit dem Namen "location", das 10 Felder enthält, aber nicht alle Felder werden gleichzeitig benötigt. Angenommen, ich möchte nur den Parameter 'LocationName' dieses Modells.

Schlechtes Beispiel

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

return location.Name;

Gutes Beispiel

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

return location;

Der Code im "guten Beispiel" wird nur "LocationName" und nichts anderes abrufen.

Beachten Sie, dass AsNoTracking() nicht erforderlich ist, da in diesem Beispiel keine Entität AsNoTracking() wird. Es gibt sowieso nichts zu verfolgen.

Weitere Felder mit anonymen Typen abrufen

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;

Wie im vorherigen Beispiel werden nur die Felder 'LocationName' und 'LocationArea' aus der Datenbank abgerufen. Der anonyme Typ kann so viele Werte enthalten, wie Sie möchten.

Führen Sie, wenn möglich, Abfragen in der Datenbank aus, nicht im Speicher.

Angenommen, wir wollen zählen, wie viele Grafschaften es in Texas gibt:

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

Die Abfrage ist korrekt, aber ineffizient. States.Single(…) lädt einen Status aus der Datenbank. Als Nächstes lädt Counties alle 254 Counties mit allen Feldern in einer zweiten Abfrage. .Count() wird dann in der geladenen Counties Sammlung im Speicher ausgeführt .
Wir haben viele Daten geladen, die wir nicht benötigen, und wir können es besser machen:

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

Hier führen wir nur eine Abfrage aus, die in SQL in eine Anzahl und einen Join übersetzt wird. Wir geben nur die Anzahl aus der Datenbank zurück - wir haben zurückgegebene Zeilen, Felder und die Erstellung von Objekten gespeichert.

Der Abfragetyp lässt sich anhand des Auflistungstyps leicht erkennen: IQueryable<T> vs. IEnumerable<T> .

Führen Sie mehrere Abfragen asynchron und parallel aus

Wenn Sie asynchrone Abfragen verwenden, können Sie mehrere Abfragen gleichzeitig ausführen, jedoch nicht in demselben Kontext. Wenn die Ausführungszeit einer Abfrage 10 Sekunden beträgt, beträgt die Zeit für das fehlerhafte Beispiel 20 Sekunden, während die Zeit für das gute Beispiel 10 Sekunden beträgt.

Schlechtes Beispiel

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

Gutes Beispiel

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;

Deaktivieren Sie die Änderungsnachverfolgung und die Proxy-Generierung

Wenn Sie nur Daten abrufen möchten, aber nichts ändern möchten, können Sie die Änderungsnachverfolgung und die Proxy-Erstellung deaktivieren. Dies verbessert Ihre Leistung und verhindert auch ein verzögertes Laden.

Schlechtes Beispiel:

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

Gutes Beispiel:

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

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

Es ist besonders üblich, diese Einstellungen im Konstruktor Ihres Kontexts zu deaktivieren, insbesondere wenn Sie möchten, dass diese in Ihrer Lösung angezeigt werden:

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

    //snip
}

Mit Stub-Entitäten arbeiten

Nehmen wir an, wir haben Product und Category in einer Viele-zu-Viele-Beziehung:

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

Wenn Sie einem Product eine Category hinzufügen möchten, müssen Sie das Produkt laden und die Kategorie zu seinen Categories hinzufügen. Beispiel:

Schlechtes Beispiel:

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

(wobei db eine DbContext Unterklasse ist).

Dadurch wird ein Datensatz in der Junction-Tabelle zwischen Product und Category . Diese Tabelle enthält jedoch nur zwei Id Werte. Es ist eine Verschwendung von Ressourcen, zwei vollständige Entitäten zu laden, um einen kleinen Datensatz zu erstellen.

Eine effizientere Methode ist die Verwendung von Stub-Entities , dh Entity-Objekten, die im Speicher erstellt werden und nur das absolute Minimum an Daten enthalten, normalerweise nur einen Id Wert. So sieht es aus:

Gutes Beispiel:

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

Das Endergebnis ist dasselbe, aber es werden zwei Roundtrips zur Datenbank vermieden.

Duplikate vermeiden

Wenn Sie prüfen möchten, ob die Verknüpfung bereits vorhanden ist, genügt eine billige Abfrage. Zum Beispiel:

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

Auch hier werden keine vollständigen Elemente in den Speicher geladen. Es fragt effektiv die Junction-Tabelle ab und gibt nur einen Boolean zurück.



Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow