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ą połączeń lub tabelą wiążącą), której klucz podstawowy składa się z kluczy obcych 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ł .

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, które opisują typ jednostki i wielość typu (jeden, zero-jeden lub wiele) dla dwóch jednostek w tej relacji. Relacja może być regulowana przez ograniczenie referencyjne, które opisuje, która strona relacji pełni rolę główną, a która 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 modelu właściwości, które odwzorowują 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 relacjami 1-do-1 lub 1-do-0..1, nie ma oddzielnej kolumny klucza obcego, właściwość klucza głównego stanowi 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 za pomocą tabeli łączącej zawierającej tylko klucze obce, EF użyje niezależnego skojarzenia do zarządzania taką relacją wiele-do-wielu.   

Na poniższej ilustracji przedstawiono model koncepcyjny 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 zmienia się z EntityState.Unchanged 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ć dowolnej z metod do zmiany, utworzenia lub zmodyfikowania relacji. W przypadku skojarzeń niezależnych 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 / Notatka

    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 a department. Jeśli obiekty są przyłączone do kontekstu, course jest również dodawane do kolekcji department.Courses, a odpowiednia właściwość klucza obcego w obiekcie course 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, trzeba załadować powiązany element końcowy przed ustawieniem go na null. Przykład:

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

    Począwszy od Entity Framework 5.0, który jest oparty na platformie .NET 4.5, można ustawić wartość relacji na null bez ładowania powiązanego końca relacji. 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 do 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, referencja działu i właściwość klucza obcego w obiekcie kurs zostaną ustawione na odpowiednią wartość department.

    department.Courses.Add(newCourse);
    
  • Używając metody ChangeRelationshipState do zmiany stanu określonej relacji między dwoma obiektami encji. 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). ** Aby użyć tej metody, należy przejść na poziom ObjectContext, jak pokazano w poniższym przykładzie.
    W poniższym przykładzie istnieje relacja typu wiele do wielu między instruktorami i kursami. Wywołanie metody ChangeRelationshipState i przekazanie parametru EntityState.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 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 / Notatka

W skojarzeniu klucza obcego, gdy ładujesz powiązany koniec obiektu zależnego, powiązany obiekt zostanie załadowany na podstawie wartości klucza obcego tego zależnego obiektu, 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 zapytany w oparciu o wartość klucza obcego, który 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 tworzenia modelu przy użyciu projektanta EF ustaw atrybut ConcurrencyMode na 'fixed', aby określić, czy właściwość ma być sprawdzana pod kątem konkurencyjnoś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, które obejmuje nakładające się klucze, zalecamy zamiast korzystania z odwołań do obiektów, zmodyfikowanie wartości klucza obcego.