Varlık İlişkilerini İşlemeHandling Entity Relations

, Mike te sonby Mike Wasson

Tamamlanmış projeyi indirDownload Completed Project

Bu bölümde, EF 'in ilgili varlıkları nasıl yüklediği ve model sınıflarınızda dairesel gezinti özelliklerinin nasıl işleneceği hakkında bazı ayrıntılar açıklanmaktadır.This section describes some details of how EF loads related entities, and how to handle circular navigation properties in your model classes. (Bu bölüm, arka plan bilgisi sağlar ve öğreticiyi tamamlaması için gerekli değildir.(This section provides background knowledge, and is not required to complete the tutorial. İsterseniz Bölüm 5' e atlayın..)If you prefer, skip to Part 5..)

Ekip yükleme ve geç yükleme karşılaştırmasıEager Loading versus Lazy Loading

Bir ilişkisel veritabanıyla EF kullanırken, EF 'in ilgili verileri nasıl yüklediğini anlamak önemlidir.When using EF with a relational database, it's important to understand how EF loads related data.

Ayrıca, EF 'in oluşturduğu SQL sorgularını görmek de yararlı olur.It's also useful to see the SQL queries that EF generates. SQL 'i izlemek için aşağıdaki kod satırını BookServiceContext oluşturucusuna ekleyin:To trace the SQL, add the following line of code to the BookServiceContext constructor:

public BookServiceContext() : base("name=BookServiceContext")
{
    // New code:
    this.Database.Log = s => System.Diagnostics.Debug.WriteLine(s);
}

/Api/Books 'a bir GET isteği gönderirseniz, aşağıdaki gibi JSON döndürür:If you send a GET request to /api/books, it returns JSON like the following:

