2017-01-26 6 views
0

ich C# und Entity Framework 6.Linq Abfrage Wo() mit Datum Subtraktion zu viel Zeit nehmen

mit habe ich eine Einheit (Tier) in meiner Datenbank, die eine Liste von einem anderen Unternehmen (Feeding) hat an es. Der Feed-Entität ist eine DateTime zugeordnet.

Meine Abfrage sieht wie folgt aus:

var sum = animal.Feedings.Where(f => (DateTimeOffset.Now.Date- f.DateTime.Date).Days == 1).Select(f => f.Amount).Sum() 

ich im Grunde die Summe der Gesamtfuttermenge von gestern wollen. Aber wenn mein Animal-Objekt eine erhebliche Menge an Fütterungen hat (sagen wir 5000, was erwartet wird und sogar noch höher sein könnte), dauert diese Linie mehr als 20 Sekunden.

Gibt es eine Möglichkeit, diese Codezeile effizienter zu gestalten?

BEARBEITEN Es scheint, dass das Fütterungsobjekt aus dem Tierobjekt lazy geladen wird.

var feedings = animal.Feedings 

Diese Zeile benötigt jetzt die exzessive Zeit. Das Tierobjekt wurde ursprünglich aus einem AsQueryable() - Objekt erstellt, indem das Tier anhand einer ID ausgewählt wurde.

EDIT # 2 Diese Logik ist in dem Tiere Repository und kann nicht die DbContext zugreifen, kann es nur eine IQueryable der Tiersammlung zugreifen. Ich habe auch keinen Zugriff auf EFs Include() -Funktion, um zu versuchen, die Fütterungen mit der Abfrage zu integrieren.

+0

Erstellen Sie Ansicht, UDF oder SP in db und wählen Sie dann aus der erstellten Abfrage. Auf diese Weise können Sie SQL-Performance –

+0

wo genau ist die Verzögerung passiert. Auf der C# Ebene oder auf der DB-Ebene? Stellen Sie sicher, dass die Verzögerung nicht die DB ist, wenn dies der Fall ist, versuchen Sie, die Tabelle für Ihre linq-Abfrage (Indizes usw.) zu optimieren. – Milan

+0

Wenn das 'Tier'-Objekt bereits geladen ist (wie es scheint), wird höchstwahrscheinlich die Zeit dafür genommen, die' Fütterungen'-Sammlung zu laden. Haben Sie 'var fedings = animal.Feedings;' versucht und sehen Sie, wie lange es dauert, und messen Sie dann die LINQ-Abfrage, die eine LINQ to Objects-Abfrage sein sollte, und 5K-Elemente sollten in Millisekunden ausgeführt werden. –

Antwort

3

Der langsame Teil der Feedings Sammlung wird geladen. Wenn es bereits geladen ist, wird die LINQ to Objects-Abfrage sehr schnell ausgeführt.

Wenn Sie den Zugriff auf die DbContext haben, anstatt die Sammlung Laden Sie die Abfrage in der Datenbank wie folgt ausführen:

var date = new DateTimeOffset(DateTime.Today.AddDays(-1)); 
var sum = dbContext.Entry(animal).Collection(e => e.Feedings).Query() 
    .Where(f => DbFunctions.TruncateTime(f.DateTime) == date) 
    .Sum(f => f.Amount); 

Zugang zu IQueryable<Animals> Having (wie in den Kommentaren und dem Update erwähnt) wird auch Arbeit. Nur statt

dbContext.Entry(animal).Collection(e => e.Feedings).Query() 

würden Sie so etwas wie dieses

animals.Where(a => a.Id == animal.Id).SelectMany(a => a.Feedings) 

Der Rest ist das gleiche verwenden.

+0

Leider befindet sich diese Logik im Tier-Repository und kann nicht auf den DB-Kontext zugreifen. Es kann nur auf eine IQueryable der Tiersammlung zugreifen. – cookiekitty

+0

So etwas wie 'IQueryable Animals'? –

+0

Btw, diese "Repositories" scheinen mehr Probleme zu verursachen als sie zu lösen (IMO). Oder zumindest die einfachen Dinge schwieriger, wenn nicht unmöglich machen. –

2

Wenn Sie einen Index für Datetime haben, versuchen Sie dann

var startDate = DateTimeOffset.Now.AddDays(-1).Date; 
var endDate = DateTimeOffset.Now.Date; 

var sum = animal.Feedings 
    .Where(f => (f.DateTime >= startDate && f.DateTime < endDate)) 
    .Select(f => f.Amount) 
    .DefaultIfEmpty(0) // don't throw an error if the query has no results 
    .Sum();