Relacje, właściwości nawigacji i klucze obce

Ten artykuł zawiera omówienie sposobu zarządzania relacjami między jednostkami w programie Entity Framework. Zawiera również wskazówki dotyczące mapowania relacji i manipulowania nimi.

Relacje w programie EF

W relacyjnych bazach danych relacje (nazywane również skojarzeniami) między tabelami są definiowane za pomocą kluczy obcych. Klucz obcy (FK) to kolumna lub kombinacja kolumn używanych do ustanawiania i wymuszania połączenia między danymi w dwóch tabelach. Istnieją zazwyczaj trzy typy relacji: jeden do jednego, jeden do wielu i wiele do wielu. W relacji jeden do wielu klucz obcy jest definiowany w tabeli, która reprezentuje wiele końca relacji. Relacja wiele-do-wielu obejmuje zdefiniowanie trzeciej tabeli (nazywanej tabelą skrzyżowań lub sprzężenia), której klucz podstawowy składa się z kluczy obcych z obu powiązanych tabel. W relacji jeden do jednego klucz podstawowy działa dodatkowo jako klucz obcy i nie ma oddzielnej kolumny klucza obcego dla każdej tabeli.

Na poniższej ilustracji przedstawiono dwie tabele, które uczestniczą w relacji jeden do wielu. Tabela Course jest tabelą zależną, ponieważ zawiera kolumnę DepartmentID , która łączy ją z tabelą Dział .

Department and Course tables

W programie Entity Framework jednostka może być powiązana z innymi jednostkami za pośrednictwem skojarzenia lub relacji. Każda relacja zawiera dwa końce opisujące typ jednostki i mętność typu (jeden, zero lub jeden lub wiele) dla dwóch jednostek w tej relacji. Relacja może być zarządzana przez ograniczenie odwołań, które opisuje, który koniec relacji jest główną rolą i która jest rolą zależną.

Właściwości nawigacji umożliwiają nawigowanie po skojarzeniu między dwoma typami jednostek. Każdy obiekt może mieć właściwość nawigacji dla każdej relacji, w której uczestniczy. Właściwości nawigacji umożliwiają nawigowanie po relacjach i zarządzanie nimi w obu kierunkach, zwracanie obiektu odwołania (jeśli wielokrotność jest jedną lub zero-lub-jedną) lub kolekcją (jeśli wielokrotność jest wiele). Możesz również wybrać nawigację jednokierunkową, w takim przypadku zdefiniujesz właściwość nawigacji tylko na jednym z typów, które uczestniczą w relacji, a nie na obu tych typach.

Zaleca się uwzględnienie właściwości w modelu mapowania na klucze obce w bazie danych. W przypadku uwzględnionych właściwości klucza obcego można utworzyć lub zmienić relację, modyfikując wartość klucza obcego na obiekcie zależnym. Tego rodzaju skojarzenie jest nazywane skojarzeniem klucza obcego. Korzystanie z kluczy obcych jest jeszcze ważniejsze podczas pracy z odłączonymi jednostkami. Należy pamiętać, że podczas pracy z 1-do-1 lub 1-do-0.. 1 relacje, nie ma oddzielnej kolumny klucza obcego, właściwość klucza podstawowego działa jako klucz obcy i jest zawsze uwzględniana w modelu.

Jeśli kolumny klucza obcego nie są uwzględnione w modelu, informacje o skojarzeniu są zarządzane jako niezależny obiekt. Relacje są śledzone za pomocą odwołań do obiektów zamiast właściwości klucza obcego. Ten typ skojarzenia jest nazywany niezależnym skojarzeniem. Najczęstszym sposobem modyfikowania niezależnego skojarzenia jest zmodyfikowanie właściwości nawigacji generowanych dla każdej jednostki, która uczestniczy w skojarzeniu.

Możesz użyć jednego lub obu typów skojarzeń w modelu. Jeśli jednak masz czystą relację wiele-do-wielu, która jest połączona przez tabelę sprzężenia zawierającą tylko klucze obce, program EF będzie używać niezależnego skojarzenia do zarządzania taką relacją wiele-do-wielu.   

Na poniższej ilustracji przedstawiono model koncepcyjny utworzony za pomocą Projektant Entity Framework. Model zawiera dwie jednostki, które uczestniczą w relacji jeden do wielu. Obie jednostki mają właściwości nawigacji. Kurs jest jednostką zależną i ma zdefiniowaną właściwość klucza obcego DepartmentID .

Department and Course tables with navigation properties

Poniższy fragment kodu przedstawia ten sam model, który został utworzony za pomocą funkcji Code First.

