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

Ten artykuł zawiera omówienie sposobu zarządzania relacjami między jednostkami przez program 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 zdefiniowany 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 żadnej 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ł .

Tabele działu i kursu

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 wielokrotność 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 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 w obu kierunkach i zarządzanie nimi, zwracając obiekt odwołania (jeśli wielokrotność to jeden lub zero lub jeden) lub kolekcja (jeśli wielokrotność jest wiele). Możesz również wybrać nawigację jednokierunkową, w takim przypadku należy zdefiniować właściwość nawigacji tylko na jednym z typów, które uczestniczą w relacji, a nie na obu.

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. Używanie kluczy obcych jest jeszcze ważniejsze podczas pracy z odłączonymi jednostkami. Pamiętaj, ż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, ef będzie używać niezależnego skojarzenia do zarządzania taką relacją wiele-do-wielu.   

Na poniższej ilustracji przedstawiono model koncepcyjny, który został utworzony za pomocą projektanta platformy 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 .

Tabele działu i kursu z właściwościami nawigacji

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 ze stanem EntityState.Unchanged zmieni się na EntityState.Modified. W niezależnej relacji zmiana relacji nie aktualizuje 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, tworzenia lub modyfikowania 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 null. Należy pamiętać, że właściwość klucza obcego musi mieć wartość null.

    course.DepartmentID = null;
    

    Uwaga

    Jeśli odwołanie jest w stanie dodanym (w tym przykładzie obiekt kursu), właściwość nawigacji referencyjnej nie zostanie zsynchronizowana z wartościami klucza nowego obiektu do momentu wywołania funkcji 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, gdy tylko ustawisz relację, 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 a elementem department. 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 null. 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, czyli opartego 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. Można na przykład dodać obiekt typu Course do kolekcji department.Courses . Ta operacja tworzy relację między określonym kursem a konkretnym departmentelementem . 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);
    
  • Za pomocą 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żna jej używać 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 jeśli aktualizujesz relację (nie tylko dodasz), musisz usunąć starą relację po dodaniu nowej:

    ((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 struktura 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 główny, który jest ł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 przypadku kluczy obcych, jak i niezależnych skojarzeń kontrole współbieżności są oparte na kluczach jednostek i innych właściwościach jednostki zdefiniowanych w modelu. W przypadku tworzenia modelu za pomocą narzędzia EF Designer 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ć, że właściwość powinna być sprawdzana pod kątem współbieżności. W danej klasie można mieć tylko jedną 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, które obejmuje nakładające się klucze, zalecamy zmodyfikowanie wartości klucza obcego zamiast przy użyciu odwołań do obiektu.