Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Abgeschlossenes Projekt herunterladen
In diesem Abschnitt werden einige Details dazu beschrieben, wie EF verwandte Entitäten lädt und wie Sie Zirkelnavigationseigenschaften in Ihren Modellklassen behandeln. (Dieser Abschnitt enthält Hintergrundkenntnisse und ist nicht erforderlich, um das Lernprogramm abzuschließen. Wenn Es Ihnen lieber ist, fahren Sie mit Teil 5 fort.)
Eager Loading versus Lazy Loading
Bei der Verwendung von EF mit einer relationalen Datenbank ist es wichtig zu verstehen, wie EF verwandte Daten lädt.
Es ist auch hilfreich, die VON EF generierten SQL-Abfragen anzuzeigen. Um die SQL-Datei zu verfolgen, fügen Sie dem BookServiceContext Konstruktor die folgende Codezeile hinzu:
public BookServiceContext() : base("name=BookServiceContext")
{
// New code:
this.Database.Log = s => System.Diagnostics.Debug.WriteLine(s);
}
Wenn Sie eine GET-Anforderung an /api/books senden, wird JSON wie folgt zurückgegeben:
[
{
"BookId": 1,
"Title": "Pride and Prejudice",
"Year": 1813,
"Price": 9.99,
"Genre": "Comedy of manners",
"AuthorId": 1,
"Author": null
},
...
Sie können sehen, dass die Author-Eigenschaft null ist, obwohl das Buch eine gültige AuthorId enthält. Das liegt daran, dass EF die zugehörigen Author-Entitäten nicht lädt. Das Ablaufverfolgungsprotokoll der SQL-Abfrage bestätigt Folgendes:
SELECT
[Extent1].[BookId] AS [BookId],
[Extent1].[Title] AS [Title],
[Extent1].[Year] AS [Year],
[Extent1].[Price] AS [Price],
[Extent1].[Genre] AS [Genre],
[Extent1].[AuthorId] AS [AuthorId]
FROM [dbo].[Books] AS [Extent1]
Die SELECT-Anweisung stammt aus der Tabelle "Books" und verweist nicht auf die Tabelle "Author".
Dies ist die Methode in der BooksController Klasse, die die Liste der Bücher zurückgibt.
public IQueryable<Book> GetBooks()
{
return db.Books;
}
Sehen wir uns an, wie wir den Autor als Teil der JSON-Daten zurückgeben können. Es gibt drei Möglichkeiten zum Laden verwandter Daten in Entity Framework: eifriges Laden, faules Laden und explizites Laden. Es gibt Kompromisse mit den einzelnen Techniken, daher ist es wichtig zu verstehen, wie sie funktionieren.
Begierdiges Laden
Beim eifrigen Laden lädt EF verwandte Entitäten als Teil der anfänglichen Datenbankabfrage. Verwenden Sie die System.Data.Entity.Include-Erweiterungsmethode, um Eager Loading durchzuführen.
public IQueryable<Book> GetBooks()
{
return db.Books
// new code:
.Include(b => b.Author);
}
Dadurch wird EF aufgefordert, die Author-Daten in die Abfrage einzuschließen. Wenn Sie diese Änderung vornehmen und die App ausführen, sieht nun die JSON-Daten wie folgt aus:
[
{
"BookId": 1,
"Title": "Pride and Prejudice",
"Year": 1813,
"Price": 9.99,
"Genre": "Comedy of manners",
"AuthorId": 1,
"Author": {
"AuthorId": 1,
"Name": "Jane Austen"
}
},
...
Das Ablaufverfolgungsprotokoll zeigt, dass EF eine Verknüpfung der Tabellen "Buch" und "Autor" ausgeführt hat.
SELECT
[Extent1].[BookId] AS [BookId],
[Extent1].[Title] AS [Title],
[Extent1].[Year] AS [Year],
[Extent1].[Price] AS [Price],
[Extent1].[Genre] AS [Genre],
[Extent1].[AuthorId] AS [AuthorId],
[Extent2].[AuthorId] AS [AuthorId1],
[Extent2].[Name] AS [Name]
FROM [dbo].[Books] AS [Extent1]
INNER JOIN [dbo].[Authors] AS [Extent2] ON [Extent1].[AuthorId] = [Extent2].[AuthorId]
Faules Laden
Beim Lazy Loading lädt EF automatisch eine verwandte Entität, wenn die Navigations-Eigenschaft für diese Entität dereferenziert wird. Um Lazy Loading zu aktivieren, deklarieren Sie die Navigationseigenschaft als virtuell. Beispiel: In der Book-Klasse:
public class Book
{
// (Other properties)
// Virtual navigation property
public virtual Author Author { get; set; }
}
Betrachten Sie nun den folgenden Code:
var books = db.Books.ToList(); // Does not load authors
var author = books[0].Author; // Loads the author for books[0]
Wenn Lazy Loading aktiviert ist, bewirkt der Zugriff auf die Author-Eigenschaft von books[0], dass EF die Datenbank nach dem Autor abfragt.
Lazy Loading erfordert mehrere Datenbankabfragen, da EF bei jedem Abruf einer zugehörigen Entität eine Anfrage sendet. Im Allgemeinen möchten Sie das faule Laden für Objekte deaktivieren, die Sie serialisieren. Der Serialisierer muss alle Eigenschaften des Modells lesen, wodurch das Laden der zugehörigen Entitäten ausgelöst wird. Hier sind z. B. die SQL-Abfragen, wenn EF die Liste der Bücher mit aktiviertem Lazy Loading serialisiert. Sie können sehen, dass EF drei separate Abfragen für die drei Autoren vorgibt.
SELECT
[Extent1].[BookId] AS [BookId],
[Extent1].[Title] AS [Title],
[Extent1].[Year] AS [Year],
[Extent1].[Price] AS [Price],
[Extent1].[Genre] AS [Genre],
[Extent1].[AuthorId] AS [AuthorId]
FROM [dbo].[Books] AS [Extent1]
SELECT
[Extent1].[AuthorId] AS [AuthorId],
[Extent1].[Name] AS [Name]
FROM [dbo].[Authors] AS [Extent1]
WHERE [Extent1].[AuthorId] = @EntityKeyValue1
SELECT
[Extent1].[AuthorId] AS [AuthorId],
[Extent1].[Name] AS [Name]
FROM [dbo].[Authors] AS [Extent1]
WHERE [Extent1].[AuthorId] = @EntityKeyValue1
SELECT
[Extent1].[AuthorId] AS [AuthorId],
[Extent1].[Name] AS [Name]
FROM [dbo].[Authors] AS [Extent1]
WHERE [Extent1].[AuthorId] = @EntityKeyValue1
Es gibt immer noch Zeiten, in denen Sie Lazy Loading verwenden möchten. Eager Loading kann dazu führen, dass EF eine sehr komplexe Verknüpfung generiert. Oder Sie benötigen möglicherweise verwandte Entitäten für eine kleine Teilmenge der Daten, und Lazy Loading wäre effizienter.
Eine Möglichkeit, Serialisierungsprobleme zu vermeiden, besteht darin, Datenübertragungsobjekte (DATA Transfer Objects, DTOs) anstelle von Entitätsobjekten zu serialisieren. Ich zeige diesen Ansatz später im Artikel.
Explizites Laden
Das explizite Laden ähnelt dem faulen Laden, mit der Ausnahme, dass Sie die zugehörigen Daten explizit im Code abrufen. dies geschieht nicht automatisch, wenn Sie auf eine Navigationseigenschaft zugreifen. Das explizite Laden bietet Ihnen mehr Kontrolle darüber, wann verwandte Daten geladen werden sollen, erfordert jedoch zusätzlichen Code. Weitere Informationen zum expliziten Laden finden Sie unter Laden verwandter Entitäten.
Navigationseigenschaften und Zirkelbezüge
Als ich die Book- und Author-Modelle definiert habe, habe ich eine Navigationseigenschaft für die Book Klasse für die Book-Author Beziehung definiert, aber ich habe keine Navigationseigenschaft in der anderen Richtung definiert.
Was geschieht, wenn Sie der Klasse die entsprechende Navigationseigenschaft Author hinzufügen?
public class Author
{
public int AuthorId { get; set; }
[Required]
public string Name { get; set; }
public ICollection<Book> Books { get; set; }
}
Leider verursacht dies ein Problem, wenn Sie die Modelle serialisieren. Wenn Sie die zugehörigen Daten laden, wird ein Zirkelobjektdiagramm erstellt.
Wenn der JSON- oder XML-Formatierer versucht, das Diagramm zu serialisieren, löst er eine Ausnahme aus. Die beiden Formatierer lösen unterschiedliche Ausnahmemeldungen aus. Hier ist ein Beispiel für den JSON-Formatierer:
{
"Message": "An error has occurred.",
"ExceptionMessage": "The 'ObjectContent`1' type failed to serialize the response body for content type
'application/json; charset=utf-8'.",
"ExceptionType": "System.InvalidOperationException",
"StackTrace": null,
"InnerException": {
"Message": "An error has occurred.",
"ExceptionMessage": "Self referencing loop detected with type 'BookService.Models.Book'.
Path '[0].Author.Books'.",
"ExceptionType": "Newtonsoft.Json.JsonSerializationException",
"StackTrace": "..."
}
}
Dies ist der XML-Formatierer:
<Error>
<Message>An error has occurred.</Message>
<ExceptionMessage>The 'ObjectContent`1' type failed to serialize the response body for content type
'application/xml; charset=utf-8'.</ExceptionMessage>
<ExceptionType>System.InvalidOperationException</ExceptionType>
<StackTrace />
<InnerException>
<Message>An error has occurred.</Message>
<ExceptionMessage>Object graph for type 'BookService.Models.Author' contains cycles and cannot be
serialized if reference tracking is disabled.</ExceptionMessage>
<ExceptionType>System.Runtime.Serialization.SerializationException</ExceptionType>
<StackTrace> ... </StackTrace>
</InnerException>
</Error>
Eine Lösung ist die Verwendung von DTOs, die ich im nächsten Abschnitt beschreibt. Alternativ können Sie die JSON- und XML-Formatierer für die Behandlung von Diagrammzyklen konfigurieren. Weitere Informationen finden Sie unter Behandlung von zirkulären Objektverweisen.
Für dieses Lernprogramm benötigen Sie Author.Book die Navigationseigenschaft nicht, sodass Sie diese weglassen können.