public class Course
{
  public int CourseID { get; set; }
  public string Title { get; set; }
  public int Credits { get; set; }
  public int DepartmentID { get; set; }
  public virtual Department Department { get; set; }
}

public class Department
{
   public Department()
   {
     this.Courses = new HashSet<Course>();
   }  
   public int DepartmentID { get; set; }
   public string Name { get; set; }
   public decimal Budget { get; set; }
   public DateTime StartDate { get; set; }
   public int? Administrator {get ; set; }
   public virtual ICollection<Course> Courses { get; set; }
}

Konfigurowanie lub mapowanie relacji

W pozostałej części tej strony opisano sposób uzyskiwania dostępu do danych i manipulowania nimi przy użyciu relacji. Aby uzyskać informacje na temat konfigurowania relacji w modelu, zobacz następujące strony.

Tworzenie i modyfikowanie relacji

W skojarzeniu klucza obcego po zmianie relacji stan obiektu zależnego zmieni EntityState.Unchanged się na EntityState.Modified. W relacji niezależnej zmiana relacji nie powoduje zaktualizowania stanu obiektu zależnego.

W poniższych przykładach pokazano, jak używać właściwości klucza obcego i właściwości nawigacji w celu skojarzenia powiązanych obiektów. W przypadku skojarzeń kluczy obcych można użyć metody do zmiany, utworzenia lub zmodyfikowania relacji. W przypadku niezależnych skojarzeń nie można użyć właściwości klucza obcego.

  • Przypisując nową wartość do właściwości klucza obcego, jak w poniższym przykładzie.

    course.DepartmentID = newCourse.DepartmentID;
    
  • Poniższy kod usuwa relację, ustawiając klucz obcy na wartość null. Należy pamiętać, że właściwość klucza obcego musi mieć wartość null.

    course.DepartmentID = null;
    

    Uwaga

    Jeśli odwołanie znajduje się w stanie dodanym (w tym przykładzie obiekt kursu), właściwość nawigacji referencyjnej nie zostanie zsynchronizowana z wartościami klucza nowego obiektu, dopóki nie zostanie wywołana funkcja SaveChanges. Synchronizacja nie występuje, ponieważ kontekst obiektu nie zawiera stałych kluczy dla dodanych obiektów, dopóki nie zostaną zapisane. Jeśli musisz mieć nowe obiekty w pełni zsynchronizowane zaraz po ustawieniu relacji, użyj jednej z następujących metod.*

  • Przypisując nowy obiekt do właściwości nawigacji. Poniższy kod tworzy relację między kursem departmenta . Jeśli obiekty są dołączone do kontekstu, course element jest również dodawany do department.Courses kolekcji, a odpowiednia właściwość klucza obcego w course obiekcie jest ustawiona na wartość właściwości klucza działu.

    course.Department = department;
    
  • Aby usunąć relację, ustaw właściwość nawigacji na nullwartość . Jeśli pracujesz z programem Entity Framework opartym na platformie .NET 4.0, należy załadować powiązany koniec przed ustawieniem wartości null. Przykład:

    context.Entry(course).Reference(c => c.Department).Load();
    course.Department = null;
    

    Począwszy od programu Entity Framework 5.0, który jest oparty na platformie .NET 4.5, można ustawić relację na wartość null bez ładowania powiązanego końca. Możesz również ustawić bieżącą wartość na null przy użyciu następującej metody.

    context.Entry(course).Reference(c => c.Department).CurrentValue = null;
    
  • Usuwając lub dodając obiekt w kolekcji jednostek. Na przykład można dodać obiekt typu Course do kolekcji department.Courses . Ta operacja tworzy relację między określonym kursem a określonym department. Jeśli obiekty są dołączone do kontekstu, odwołanie działu i właściwość klucza obcego w obiekcie kursu zostanie ustawiona na odpowiednią departmentwartość .

    department.Courses.Add(newCourse);
    
  • Używając ChangeRelationshipState metody , aby zmienić stan określonej relacji między dwoma obiektami jednostki. Ta metoda jest najczęściej używana podczas pracy z aplikacjami N-warstwowymi i niezależnym skojarzeniem (nie może być używana ze skojarzeniem klucza obcego). Ponadto, aby użyć tej metody, należy wyświetlić listę rozwijaną na ObjectContext, jak pokazano w poniższym przykładzie.
    W poniższym przykładzie istnieje relacja wiele do wielu między instruktorami i kursami. Wywołanie metody i przekazanie parametru ChangeRelationshipStateEntityState.Added informuje SchoolContext , że relacja została dodana między dwoma obiektami:

    
    ((IObjectContextAdapter)context).ObjectContext.
      ObjectStateManager.
      ChangeRelationshipState(course, instructor, c => c.Instructor, EntityState.Added);
    

    Należy pamiętać, że w przypadku aktualizowania (nie tylko dodawania) relacji należy usunąć starą relację po dodaniu nowej relacji:

    ((IObjectContextAdapter)context).ObjectContext.
      ObjectStateManager.
      ChangeRelationshipState(course, oldInstructor, c => c.Instructor, EntityState.Deleted);
    