[
  {
    "BookId": 1,
    "Title": "Pride and Prejudice",
    "Year": 1813,
    "Price": 9.99,
    "Genre": "Comedy of manners",
    "AuthorId": 1,
    "Author": null
  },
  ...

Kitap geçerli bir AuthorId içeriyor olsa da, Author özelliğinin null olduğunu görebilirsiniz.You can see that the Author property is null, even though the book contains a valid AuthorId. Bunun nedeni, EF 'in ilgili yazar varlıklarını yüklememalarıdır.That's because EF is not loading the related Author entities. SQL sorgusunun izleme günlüğü şunları doğrular:The trace log of the SQL query confirms this:

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 deyimleri kitaplar tablosundan geçer ve yazar tablosuna başvurmuyor.The SELECT statement takes from the Books table, and does not reference the Author table.

Başvuru için, kitap listesini döndüren BooksController sınıfında yöntemi aşağıda verilmiştir.For reference, here is the method in the BooksController class that returns the list of books.

public IQueryable<Book> GetBooks()
{
    return db.Books;
}

Ayrıca, JSON verilerinin bir parçası olarak yazarın nasıl dönediğimiz hakkında bilgi vereceğiz.Let's see how we can return the Author as part of the JSON data. Entity Framework içinde ilgili verileri yüklemek için üç yol vardır: Eager yükleme, geç yükleme ve açık yükleme.There are three ways to load related data in Entity Framework: eager loading, lazy loading, and explicit loading. Her teknikle ilgili denge vardır. bu nedenle, nasıl çalıştığını anlamak önemlidir.There are trade-offs with each technique, so it's important to understand how they work.

Ekip yüklemeEager Loading

Bu yüklemede, EF ile ilgili varlıkları ilk veritabanı sorgusunun bir parçası olarak yükler.With eager loading, EF loads related entities as part of the initial database query. Eager yükleme işlemini gerçekleştirmek için System. Data. Entity. Include genişletme yöntemini kullanın.To perform eager loading, use the System.Data.Entity.Include extension method.

public IQueryable<Book> GetBooks()
{
    return db.Books
        // new code:
        .Include(b => b.Author);
}

Bu, sorguya yazar verilerini ekleme hakkında söyleme söyler.This tells EF to include the Author data in the query. Bu değişikliği yapar ve uygulamayı çalıştırırsanız, şu anda JSON verileri şöyle görünür:If you make this change and run the app, now the JSON data looks like this:

[
  {
    "BookId": 1,
    "Title": "Pride and Prejudice",
    "Year": 1813,
    "Price": 9.99,
    "Genre": "Comedy of manners",
    "AuthorId": 1,
    "Author": {
      "AuthorId": 1,
      "Name": "Jane Austen"
    }
  },
  ...

İzleme günlüğünde, EF 'in kitap ve yazar tablolarında bir JOIN gerçekleştirdiği gösterilmektedir.The trace log shows that EF performed a join on the Book and Author tables.

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]

Geç yüklemeLazy Loading

Yavaş yükleme ile ilgili varlık için gezinti özelliği başvurulduğunu EF, ilgili bir varlığı otomatik olarak yükler.With lazy loading, EF automatically loads a related entity when the navigation property for that entity is dereferenced. Yavaş yüklemeyi etkinleştirmek için, gezinti özelliğini sanal yapın.To enable lazy loading, make the navigation property virtual. Örneğin, Book sınıfında:For example, in the Book class:

public class Book
{
    // (Other properties)

    // Virtual navigation property
    public virtual Author Author { get; set; }
}

Şimdi aşağıdaki kodu göz önünde bulundurun:Now consider the following code:

var books = db.Books.ToList();  // Does not load authors
var author = books[0].Author;   // Loads the author for books[0]

Yavaş yükleme etkinleştirildiğinde, books[0] Author özelliğine erişmek, yazma için veritabanını sorgulamak için EF 'e neden olur.When lazy loading is enabled, accessing the Author property on books[0] causes EF to query the database for the author.

EF, her ilgili varlığı aldığında bir sorgu gönderdiğinden, yavaş yükleme birden çok veritabanı gezilerini gerektirir.Lazy loading requires multiple database trips, because EF sends a query each time it retrieves a related entity. Genellikle, seri hale getirmek istediğiniz nesneler için yavaş yüklemeyi devre dışı istersiniz.Generally, you want lazy loading disabled for objects that you serialize. Seri hale getirici modeldeki tüm özellikleri okumalı ve bu da ilgili varlıkları yüklemeyi tetikler.The serializer has to read all of the properties on the model, which triggers loading the related entities. Örneğin, yavaş yükleme özelliği etkin olan kitaplar listesini seri hale geldiğinde SQL sorguları burada açıklanmıştır.For example, here are the SQL queries when EF serializes the list of books with lazy loading enabled. EF 'in üç yazar için üç ayrı sorgu yaptığı hakkında bilgi alabilirsiniz.You can see that EF makes three separate queries for the three authors.

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

Yavaş yükleme kullanmak isteyebileceğiniz zamanlar hala vardır.There are still times when you might want to use lazy loading. Eager yüklemesi, EF 'in çok karmaşık bir birleşme oluşturmasına neden olabilir.Eager loading can cause EF to generate a very complex join. Ya da verilerin küçük bir alt kümesi için ilgili varlıklara ihtiyacınız olabilir ve yavaş yükleme daha verimli olacaktır.Or you might need related entities for a small subset of the data, and lazy loading would be more efficient.

Serileştirme sorunlarından kaçınmak için bir yol, varlık nesneleri yerine veri aktarımı nesneleri (DTOs) serileştirilmenin bir yoludur.One way to avoid serialization problems is to serialize data transfer objects (DTOs) instead of entity objects. Bu yaklaşımı makalenin ilerleyen kısımlarında göstereceğiz.I'll show this approach later in the article.

Açık yüklemeExplicit Loading

Açık yükleme, kod içinde ilgili verileri açıkça almanız dışında, yavaş yüklemeye benzer. bir gezinti özelliğine eriştiğinizde otomatik olarak gerçekleşmez.Explicit loading is similar to lazy loading, except that you explicitly get the related data in code; it doesn't happen automatically when you access a navigation property. Açık yükleme, ilgili verilerin ne zaman yükleneceği konusunda daha fazla denetim sağlar, ancak ek kod gerektirir.Explicit loading gives you more control over when to load related data, but requires extra code. Açık yükleme hakkında daha fazla bilgi için bkz. Ilgili varlıkları yükleme.For more information about explicit loading, see Loading Related Entities.

Kitap ve yazar modellerini tanımladığımda, kitap yazarı ilişkisinin Book sınıfında bir gezinti özelliği tanımlıyorum, ancak başka bir yönde bir gezinti özelliği tanımlıyorum.When I defined the Book and Author models, I defined a navigation property on the Book class for the Book-Author relationship, but I did not define a navigation property in the other direction.

Karşılık gelen gezinti özelliğini Author sınıfa eklerseniz ne olur?What happens if you add the corresponding navigation property to the Author class?

public class Author
{
    public int AuthorId { get; set; }
    [Required]
    public string Name { get; set; }

    public ICollection<Book> Books { get; set; }
}

Ne yazık ki, modelleri seri hale alırken bir sorun oluşturur.Unfortunately, this creates a problem when you serialize the models. İlgili verileri yüklerseniz, döngüsel bir nesne grafiği oluşturur.If you load the related data, it creates a circular object graph.

JSON veya XML biçimlendiricisi grafiği serileştirmek denediğinde, bir özel durum oluşturur.When the JSON or XML formatter tries to serialize the graph, it will throw an exception. İki biçimlendirme farklı özel durum iletileri oluşturur.The two formatters throw different exception messages. JSON biçimlendiricisi için bir örnek aşağıda verilmiştir:Here is an example for the JSON formatter:

{
  "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": "..."
     }
}

XML biçimlendiricisi aşağıda verilmiştir:Here is the XML formatter:

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

Bir çözüm, sonraki bölümde tanımlandığım DTOs 'ı kullanmaktır.One solution is to use DTOs, which I describe in the next section. Alternatif olarak, JSON ve XML formatlarını grafik döngülerini işleyecek şekilde yapılandırabilirsiniz.Alternatively, you can configure the JSON and XML formatters to handle graph cycles. Daha fazla bilgi için bkz. Döngüsel nesne başvurularını işleme.For more information, see Handling Circular Object References.

Bu öğreticide, Author.Book gezinti özelliğine ihtiyacınız yoktur, bu nedenle bırakabilirsiniz.For this tutorial, you don't need the Author.Book navigation property, so you can leave it out.