Nawigacje po relacjach
Relacje programu EF Core są definiowane przez klucze obce. Nawigacje są nakładane na klucze obce, aby zapewnić naturalny, obiektowy widok do odczytywania i manipulowania relacjami. Korzystając z nawigacji, aplikacje mogą pracować z grafami jednostek bez obaw o to, co dzieje się z wartościami klucza obcego.
Ważne
Wiele relacji nie może udostępniać nawigacji. Każdy klucz obcy może być skojarzony z co najwyżej jedną nawigacją od podmiotu zabezpieczeń do zależnego, a co najwyżej jedną nawigację z zależności od podmiotu zabezpieczeń.
Napiwek
Nie ma potrzeby tworzenia nawigacji wirtualnych, chyba że są one używane przez serwery proxy z opóźnieniem ładowania lub śledzenia zmian.
Nawigacje referencyjne
Nawigacje są dostępne w dwóch formach — odwołanie i kolekcja. Nawigacje referencyjne to proste odwołania do obiektów do innej jednostki. Reprezentują one strony "jeden" relacji jeden do wielu i jeden do jednego . Na przykład:
public Blog TheBlog { get; set; }
Nawigacje referencyjne muszą mieć zestaw, chociaż nie musi być publiczne. Nawigacje referencyjne nie powinny być automatycznie inicjowane do wartości domyślnej innej niż null; Jest to równoważne twierdzeniu, że jednostka istnieje, gdy nie.
W przypadku korzystania z typów odwołań dopuszczanych wartości null w języku C# nawigacje referencyjne muszą mieć wartość null dla opcjonalnych relacji:
public Blog? TheBlog { get; set; }
Nawigacja referencyjna dla wymaganych relacji może być dopuszczana do wartości null lub nie może być równa null.
Nawigacje po kolekcjach
Nawigacje kolekcji to wystąpienia typu kolekcji .NET; oznacza to, że dowolny typ implementujący ICollection<T>. Kolekcja zawiera wystąpienia powiązanego typu jednostki, z których może istnieć dowolna liczba. Reprezentują one strony "wiele" relacji jeden do wielu i wiele do wielu . Na przykład:
public ICollection<Post> ThePosts { get; set; }
Nawigacje kolekcji nie muszą mieć zestawu. Często inicjuje się kolekcję w tekście, usuwając w ten sposób konieczność sprawdzania, czy właściwość ma wartość null
. Na przykład:
public ICollection<Post> ThePosts { get; } = new List<Post>();
Napiwek
Nie należy przypadkowo tworzyć właściwości ciała wyrażenia, takiej jak public ICollection<Post> ThePosts => new List<Post>();
. Spowoduje to utworzenie nowego, pustego wystąpienia kolekcji przy każdym uzyskiwaniu dostępu do właściwości i dlatego będzie bezużyteczne jako nawigacja.
Typy kolekcji
Bazowe wystąpienie kolekcji musi implementować ICollection<T>metodę i musi mieć metodę roboczą Add
. Często jest używany program List<T> lub HashSet<T>. List<T>
jest wydajny w przypadku niewielkiej liczby powiązanych jednostek i utrzymuje stabilną kolejność. HashSet<T>
ma bardziej wydajne wyszukiwanie dla dużej liczby jednostek, ale nie ma stabilnej kolejności. Możesz również użyć własnej implementacji kolekcji niestandardowej.
Ważne
Kolekcja musi używać równości odwołań. Podczas tworzenia HashSet<T>
elementu nawigacji dla kolekcji upewnij się, że używasz elementu ReferenceEqualityComparer.
Tablice nie mogą być używane na potrzeby nawigacji kolekcji, ponieważ mimo że implementują ICollection<T>
metodę , Add
metoda zgłasza wyjątek po wywołaniu.
Mimo że wystąpienie kolekcji musi być wystąpieniem ICollection<T>
, kolekcja nie musi być widoczna jako taka. Na przykład często można uwidocznić nawigację jako IEnumerable<T>element , który udostępnia widok tylko do odczytu, którego nie można losowo modyfikować za pomocą kodu aplikacji. Na przykład:
public class Blog
{
public int Id { get; set; }
public IEnumerable<Post> ThePosts { get; } = new List<Post>();
}
Odmiana tego wzorca obejmuje metody manipulowania kolekcją zgodnie z potrzebami. Na przykład:
public class Blog
{
private readonly List<Post> _posts = new();
public int Id { get; set; }
public IEnumerable<Post> Posts => _posts;
public void AddPost(Post post) => _posts.Add(post);
}
Kod aplikacji nadal może rzutować ujawnioną kolekcję na obiekt ICollection<T>
, a następnie manipulować nim. Jeśli jest to problem, jednostka może zwrócić defensywną kopię kolekcji. Na przykład:
public class Blog
{
private readonly List<Post> _posts = new();
public int Id { get; set; }
public IEnumerable<Post> Posts => _posts.ToList();
public void AddPost(Post post) => _posts.Add(post);
}
Dokładnie zastanów się, czy wartość uzyskana z tego poziomu jest wystarczająco wysoka, że przewyższa obciążenie związane z tworzeniem kopii kolekcji za każdym razem, gdy uzyskuje się dostęp do nawigacji.
Napiwek
Ten ostatni wzorzec działa, ponieważ domyślnie program EF uzyskuje dostęp do kolekcji za pośrednictwem pola zapasowego. Oznacza to, że sama platforma EF dodaje i usuwa jednostki z rzeczywistej kolekcji, podczas gdy aplikacje wchodzą w interakcję tylko z defensywną kopią kolekcji.
Inicjowanie nawigacji kolekcji
Nawigacje po kolekcjach mogą być inicjowane przez typ jednostki — z niecierpliwością:
public class Blog
{
public ICollection<Post> Posts { get; } = new List<Post>();
}
Lub leniwie:
public class Blog
{
private ICollection<Post>? _posts;
public ICollection<Post> Posts => _posts ??= new List<Post>();
}
Jeśli platforma EF musi dodać jednostkę do nawigacji kolekcji, na przykład podczas wykonywania zapytania, zainicjuje kolekcję, jeśli jest obecnie null
. Utworzone wystąpienie zależy od uwidocznionego typu nawigacji.
- Jeśli nawigacja jest uwidoczniona jako
HashSet<T>
element , zostanie utworzone wystąpienieHashSet<T>
funkcji using ReferenceEqualityComparer . - W przeciwnym razie, jeśli nawigacja jest uwidoczniona jako typ betonowy z konstruktorem bez parametrów, tworzone jest wystąpienie tego typu konkretnego. Dotyczy
List<T>
to programu , ale także innych typów kolekcji, w tym niestandardowych typów kolekcji. - W przeciwnym razie, jeśli nawigacja jest uwidoczniona jako
IEnumerable<T>
,ICollection<T>
lubISet<T>
, zostanie utworzone wystąpienieHashSet<T>
użyciaReferenceEqualityComparer
. - W przeciwnym razie, jeśli nawigacja jest uwidoczniona jako
IList<T>
, zostanie utworzone wystąpienie klasyList<T>
. - W przeciwnym razie jest zgłaszany wyjątek.
Uwaga
Jeśli są używane jednostki powiadomień, w tym serwery proxy śledzenia zmian, są ObservableCollection<T> używane i ObservableHashSet<T> są używane zamiast List<T>
elementów i HashSet<T>
.
Ważne
Zgodnie z opisem w dokumentacji śledzenia zmian program EF śledzi tylko jedno wystąpienie dowolnej jednostki z daną wartością klucza. Oznacza to, że kolekcje używane jako nawigacje muszą używać semantyki równości odwołań. Typy jednostek, które nie zastępują równości obiektów, będą domyślnie uzyskiwać tę wartość. Pamiętaj, aby używać ReferenceEqualityComparer elementu podczas tworzenia elementu HashSet<T>
do użycia jako nawigacji, aby upewnić się, że działa on dla wszystkich typów jednostek.
Konfigurowanie nawigacji
Nawigacje są uwzględniane w modelu w ramach konfigurowania relacji. Oznacza to, zgodnie z konwencją lub przy użyciu HasOne
metody , HasMany
itp. w interfejsie API tworzenia modelu. Większość konfiguracji skojarzonych z nawigacjami odbywa się przez skonfigurowanie samej relacji.
Istnieją jednak pewne typy konfiguracji, które są specyficzne dla samych właściwości nawigacji, a nie są częścią ogólnej konfiguracji relacji. Ten typ konfiguracji jest wykonywany przy użyciu Navigation
metody . Aby na przykład wymusić na platformie EF dostęp do nawigacji za pośrednictwem jej właściwości, zamiast używać pola zapasowego:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.Navigation(e => e.Posts)
.UsePropertyAccessMode(PropertyAccessMode.Property);
modelBuilder.Entity<Post>()
.Navigation(e => e.Blog)
.UsePropertyAccessMode(PropertyAccessMode.Property);
}
Uwaga
Nie Navigation
można użyć wywołania do utworzenia właściwości nawigacji. Służy tylko do konfigurowania właściwości nawigacji, która została wcześniej utworzona przez zdefiniowanie relacji lub z konwencji.
Wymagane nawigacje
Jeśli relacja jest wymagana, wymagana jest nawigacja od zależnego od podmiotu zabezpieczeń, co z kolei oznacza, że właściwość klucza obcego jest niepusta. Z drugiej strony nawigacja jest opcjonalna, jeśli klucz obcy jest dopuszczany do wartości null, a zatem relacja jest opcjonalna.
Nawigacje odwołania od podmiotu zabezpieczeń do zależności są różne. W większości przypadków jednostka główna może zawsze istnieć bez żadnych jednostek zależnych. Oznacza to, że wymagana relacja nie wskazuje, że zawsze będzie istnieć co najmniej jedna jednostka zależna. W modelu EF nie ma możliwości, a także nie ma standardowego sposobu w relacyjnej bazie danych, aby upewnić się, że podmiot zabezpieczeń jest skojarzony z określoną liczbą zależności. Jeśli jest to konieczne, należy zaimplementować ją w logice aplikacji (biznesowej).
Istnieje jeden wyjątek od tej reguły — gdy podmiot zabezpieczeń i typy zależne współużytkują tę samą tabelę w relacyjnej bazie danych lub zawarte w dokumencie. Może się to zdarzyć w przypadku typów należących do użytkownika lub typów innych niż należące do tej samej tabeli. W takim przypadku właściwość nawigacji od podmiotu zabezpieczeń do zależności może być oznaczona jako wymagana, co oznacza, że zależność musi istnieć.
Konfiguracja właściwości nawigacji zgodnie z wymaganiami jest wykonywana Navigation
przy użyciu metody . Na przykład:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.Navigation(e => e.BlogHeader)
.IsRequired();
}