2016-05-06 12 views
2

ich zuerst db Architektur mit einfachen Vererbung THP-Code implementiert haben: Database diagramEF Include auf TPH

Und ich brauche alle Benachrichtigungen aller Art abzufragen. TargetUser Eigenschaft in NotificationUser Tabelle ist eine Assoziation. Ich versuche nächsten Code auszuführen:

var notifications = _context.Notifications; 
foreach (var notification in notifications) 
{ 
    Debug.WriteLine((notification is NotificationUser)? ((NotificationUser) notification).TargetUser?.Name : "-"); 
} 

In Datenbank propety TargetUser gesetzt Fremdschlüssel zu korrigieren, aber in Code bekomme ich kein Ergebnis. Lazy Loading ist aktiviert.

Ist es möglich, eifrig laden? Ich hatte bereits versucht, _context.Notifications.Include('TargetUser') zu schreiben, indem es eine Ausnahme wirft.


Upd. Die Ausnahme ist:

A specified Include path is not valid. The EntityType 'Core.Concrete.NotificationBase' does not declare a navigation property with the name 'TargetUser'. 

Versuchte this answer zu ändern:

var notifications = _context.Notifications.OfType<NotificationUser>() 
       .Include(n => n.TargetUser) 
       .Cast<NotificationBase>() 
       .Union(_context.Notifications.OfType<NotificationPlace>() 

aber immer noch die gleiche Ausnahme ausgelöst.

+0

Nicht context.Notifications.Include Arbeit (‚TargetUser‘) sieht aus wie es einige allgemeine Fehler ist. Du hast gesagt, dass es eine Ausnahme gibt? Kann ich es sehen? –

+0

@AlexanderHaas, versucht, Code mit Include erneut auszuführen und Eigenschaft wurde korrekt geladen. Scheint so, als hätte ich einen Grammatikfehler. Danke trotzdem! – Ivvan

+0

@AlexanderHaas, Ok, es funktioniert nicht. Ich habe nicht bemerkt. OfType () in meiner Abfrage. Ohne es hat es eine Ausnahme geworfen. Aktualisierte Frage mit Nachricht. – Ivvan

Antwort

0

bereits viele verschiedene Lösungen ausprobiert und keiner meine Anforderungen passen, da ich auf eine gerade arbeite API, und die Abfrage muss die Paginierung unterstützen und konstante Anforderungen an die Datenbank stellen (und nicht alle Entitäten im Speicher laden).

Endlich eine Lösung gefunden, vielleicht nicht die beste, aber genug für jetzt. Erstens fordere ich einen Teil der bestellten Daten (Paginierung Logik):

var notifications = _context.Notifications 
      .OrderByDescending(n => n.DateTime) 
      .Skip(offset) 
      .Take(limit); 

(An dieser Stelle ich in irgendwelchen Eigenschaften nicht interessiert bin) Als nächstes, ich bin für jeden Entitätstyp die IDs der geladenen Elemente bekommen :

var ids = notifications.OfType<NotificationUser>().Select(n => n.Id).ToList(); 

und schließlich Last spezifische Entitäten alle Eigenschaften, einschließlich:

var userNotifications = _context.Notifications.OfType<NotificationUser>() 
      .Include(n => n.TargetUser) 
      .Include(n => n.TargetUser.Images) 
      .Where(n => ids.Contains(n.Id)) 
      .ToList(); 

alle Unternehmen geht aufzulisten und sortiert noch einmal.

Viele schlechte Sachen hier, hoffe jemand kann bessere Lösung bieten.

1

Ich weiß nicht über die Anzahl der Entitäten, mit denen Sie arbeiten werden. Wenn möglich, würde ich versuchen, die Vereinigung nicht auf dem DB Server zu tun:

var userNotifications = _context.Notifications.OfType<NotificationUser>() 
           .Include(n => n.TargetUser).ToList(); 
var placeNotifications = _context.Notifications.OfType<NotificationPlace>().ToList(); 
var notifications = userNotifications.Union(placeNotifications); 

Siehe https://stackoverflow.com/a/27643393/2342504

+0

Eigentlich ist es ein Teil der Web-API und Nummer von Entitäten kann sehr groß sein, daher ist es in meinem Fall keine gute Übung, sie alle in den Speicher zu laden. – Ivvan

1

Ich weiß, dass dies ein alter Thread ist, aber ich möchte noch einige Verbesserungen für jemanden auf der Suche nach der gleichen Lösung veröffentlichen.

1.Netzwerkredundanz

Ids auswählen und dann eine Abfrage ausgeführt wird, dass lädt Elemente mit dem Ids redundant ist und die gleiche Wirkung kann durch einfaches Ausführen dieses

Lösung erreicht werden:

var userNotifications = _context.Notifications 
    .OrderByDescending(n => n.DateTime) 
    .Skip(offset) 
    .Take(limit) 
    .OfType<NotificationUser>() 
    .Include(n => n.TargetUser) 
    .Include(n => n.TargetUser.Images) 
    .ToList(); 

Auf diese Weise, Sie warten nicht auf 2 DB-Verbindungen, sondern nur auf eine. Außerdem sparen Sie Traffic.

2. Paging auf ignorierten Entitäten?

Man würde annehmen, dass diese spezifische Methode nur für die Betrachtung von Entitäten eines geerbten Typs verwendet wird. Ich würde also erwarten, dass Skip and Take direkt nur an Entitäten dieses Typs arbeitet. z.B. Ich möchte 10 NotificationUsers überspringen, nicht 10 User (von denen nur 4 NotificationUsers sind).

Lösung: Verschieben ofType höher die Abfrage

var userNotifications = _context.Notifications 
    .OfType<NotificationUser>() 
    .OrderByDescending(n => n.DateTime) 
    .Skip(offset) 
    .Take(limit) 
    .Include(n => n.TargetUser) 
    .Include(n => n.TargetUser.Images) 
    .ToList(); 

3. Async/Await

Wenn eine API zu schreiben, können Sie über die Verwendung von Asynchron/erwarten wie das denken sollte nicht blockiert die Thread verschwendet also weniger Ressourcen (das wird wahrscheinlich erfordern, dass Sie viel von Ihrem existierenden Code umschreiben, wenn Sie ihn nicht bereits benutzen).

Bitte studieren Sie die Vorteile von async/await und verwenden Sie sie in Szenarien wie Warten auf ein Ergebnis.

Lösung: Ändern Sie diese

private List<NotificationUser> GetNotificationUsers(int offset, int limit) 
    { 
     return _context.Notifications 
       .OfType<NotificationUser>() 
       .OrderByDescending(n => n.DateTime) 
       .Skip(offset) 
       .Take(limit) 
       .Include(n => n.TargetUser) 
       .Include(n => n.TargetUser.Images) 
       .ToList(); 
    } 

in diesem

private async Task<List<NotificationUser>> GetNotificationUsersAsync(int offset, int limit) 
    { 
     return await _context.Notifications 
       .OfType<NotificationUser>() 
       .OrderByDescending(n => n.DateTime) 
       .Skip(offset) 
       .Take(limit) 
       .Include(n => n.TargetUser) 
       .Include(n => n.TargetUser.Images) 
       .ToListAsync(); 
    } 

HINWEIS: Sie müssen auch dann an jedem Ort ändern, die diese Methode von

verwendet
var x = GetNotificationUsers(skip, take); 

zu

var x = await GetNotificationUsersAsync(skip, take); 

Und diese Methode Asynchron machen und eine Aufgabe zurückkehren und