Synchronizowanie zmian między kluczami obcymi i właściwościami nawigacji

Po zmianie relacji obiektów dołączonych do kontekstu przy użyciu jednej z metod opisanych powyżej platforma Entity Framework musi zachować zsynchronizowanie kluczy obcych, odwołań i kolekcji. Program Entity Framework automatycznie zarządza tą synchronizacją (znaną również jako poprawka relacji) dla jednostek POCO z serwerami proxy. Aby uzyskać więcej informacji, zobacz Praca z serwerami proxy.

Jeśli używasz jednostek POCO bez serwerów proxy, musisz upewnić się, że metoda DetectChanges jest wywoływana w celu zsynchronizowania powiązanych obiektów w kontekście. Należy pamiętać, że następujące interfejsy API automatycznie wyzwalają wywołanie DetectChanges .

  • DbSet.Add
  • DbSet.AddRange
  • DbSet.Remove
  • DbSet.RemoveRange
  • DbSet.Find
  • DbSet.Local
  • DbContext.SaveChanges
  • DbSet.Attach
  • DbContext.GetValidationErrors
  • DbContext.Entry
  • DbChangeTracker.Entries
  • Wykonywanie zapytania LINQ względem elementu DbSet

W programie Entity Framework często używasz właściwości nawigacji do ładowania jednostek powiązanych z zwróconą jednostką przez zdefiniowane skojarzenie. Aby uzyskać więcej informacji, zobacz Ładowanie powiązanych obiektów.

Uwaga

W skojarzeniu klucza obcego po załadowaniu powiązanego końca obiektu zależnego powiązany obiekt zostanie załadowany na podstawie wartości klucza obcego zależnego, który jest obecnie w pamięci:

    // Get the course where currently DepartmentID = 2.
    Course course = context.Courses.First(c => c.DepartmentID == 2);

    // Use DepartmentID foreign key property
    // to change the association.
    course.DepartmentID = 3;

    // Load the related Department where DepartmentID = 3
    context.Entry(course).Reference(c => c.Department).Load();

W niezależnym skojarzeniu powiązany koniec obiektu zależnego jest odpytywane na podstawie wartości klucza obcego, która znajduje się obecnie w bazie danych. Jeśli jednak relacja została zmodyfikowana, a właściwość odwołania względem obiektu zależnego wskazuje inny obiekt podmiotu głównego załadowany w kontekście obiektu, program Entity Framework spróbuje utworzyć relację zgodnie z definicją na kliencie.

Zarządzanie współbieżnością

Zarówno w skojarzeniach klucza obcego, jak i niezależnych, kontrole współbieżności są oparte na kluczach jednostki i innych właściwościach jednostki zdefiniowanych w modelu. W przypadku używania Projektant EF do utworzenia modelu ustaw ConcurrencyMode atrybut na stały, aby określić, że właściwość powinna być sprawdzana pod kątem współbieżności. W przypadku używania funkcji Code First do zdefiniowania modelu użyj ConcurrencyCheck adnotacji we właściwościach, które mają być sprawdzane pod kątem współbieżności. Podczas pracy z funkcją Code First można również użyć TimeStamp adnotacji, aby określić, czy właściwość powinna być sprawdzana pod kątem współbieżności. W danej klasie może znajdować się tylko jedna właściwość znacznika czasu. Code First mapuje tę właściwość na pole niepuste w bazie danych.

Zalecamy, aby zawsze używać skojarzenia klucza obcego podczas pracy z jednostkami uczestniczącymi w sprawdzaniu współbieżności i rozwiązywaniu problemów.

Aby uzyskać więcej informacji, zobacz Obsługa konfliktów współbieżności.

Praca z nakładającymi się kluczami

Nakładające się klucze to klucze złożone, w których niektóre właściwości klucza są również częścią innego klucza w jednostce. Nie można mieć nakładających się kluczy w niezależnym skojarzeniu. Aby zmienić skojarzenie klucza obcego obejmujące nakładające się klucze, zalecamy zmodyfikowanie wartości klucza obcego zamiast przy użyciu odwołań do obiektów.