Zmiany powodujące niezgodność w programie EF Core 6.0
Następujące zmiany interfejsu API i zachowania mogą powodować przerwanie aktualizacji istniejących aplikacji do programu EF Core 6.0.
Struktura docelowa
Program EF Core 6.0 jest przeznaczony dla platformy .NET 6. Aplikacje przeznaczone dla starszych wersji platform .NET, .NET Core i .NET Framework będą musiały być przeznaczone dla platformy .NET 6, aby korzystały z platformy EF Core 6.0.
Podsumowanie
* Te zmiany są szczególnie interesujące dla autorów dostawców i rozszerzeń bazy danych.
Zmiany o dużym wpływie
Zagnieżdżone opcjonalne zależności współużytkują tabelę i bez wymaganych właściwości są niedozwolone
Problem ze śledzeniem nr 24558
Stare zachowanie
Modele z zagnieżdżonym opcjonalnymi zależnościami współużytkujące tabelę i bez wymaganych właściwości były dozwolone, ale mogą spowodować utratę danych podczas wykonywania zapytań dotyczących danych, a następnie zapisywania ich ponownie. Rozważmy na przykład następujący model:
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
public ContactInfo ContactInfo { get; set; }
}
[Owned]
public class ContactInfo
{
public string Phone { get; set; }
public Address Address { get; set; }
}
[Owned]
public class Address
{
public string House { get; set; }
public string Street { get; set; }
public string City { get; set; }
public string Postcode { get; set; }
}
Żadna z właściwości w pliku ContactInfo
lub Address
nie jest wymagana, a wszystkie te typy jednostek są mapowane na tę samą tabelę. Reguły dla opcjonalnych zależności (w przeciwieństwie do wymaganych zależności) mówią, że jeśli wszystkie kolumny dla ContactInfo
parametru ContactInfo
mają wartość null, podczas wykonywania zapytań dotyczących właściciela Customer
nie zostanie utworzone żadne wystąpienie klasy . Oznacza to jednak również, że żadne wystąpienie elementu Address
nie zostanie utworzone, nawet jeśli Address
kolumny nie mają wartości null.
Nowe zachowanie
Próba użycia tego modelu spowoduje teraz zgłoszenie następującego wyjątku:
System.InvalidOperationException: Typ jednostki "ContactInfo" jest opcjonalnym zależnym od udostępniania tabel i zawierającym inne elementy zależne bez żadnej wymaganej właściwości innej niż współdzielona w celu określenia, czy jednostka istnieje. Jeśli wszystkie właściwości dopuszczane do wartości null zawierają wartość null w bazie danych, wystąpienie obiektu nie zostanie utworzone w zapytaniu, co spowoduje utratę wartości zależnych zagnieżdżonych. Dodaj wymaganą właściwość, aby utworzyć wystąpienia z wartościami null dla innych właściwości lub oznaczyć nawigację przychodzącą zgodnie z wymaganiami, aby zawsze utworzyć wystąpienie.
Zapobiega to utracie danych podczas wykonywania zapytań i zapisywania danych.
Dlaczego
Używanie modeli z zagnieżdżonym opcjonalnymi zależnościami współużytkowania tabeli i bez wymaganych właściwości często powodowało dyskretną utratę danych.
Środki zaradcze
Unikaj używania opcjonalnych zależności współużytkowania tabeli i bez wymaganych właściwości. Istnieją trzy proste sposoby, aby to zrobić:
Ustaw wymagane zależności. Oznacza to, że jednostka zależna zawsze będzie mieć wartość po wykonaniu zapytania, nawet jeśli wszystkie jej właściwości mają wartość null. Na przykład:
public class Customer { public int Id { get; set; } public string Name { get; set; } [Required] public Address Address { get; set; } }
Lub:
modelBuilder.Entity<Customer>( b => { b.OwnsOne(e => e.Address); b.Navigation(e => e.Address).IsRequired(); });
Upewnij się, że zależne zawiera co najmniej jedną wymaganą właściwość.
Mapowanie opcjonalnych zależności na własną tabelę zamiast udostępniania tabeli podmiotowi zabezpieczeń. Na przykład:
modelBuilder.Entity<Customer>( b => { b.ToTable("Customers"); b.OwnsOne(e => e.Address, b => b.ToTable("CustomerAddresses")); });
Problemy z opcjonalnymi zależnościami i przykładami tych środków zaradczych znajdują się w dokumentacji dotyczącej nowości w programie EF Core 6.0.
Zmiany o średnim wpływie
Zmiana właściciela jednostki należącej teraz zgłasza wyjątek
Stare zachowanie
Możliwe było ponowne przypisanie jednostki należącej do innej jednostki właściciela.
Nowe zachowanie
Ta akcja spowoduje teraz zgłoszenie wyjątku:
Właściwość "{entityType}". Element {property}" jest częścią klucza i dlatego nie można go modyfikować ani oznaczać jako zmodyfikowane. Aby zmienić podmiot zabezpieczeń istniejącej jednostki z identyfikującym klucz obcy, najpierw usuń zależne i wywołaj polecenie "SaveChanges", a następnie skojarz zależność z nowym podmiotem zabezpieczeń.
Dlaczego
Mimo że nie wymagamy, aby właściwości klucza istniały w typie własności, program EF nadal tworzy właściwości w tle, które będą używane jako klucz podstawowy i klucz obcy wskazujący właściciela. Gdy jednostka właściciela zostanie zmieniona, spowoduje to zmianę wartości klucza obcego w jednostce będącej własnością, a ponieważ są one również używane jako klucz podstawowy, powoduje to zmianę tożsamości jednostki. Nie jest to jeszcze w pełni obsługiwane w programie EF Core i jest dozwolone tylko warunkowo dla jednostek będących własnością, co czasami powoduje, że stan wewnętrzny staje się niespójny.
Środki zaradcze
Zamiast przypisywać to samo wystąpienie należące do nowego właściciela, możesz przypisać kopię i usunąć starą.
Azure Cosmos DB: powiązane typy jednostek są odnajdywane jako należące do użytkownika
Problem ze śledzeniem #24803Co nowego: domyślnie do niejawnej własności
Stare zachowanie
Podobnie jak w przypadku innych dostawców, powiązane typy jednostek zostały odnalezione jako normalne (nienależące do firmy).
Nowe zachowanie
Powiązane typy jednostek będą teraz własnością typu jednostki, na którym zostały odnalezione. Tylko typy jednostek, które odpowiadają DbSet<TEntity> właściwości, zostaną odnalezione jako nienależące do firmy.
Dlaczego
To zachowanie jest zgodne z typowym wzorcem modelowania danych w usłudze Azure Cosmos DB osadzania powiązanych danych w jednym dokumencie. Usługa Azure Cosmos DB nie obsługuje natywnie dołączania do różnych dokumentów, dlatego modelowanie powiązanych jednostek, ponieważ nienależące do firmy ma ograniczoną użyteczność.
Środki zaradcze
Aby skonfigurować typ jednostki jako nienależące do wywołania modelBuilder.Entity<MyEntity>();
SQLite: Połączenia są w puli
Problem ze śledzeniem nr 13837Co nowego: domyślnie do niejawnej własności
Stare zachowanie
Wcześniej połączenia w witrynie Microsoft.Data.Sqlite nie były w puli.
Nowe zachowanie
Począwszy od wersji 6.0, połączenia są teraz domyślnie w puli. Powoduje to, że pliki bazy danych są otwierane przez proces nawet po zamknięciu obiektu połączenia ADO.NET.
Dlaczego
Buforowanie bazowych połączeń znacznie poprawia wydajność otwierania i zamykania obiektów połączenia ADO.NET. Jest to szczególnie zauważalne w przypadku scenariuszy, w których otwarcie połączenia bazowego jest kosztowne, jak w przypadku szyfrowania lub w scenariuszach, w których istnieje wiele krótkotrwałych połączeń z bazą danych.
Środki zaradcze
Buforowanie połączeń można wyłączyć, dodając Pooling=False
je do parametry połączenia.
Niektóre scenariusze (takie jak usuwanie pliku bazy danych) mogą teraz napotkać błędy informujące, że plik jest nadal używany. Przed wykonaniem operacji pliku przy użyciu polecenia można ręcznie wyczyścić pulę połączeń.SqliteConnection.ClearPool()
SqliteConnection.ClearPool(connection);
File.Delete(databaseFile);
Relacje wiele-do-wielu bez mapowanych jednostek sprzężenia są teraz szkieletowe
Problem ze śledzeniem nr 22475
Stare zachowanie
Tworzenie szkieletów (inżynieria odwrotna) DbContext
typów jednostek i z istniejącej bazy danych zawsze jawnie mapowane tabele sprzężenia w celu sprzężenia typów jednostek dla relacji wiele-do-wielu.
Nowe zachowanie
Proste tabele sprzężenia zawierające tylko dwie właściwości klucza obcego do innych tabel nie są już mapowane na jawne typy jednostek, ale są zamiast tego mapowane jako relacja wiele-do-wielu między dwiema tabelami sprzężonych.
Dlaczego
Relacje wiele-do-wielu bez jawnych typów sprzężenia zostały wprowadzone w programie EF Core 5.0 i są czystszym, bardziej naturalnym sposobem reprezentowania prostych tabel sprzężenia.
Środki zaradcze
Istnieją dwa środki zaradcze. Preferowaną metodą jest zaktualizowanie kodu w celu bezpośredniego używania relacji wiele-do-wielu. Bardzo rzadko typ jednostki sprzężenia musi być używany bezpośrednio, gdy zawiera tylko dwa klucze obce dla relacji wiele-do-wielu.
Alternatywnie można dodać jawną jednostkę sprzężenia z powrotem do modelu EF. Na przykład przy założeniu relacji wiele-do-wielu między Post
i Tag
dodaj ponownie typ sprzężenia i nawigacje przy użyciu klas częściowych:
public partial class PostTag
{
public int PostsId { get; set; }
public int TagsId { get; set; }
public virtual Post Posts { get; set; }
public virtual Tag Tags { get; set; }
}
public partial class Post
{
public virtual ICollection<PostTag> PostTags { get; set; }
}
public partial class Tag
{
public virtual ICollection<PostTag> PostTags { get; set; }
}
Następnie dodaj konfigurację dla typu sprzężenia i nawigacji do klasy częściowej dla elementu DbContext:
public partial class DailyContext
{
partial void OnModelCreatingPartial(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Post>(entity =>
{
entity.HasMany(d => d.Tags)
.WithMany(p => p.Posts)
.UsingEntity<PostTag>(
l => l.HasOne<Tag>(e => e.Tags).WithMany(e => e.PostTags).HasForeignKey(e => e.TagsId),
r => r.HasOne<Post>(e => e.Posts).WithMany(e => e.PostTags).HasForeignKey(e => e.PostsId),
j =>
{
j.HasKey("PostsId", "TagsId");
j.ToTable("PostTag");
});
});
}
}
Na koniec usuń wygenerowaną konfigurację relacji wiele-do-wielu z kontekstu szkieletu. Jest to konieczne, ponieważ typ jednostki sprzężenia szkieletowego musi zostać usunięty z modelu, zanim będzie można użyć jawnego typu. Ten kod będzie musiał zostać usunięty za każdym razem, gdy kontekst jest szkieletem, ale ponieważ powyższy kod znajduje się w klasach częściowych, będą utrwalane.
Należy pamiętać, że w przypadku tej konfiguracji jednostka sprzężenia może być używana jawnie, podobnie jak w poprzednich wersjach programu EF Core. Jednak relacja może być również używana jako relacja wiele-do-wielu. Oznacza to, że aktualizacja kodu w ten sposób może być tymczasowym rozwiązaniem, podczas gdy pozostała część kodu jest aktualizowana w celu używania relacji jako wiele do wielu w naturalny sposób.
Zmiany o niskim wpływie
Wyczyszczone mapowanie między wartościami DeleteBehavior i ON DELETE
Problem ze śledzeniem nr 21252
Stare zachowanie
Niektóre mapowania między zachowaniem relacji OnDelete()
a zachowaniem kluczy ON DELETE
obcych w bazie danych były niespójne zarówno w przypadku migracji, jak i szkieletu.
Nowe zachowanie
W poniższej tabeli przedstawiono zmiany migracji.
OnDelete() | PO USUNIĘCIU |
---|---|
NoAction | BRAK AKCJI |
ClientNoAction | BRAK AKCJI |
Ogranicz | OGRANICZAĆ |
Cascade | KASKADA |
ClientCascade | |
SetNull | USTAW WARTOŚĆ NULL |
ClientSetNull |
Zmiany dotyczące tworzenia szkieletów są następujące.
PO USUNIĘCIU | OnDelete() |
---|---|
BRAK AKCJI | ClientSetNull |
OGRANICZAĆ | |
KASKADA | Cascade |
USTAW WARTOŚĆ NULL | SetNull |
Dlaczego
Nowe mapowania są bardziej spójne. Domyślne zachowanie bazy danych NO ACTION jest teraz preferowane w przypadku bardziej restrykcyjnego i mniej wydajnego zachowania OGRANICZ.
Środki zaradcze
Domyślne zachowanie onDelete() relacji opcjonalnych to ClientSetNull. Jego mapowanie zmieniło się z RESTRICT na NO ACTION. Może to spowodować wygenerowanie wielu operacji podczas pierwszej migracji dodanej po uaktualnieniu do programu EF Core 6.0.
Możesz zastosować te operacje lub ręcznie usunąć je z migracji, ponieważ nie mają one wpływu funkcjonalnego na program EF Core.
Program SQL Server nie obsługuje funkcji RESTRICT, więc te klucze obce zostały już utworzone przy użyciu opcji BEZ AKCJI. Operacje migracji nie będą miały wpływu na program SQL Server i są bezpieczne do usunięcia.
Baza danych w pamięci sprawdza, czy wymagane właściwości nie zawierają wartości null
Problem ze śledzeniem nr 10613
Stare zachowanie
Baza danych w pamięci zezwalała na zapisywanie wartości null nawet wtedy, gdy właściwość została skonfigurowana zgodnie z potrzebami.
Nowe zachowanie
Baza danych w pamięci zgłasza Microsoft.EntityFrameworkCore.DbUpdateException
wywołanie metody lub SaveChanges
SaveChangesAsync
, a wymagana właściwość jest ustawiona na wartość null.
Dlaczego
Zachowanie bazy danych w pamięci jest teraz zgodne z zachowaniem innych baz danych.
Środki zaradcze
Poprzednie zachowanie (tj. nie sprawdzanie wartości null) można przywrócić podczas konfigurowania dostawcy w pamięci. Na przykład:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder
.UseInMemoryDatabase("MyDatabase", b => b.EnableNullChecks(false));
}
Usunięto ostatni element ORDER BY podczas dołączania do kolekcji
Problem ze śledzeniem nr 19828
Stare zachowanie
Podczas wykonywania numerów JOIN SQL w kolekcjach (relacje jeden do wielu) platforma EF Core służy do dodawania elementu ORDER BY dla każdej kolumny klucza tabeli sprzężonej. Na przykład załadowanie wszystkich blogów przy użyciu powiązanych wpisów zostało wykonane za pośrednictwem następującego kodu SQL:
SELECT [b].[BlogId], [b].[Name], [p].[PostId], [p].[BlogId], [p].[Title]
FROM [Blogs] AS [b]
LEFT JOIN [Post] AS [p] ON [b].[BlogId] = [p].[BlogId]
ORDER BY [b].[BlogId], [p].[PostId]
Te porządkowania są niezbędne do prawidłowego materializacji jednostek.
Nowe zachowanie
Ostatni element ORDER BY dla sprzężenia kolekcji został pominięty:
SELECT [b].[BlogId], [b].[Name], [p].[PostId], [p].[BlogId], [p].[Title]
FROM [Blogs] AS [b]
LEFT JOIN [Post] AS [p] ON [b].[BlogId] = [p].[BlogId]
ORDER BY [b].[BlogId]
Kolumna ORDER BY dla kolumny Identyfikator wpisu nie jest już generowana.
Dlaczego
Każda usługa ORDER BY nakłada dodatkową pracę po stronie bazy danych, a ostatnia kolejność nie jest konieczna w przypadku potrzeb materializacji platformy EF Core. Dane pokazują, że usunięcie tej ostatniej kolejności może spowodować znaczną poprawę wydajności w niektórych scenariuszach.
Środki zaradcze
Jeśli aplikacja oczekuje, że jednostki przyłączone zostaną zwrócone w określonej kolejności, wprowadź to jawnie, dodając operator LINQ OrderBy
do zapytania.
Zestaw dbSet nie implementuje już elementu IAsyncEnumerable
Problem ze śledzeniem nr 24041
Stare zachowanie
DbSet<TEntity>, który służy do wykonywania zapytań w obiekcie DbContext, używany do implementowania IAsyncEnumerable<T>elementu .
Nowe zachowanie
DbSet<TEntity> program nie implementuje IAsyncEnumerable<T>już bezpośrednio .
Dlaczego
DbSet<TEntity> pierwotnie został wdrożony IAsyncEnumerable<T> głównie w celu umożliwienia bezpośredniego wyliczenia na nim za pomocą foreach
konstrukcji. Niestety, gdy projekt odwołuje się również do pliku System.Linq.Async w celu utworzenia asynchronicznych operatorów LINQ po stronie klienta, spowodowało to niejednoznaczny błąd wywołania między operatorami zdefiniowanymi IQueryable<T>
przez i zdefiniowanymi przez IAsyncEnumerable<T>
element . Język C# 9 dodał obsługę rozszerzeń GetEnumerator
dla foreach
pętli, usuwając oryginalną główną przyczynę odwołania do elementu IAsyncEnumerable
.
Zdecydowana większość DbSet
użycia będzie nadal działać zgodnie z oczekiwaniami, ponieważ tworzą operatory LINQ za pośrednictwem DbSet
, wyliczają je itp. Jedynymi przerwanymi użyciami są te, które próbują rzutować DbSet
bezpośrednio na IAsyncEnumerable
.
Środki zaradcze
Jeśli musisz odwołać się do elementu DbSet<TEntity> jako , wywołaj metodę IAsyncEnumerable<T>DbSet<TEntity>.AsAsyncEnumerable , aby jawnie go rzutować.
Zwracany typ jednostki TVF jest również domyślnie mapowany na tabelę
Problem ze śledzeniem nr 23408
Stare zachowanie
Typ jednostki nie został domyślnie zamapowany na tabelę, gdy jest używany jako typ zwracany programu TVF skonfigurowany za pomocą polecenia HasDbFunction.
Nowe zachowanie
Typ jednostki używany jako typ zwracany tvF zachowuje domyślne mapowanie tabeli.
Dlaczego
Nie jest intuicyjne, aby skonfigurowanie programu TVF usuwa domyślne mapowanie tabeli dla zwracanego typu jednostki.
Środki zaradcze
Aby usunąć domyślne mapowanie tabeli, wywołaj metodę ToTable(EntityTypeBuilder, String):
modelBuilder.Entity<MyEntity>().ToTable((string?)null));
Sprawdź, czy unikatowość nazw ograniczeń jest teraz weryfikowana
Problem ze śledzeniem nr 25061
Stare zachowanie
Sprawdź ograniczenia o tej samej nazwie, które mogły zostać zadeklarowane i użyte w tej samej tabeli.
Nowe zachowanie
Jawne skonfigurowanie dwóch ograniczeń sprawdzania o tej samej nazwie w tej samej tabeli spowoduje teraz wyjątek. Sprawdź ograniczenia utworzone zgodnie z konwencją zostaną przypisane unikatową nazwę.
Dlaczego
Większość baz danych nie zezwala na utworzenie dwóch ograniczeń sprawdzania o tej samej nazwie w tej samej tabeli, a niektóre wymagają, aby były unikatowe nawet w różnych tabelach. Spowoduje to zgłoszenie wyjątku podczas stosowania migracji.
Środki zaradcze
W niektórych przypadkach prawidłowe nazwy ograniczeń sprawdzania mogą być inne z powodu tej zmiany. Aby jawnie określić żądaną nazwę, wywołaj metodę HasName:
modelBuilder.Entity<MyEntity>().HasCheckConstraint("CK_Id", "Id > 0", c => c.HasName("CK_MyEntity_Id"));
Dodano interfejsy metadanych IReadOnly i usunięto metody rozszerzenia
Problem ze śledzeniem nr 19213
Stare zachowanie
Istnieją trzy zestawy interfejsów metadanych: IModel, IMutableModel oraz IConventionModel metody rozszerzeń.
Nowe zachowanie
Dodano nowy zestaw interfejsów IReadOnly
, np. IReadOnlyModel. Metody rozszerzenia, które zostały wcześniej zdefiniowane dla interfejsów metadanych, zostały przekonwertowane na domyślne metody interfejsu.
Dlaczego
Domyślne metody interfejsu umożliwiają zastąpienie implementacji. Jest to używane przez nową implementację modelu w czasie wykonywania, aby zapewnić lepszą wydajność.
Środki zaradcze
Te zmiany nie powinny mieć wpływu na większość kodu. Jeśli jednak używasz metod rozszerzenia za pośrednictwem statycznej składni wywołania, należy przekonwertować ją na składnię wywołania wystąpienia.
IExecutionStrategy to teraz pojedyncza usługa
Problem ze śledzeniem nr 21350
Nowe zachowanie
IExecutionStrategy jest teraz pojedynczą usługą. Oznacza to, że każdy stan dodany w implementacjach niestandardowych pozostanie między wykonaniami a delegowanym przekazanym do ExecutionStrategy zostanie wykonany tylko raz.
Dlaczego
Ta ograniczona alokacja w dwóch gorących ścieżkach w programie EF.
Środki zaradcze
Implementacje pochodzące z ExecutionStrategy klasy powinny wyczyścić dowolny stan w pliku OnFirstExecution().
Logika warunkowa przekazanego delegata ExecutionStrategy powinna zostać przeniesiona do niestandardowej implementacji IExecutionStrategy.
SQL Server: Więcej błędów jest uznawanych za przejściowe
Problem ze śledzeniem nr 25050
Nowe zachowanie
Błędy wymienione w powyższym problemie są teraz uznawane za przejściowe. W przypadku korzystania ze strategii wykonywania domyślnej (bez ponawiania próby) te błędy zostaną teraz opakowane w dodatkowe wystąpienie wyjątku.
Dlaczego
Nadal zbieramy opinie użytkowników i zespołu programu SQL Server, na których błędy powinny być uznawane za przejściowe.
Środki zaradcze
Aby zmienić zestaw błędów, które są uważane za przejściowe, należy użyć niestandardowej strategii wykonywania, która może pochodzić z SqlServerRetryingExecutionStrategy - odporności połączenia — EF Core.
Azure Cosmos DB: więcej znaków jest ucieczki w wartościach "id"
Problem ze śledzeniem nr 25100
Stare zachowanie
W programie EF Core 5 '|'
tylko w wartościach zostały wymykane id
.
Nowe zachowanie
W programie EF Core 6 wartości '/'
, '\'
'?'
i '#'
również są usuwane w id
wartościach.
Dlaczego
Te znaki są nieprawidłowe, jak opisano w Resource.Id. Użycie ich w programie id
spowoduje niepowodzenie zapytań.
Środki zaradcze
Wygenerowaną wartość można zastąpić, ustawiając ją przed oznaczeniem jednostki jako Added
:
var entry = context.Attach(entity);
entry.Property("__id").CurrentValue = "MyEntity|/\\?#";
entry.State = EntityState.Added;
Niektóre usługi Singleton są teraz objęte zakresem
Problem ze śledzeniem nr 25084
Nowe zachowanie
Wiele usług zapytań i niektóre usługi czasu projektowania, które zostały zarejestrowane jako Singleton
są teraz zarejestrowane jako Scoped
.
Dlaczego
Okres istnienia musiał zostać zmieniony, aby umożliwić korzystanie z nowej funkcji — DefaultTypeMapping aby wpływać na zapytania.
Okresy istnienia usług w czasie projektowania zostały dostosowane w celu dopasowania do okresów istnienia usług w czasie wykonywania, aby uniknąć błędów podczas korzystania z obu tych usług.
Środki zaradcze
Użyj TryAdd polecenia , aby zarejestrować usługi EF Core przy użyciu domyślnego okresu istnienia. Używaj TryAddProviderSpecificServices tylko dla usług, które nie są dodawane przez program EF.
Nowy interfejs API buforowania dla rozszerzeń, które dodają lub zastępują usługi
Problem ze śledzeniem nr 19152
Stare zachowanie
W programie EF Core 5 GetServiceProviderHashCode zwrócone long
i zostało użyte bezpośrednio jako część klucza pamięci podręcznej dostawcy usług.
Nowe zachowanie
GetServiceProviderHashCode teraz zwraca int
wartość i jest używana tylko do obliczania kodu skrótu klucza pamięci podręcznej dla dostawcy usług.
Ponadto należy zaimplementować, aby wskazać, ShouldUseSameServiceProvider czy bieżący obiekt reprezentuje tę samą konfigurację usługi, a tym samym może użyć tego samego dostawcy usług.
Dlaczego
Po prostu użycie kodu skrótu w ramach klucza pamięci podręcznej spowodowało sporadyczne kolizje, które były trudne do zdiagnozowania i naprawienia. Dodatkowa metoda zapewnia, że ten sam dostawca usług jest używany tylko wtedy, gdy jest to konieczne.
Środki zaradcze
Wiele rozszerzeń nie uwidacznia żadnych opcji mających wpływ na zarejestrowane usługi i może korzystać z następującej implementacji programu ShouldUseSameServiceProvider:
private sealed class ExtensionInfo : DbContextOptionsExtensionInfo
{
public ExtensionInfo(IDbContextOptionsExtension extension)
: base(extension)
{
}
...
public override bool ShouldUseSameServiceProvider(DbContextOptionsExtensionInfo other)
=> other is ExtensionInfo;
}
W przeciwnym razie należy dodać dodatkowe predykaty, aby porównać wszystkie odpowiednie opcje.
Nowa procedura inicjowania modelu migawki i czasu projektowania
Problem ze śledzeniem nr 22031
Stare zachowanie
W programie EF Core 5 należy wywołać określone konwencje, zanim model migawek będzie gotowy do użycia.
Nowe zachowanie
IModelRuntimeInitializer wprowadzono niektóre z wymaganych kroków, a wprowadzono model czasu wykonywania, który nie zawiera wszystkich metadanych migracji, więc model czasu projektowania powinien być używany do różnicowania modelu.
Dlaczego
IModelRuntimeInitializer Abstrahuje od kroków finalizacji modelu, dzięki czemu można je teraz zmienić bez dalszych zmian powodujących niezgodność dla użytkowników.
Zoptymalizowany model czasu wykonywania został wprowadzony w celu zwiększenia wydajności czasu wykonywania. Ma kilka optymalizacji, z których jeden usuwa metadane, które nie są używane w czasie wykonywania.
Środki zaradcze
Poniższy fragment kodu ilustruje, jak sprawdzić, czy bieżący model różni się od modelu migawki:
var snapshotModel = migrationsAssembly.ModelSnapshot?.Model;
if (snapshotModel is IMutableModel mutableModel)
{
snapshotModel = mutableModel.FinalizeModel();
}
if (snapshotModel != null)
{
snapshotModel = context.GetService<IModelRuntimeInitializer>().Initialize(snapshotModel);
}
var hasDifferences = context.GetService<IMigrationsModelDiffer>().HasDifferences(
snapshotModel?.GetRelationalModel(),
context.GetService<IDesignTimeModel>().Model.GetRelationalModel());
Ten fragment kodu pokazuje, jak zaimplementować IDesignTimeDbContextFactory<TContext> , tworząc model zewnętrznie i wywołując metodę UseModel:
internal class MyDesignContext : IDesignTimeDbContextFactory<MyContext>
{
public TestContext CreateDbContext(string[] args)
{
var optionsBuilder = new DbContextOptionsBuilder();
optionsBuilder.UseSqlServer(Configuration.GetConnectionString("DB"));
var modelBuilder = SqlServerConventionSetBuilder.CreateModelBuilder();
CustomizeModel(modelBuilder);
var model = modelBuilder.Model.FinalizeModel();
var serviceContext = new MyContext(optionsBuilder.Options);
model = serviceContext.GetService<IModelRuntimeInitializer>().Initialize(model);
return new MyContext(optionsBuilder.Options);
}
}
OwnedNavigationBuilder.HasIndex
zwraca teraz inny typ
Problem ze śledzeniem nr 24005
Stare zachowanie
W programie EF Core 5 HasIndex zwracany IndexBuilder<TEntity>
jest typ TEntity
właściciela.
Nowe zachowanie
HasIndex teraz zwraca wartość IndexBuilder<TDependentEntity>
, gdzie TDependentEntity
jest typem własności.
Dlaczego
Zwrócony obiekt konstruktora nie został poprawnie wpisany.
Środki zaradcze
Ponowne skompilowanie zestawu względem najnowszej wersji programu EF Core wystarczy, aby rozwiązać wszelkie problemy spowodowane tą zmianą.
DbFunctionBuilder.HasSchema(null)
Zastępuje [DbFunction(Schema = "schema")]
Problem ze śledzeniem nr 24228
Stare zachowanie
W programie EF Core 5 wywołanie z HasSchema wartością null
nie przechowywało źródła konfiguracji, co umożliwiło DbFunctionAttribute jego zastąpienie.
Nowe zachowanie
Wywołanie HasSchema z wartością null
przechowuje teraz źródło konfiguracji i uniemożliwia zastąpienie go atrybutu.
Dlaczego
Konfiguracja określona za pomocą interfejsu ModelBuilder API nie powinna być zastępowana adnotacjami danych.
Środki zaradcze
Usuń wywołanie , HasSchema
aby umożliwić atrybutowi skonfigurowanie schematu.
Wstępnie zainicjowane nawigacje są zastępowane przez wartości z zapytań bazy danych
Problem ze śledzeniem nr 23851
Stare zachowanie
Właściwości nawigacji ustawione na pusty obiekt zostały pozostawione bez zmian na potrzeby śledzenia zapytań, ale zostały zastąpione dla zapytań nieśledzenia. Rozważmy na przykład następujące typy jednostek:
public class Foo
{
public int Id { get; set; }
public Bar Bar { get; set; } = new(); // Don't do this.
}
public class Bar
{
public int Id { get; set; }
}
Zapytanie bez śledzenia dotyczące Foo
dołączania Bar
Foo.Bar
do jednostki, do którego wysyła zapytanie z bazy danych. Na przykład ten kod:
var foo = context.Foos.AsNoTracking().Include(e => e.Bar).Single();
Console.WriteLine($"Foo.Bar.Id = {foo.Bar.Id}");
Wydrukowano Foo.Bar.Id = 1
.
Jednak to samo uruchomienie zapytania na potrzeby śledzenia nie zostało zastąpione Foo.Bar
przez jednostkę, do którego wykonano zapytanie z bazy danych. Na przykład ten kod:
var foo = context.Foos.Include(e => e.Bar).Single();
Console.WriteLine($"Foo.Bar.Id = {foo.Bar.Id}");
Wydrukowano Foo.Bar.Id = 0
.
Nowe zachowanie
W programie EF Core 6.0 zachowanie zapytań śledzenia jest teraz zgodne z działaniem zapytań bez śledzenia. Oznacza to, że oba te kody:
var foo = context.Foos.AsNoTracking().Include(e => e.Bar).Single();
Console.WriteLine($"Foo.Bar.Id = {foo.Bar.Id}");
I ten kod:
var foo = context.Foos.Include(e => e.Bar).Single();
Console.WriteLine($"Foo.Bar.Id = {foo.Bar.Id}");
Drukuj Foo.Bar.Id = 1
.
Dlaczego
Istnieją dwa powody wprowadzenia tej zmiany:
- Aby upewnić się, że zapytania śledzenia i bez śledzenia mają spójne zachowanie.
- Gdy baza danych jest odpytywane, należy założyć, że kod aplikacji chce odzyskać wartości przechowywane w bazie danych.
Środki zaradcze
Istnieją dwa środki zaradcze:
- Nie należy wykonywać zapytań dotyczących obiektów z bazy danych, które nie powinny być uwzględniane w wynikach. Na przykład w powyższych fragmentach kodu nie
Include
Foo.Bar
Bar
należy zwracać wystąpienia z bazy danych i uwzględnić je w wynikach. - Ustaw wartość nawigacji po wykonaniu zapytania z bazy danych. Na przykład w powyższych fragmentach kodu wywołaj metodę
foo.Bar = new()
po uruchomieniu zapytania.
Należy również rozważyć zainicjowanie wystąpień jednostek powiązanych z domyślnymi obiektami. Oznacza to, że powiązane wystąpienie jest nową jednostką, a nie zapisaną w bazie danych bez ustawionej wartości klucza. Jeśli zamiast tego powiązana jednostka istnieje w bazie danych, dane w kodzie są zasadniczo sprzeczne z danymi przechowywanymi w bazie danych.
Nieznane wartości ciągów wyliczenia w bazie danych nie są konwertowane na wartość domyślną wyliczenia podczas wykonywania zapytania
Problem ze śledzeniem nr 24084
Stare zachowanie
Właściwości wyliczenia można mapować na kolumny ciągów w bazie danych przy użyciu polecenia HasConversion<string>()
lub EnumToStringConverter
. Spowoduje to przekonwertowanie wartości ciągów w kolumnie na pasujące elementy członkowskie typu wyliczenia platformy .NET. Jeśli jednak wartość ciągu nie jest zgodna i składowa wyliczenia, właściwość została ustawiona na wartość domyślną dla wyliczenia.
Nowe zachowanie
Program EF Core 6.0 zgłasza InvalidOperationException
teraz komunikat "Nie można przekonwertować wartości ciągu "{value}
" z bazy danych na dowolną wartość w zamapowanym wyliczeniem "{enumType}
".
Dlaczego
Konwersja na wartość domyślną może spowodować uszkodzenie bazy danych, jeśli jednostka zostanie później zapisana z powrotem w bazie danych.
Środki zaradcze
W idealnym przypadku upewnij się, że kolumna bazy danych zawiera tylko prawidłowe wartości. Alternatywnie zaimplementuj element ValueConverter
ze starym zachowaniem.
Funkcja DbFunctionBuilder.HasTranslation udostępnia teraz argumenty funkcji jako IReadOnlyList, a nie IReadOnlyCollection
Problem ze śledzeniem nr 23565
Stare zachowanie
Podczas konfigurowania tłumaczenia dla funkcji zdefiniowanej przez użytkownika przy użyciu HasTranslation
metody argumenty funkcji zostały podane jako IReadOnlyCollection<SqlExpression>
.
Nowe zachowanie
W programie EF Core 6.0 argumenty są teraz udostępniane jako IReadOnlyList<SqlExpression>
.
Dlaczego
IReadOnlyList
umożliwia korzystanie z indeksatorów, więc argumenty są teraz łatwiejsze do uzyskania dostępu.
Środki zaradcze
Brak. IReadOnlyList
implementuje IReadOnlyCollection
interfejs, więc przejście powinno być proste.
Domyślne mapowanie tabeli nie jest usuwane, gdy jednostka jest mapowana na funkcję wartości tabeli
Problem ze śledzeniem nr 23408
Stare zachowanie
Gdy jednostka została zamapowana na funkcję wartości tabeli, jej domyślne mapowanie na tabelę zostało usunięte.
Nowe zachowanie
W programie EF Core 6.0 jednostka jest nadal mapowana na tabelę przy użyciu mapowania domyślnego, nawet jeśli jest również mapowana na funkcję wartości tabeli.
Dlaczego
Funkcje zwracane przez tabele, które zwracają jednostki, są często używane jako pomocnik lub hermetyzują operację zwracającą kolekcję jednostek, a nie jako ścisłe zastąpienie całej tabeli. Ta zmiana ma być bardziej zgodna z prawdopodobnym zamiarem użytkownika.
Środki zaradcze
Mapowanie na tabelę można jawnie wyłączyć w konfiguracji modelu:
modelBuilder.Entity<MyEntity>().ToTable((string)null);
dotnet-ef — obiekty docelowe platformy .NET 6
Problem ze śledzeniem nr 27787
Stare zachowanie
Polecenie dotnet-ef od pewnego czasu dotyczyło platformy .NET Core 3.1. Dzięki temu można używać nowszej wersji narzędzia bez instalowania nowszych wersji środowiska uruchomieniowego platformy .NET.
Nowe zachowanie
W programie EF Core 6.0.6 narzędzie dotnet-ef jest teraz przeznaczone dla platformy .NET 6. Nadal możesz użyć narzędzia do projektów przeznaczonych dla starszych wersji platform .NET i .NET Core, ale musisz zainstalować środowisko uruchomieniowe platformy .NET 6, aby uruchomić narzędzie.
Dlaczego
Zestaw .NET 6.0.200 SDK zaktualizował zachowanie dotnet tool install
w systemie osx-arm64 w celu utworzenia podkładki osx-x64 dla narzędzi przeznaczonych dla platformy .NET Core 3.1. Aby zachować domyślne środowisko pracy dla platformy dotnet-ef, musieliśmy zaktualizować go do docelowej platformy .NET 6.
Środki zaradcze
Aby uruchomić narzędzie dotnet-ef bez instalowania środowiska uruchomieniowego platformy .NET 6, możesz zainstalować starszą wersję narzędzia:
dotnet tool install dotnet-ef --version 3.1.*
IModelCacheKeyFactory
w celu obsługi buforowania w czasie projektowania może być konieczne zaktualizowanie implementacji
Problem ze śledzeniem nr 25154
Stare zachowanie
IModelCacheKeyFactory
Nie ma możliwości buforowania modelu czasu projektowania oddzielnie od modelu środowiska uruchomieniowego.
Nowe zachowanie
IModelCacheKeyFactory
ma nowe przeciążenie, które umożliwia buforowanie modelu czasu projektowania oddzielnie od modelu środowiska uruchomieniowego. Nie zaimplementowanie tej metody może spowodować wyjątek podobny do następującego:
System.InvalidOperationException: "Żądana konfiguracja nie jest przechowywana w modelu zoptymalizowanym pod kątem odczytu, użyj polecenia "DbContext.GetService<IDesignTimeModel>(). Model".
Dlaczego
Implementacja skompilowanych modeli wymagała rozdzielenia czasu projektowania (używanego podczas tworzenia modelu) i środowiska uruchomieniowego (używanego podczas wykonywania zapytań itp.). Jeśli kod środowiska uruchomieniowego wymaga dostępu do informacji o czasie projektowania, należy buforować model czasu projektowania.
Środki zaradcze
Zaimplementuj nowe przeciążenie. Na przykład:
public object Create(DbContext context, bool designTime)
=> context is DynamicContext dynamicContext
? (context.GetType(), dynamicContext.UseIntProperty, designTime)
: (object)context.GetType();
Nawigacja "{navigation}" została zignorowana z elementu "Include" w zapytaniu, ponieważ poprawka zostanie automatycznie wypełniona. Jeśli później zostaną określone dalsze nawigacje w obszarze "Dołącz", zostaną one zignorowane. Powrót do drzewa dołączania jest niedozwolony.
NavigationBaseIncludeIgnored
jest teraz błędem domyślnie
Stare zachowanie
CoreEventId.NavigationBaseIncludeIgnored
Zdarzenie zostało zarejestrowane jako ostrzeżenie domyślnie.
Nowe zachowanie
CoreEventId.NavigationBaseIncludeIgnored
Zdarzenie zostało zarejestrowane jako błąd domyślnie i powoduje zgłoszenie wyjątku.
Dlaczego
Te wzorce zapytań nie są dozwolone, dlatego program EF Core zgłasza teraz, aby wskazać, że zapytania powinny zostać zaktualizowane.
Środki zaradcze
Stare zachowanie można przywrócić, konfigurując zdarzenie jako ostrzeżenie. Na przykład:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder.ConfigureWarnings(b => b.Warn(CoreEventId.NavigationBaseIncludeIgnored));