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

Zmiana powodująca niezgodność Wpływ
Zagnieżdżone opcjonalne zależności współużytkują tabelę i nie można zapisać wymaganych właściwości Wys.
Zmiana właściciela jednostki należącej teraz zgłasza wyjątek Śred.
Azure Cosmos DB: powiązane typy jednostek są odnajdywane jako należące do użytkownika Śred.
SQLite: Połączenie ions są w puli Śred.
Relacje wiele-do-wielu bez mapowanych jednostek sprzężenia są teraz szkieletowe Śred.
Wyczyszczone mapowanie między wartościami DeleteBehavior i ON DELETE Niski
Baza danych w pamięci sprawdza, czy wymagane właściwości nie zawierają wartości null Niski
Usunięto ostatni element ORDER BY podczas dołączania do kolekcji Niski
Zestaw dbSet nie implementuje już elementu IAsyncEnumerable Niski
Zwracany typ jednostki TVF jest również domyślnie mapowany na tabelę Niski
Sprawdź, czy unikatowość nazw ograniczeń jest teraz weryfikowana Niski
Dodano interfejsy metadanych IReadOnly i usunięto metody rozszerzenia Niski
IExecutionStrategy to teraz pojedyncza usługa Niski
SQL Server: Więcej błędów jest uznawanych za przejściowe Niski
Azure Cosmos DB: więcej znaków jest ucieczki w wartościach "id" Niski
Niektóre usługi Singleton są teraz objęte zakresem Niskie*
Nowy interfejs API buforowania dla rozszerzeń, które dodają lub zastępują usługi Niskie*
Nowa procedura inicjowania modelu migawki i czasu projektowania Niski
OwnedNavigationBuilder.HasIndex zwraca teraz inny typ Niski
DbFunctionBuilder.HasSchema(null) Zastępuje [DbFunction(Schema = "schema")] Niski
Wstępnie zainicjowane nawigacje są zastępowane przez wartości z zapytań bazy danych Niski
Nieznane wartości ciągów wyliczenia w bazie danych nie są konwertowane na wartość domyślną wyliczenia podczas wykonywania zapytania Niski
Funkcja DbFunctionBuilder.HasTranslation udostępnia teraz argumenty funkcji jako IReadOnlyList, a nie IReadOnlyCollection Niski
Domyślne mapowanie tabeli nie jest usuwane, gdy jednostka jest mapowana na funkcję wartości tabeli Niski
dotnet-ef — obiekty docelowe platformy .NET 6 Niski
IModelCacheKeyFactory w celu obsługi buforowania w czasie projektowania może być konieczne zaktualizowanie implementacji Niski
NavigationBaseIncludeIgnored jest teraz błędem domyślnie Niski

* 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 Customernie 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ć:

  1. 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();
            });
    
  2. Upewnij się, że zależne zawiera co najmniej jedną wymaganą właściwość.

  3. 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

Problem ze śledzeniem nr 4073

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ą.

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łączenie ions 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łączenie ion 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 Tagdodaj 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 OGRANICZYĆ
Cascasde KASKADOWO
ClientCascade OGRANICZBRAK AKCJI
Setnull USTAW WARTOŚĆ NULL
ClientSetNull OGRANICZBRAK AKCJI

Zmiany dotyczące tworzenia szkieletów są następujące.

PO USUNIĘCIU OnDelete()
BRAK AKCJI ClientSetNull
OGRANICZYĆ ClientSetNullRestrict
KASKADOWO 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 SaveChangesSaveChangesAsync , 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ń GetEnumeratordla 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, użyj niestandardowej strategii wykonywania, która może pochodzić z SqlServerRetryingExecutionStrategy - Połączenie odporność na jony — 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 BarFoo.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:

  1. Aby upewnić się, że zapytania śledzenia i bez śledzenia mają spójne zachowanie.
  2. 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:

  1. 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 IncludeFoo.BarBar należy zwracać wystąpienia z bazy danych i uwzględnić je w wynikach.
  2. 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.

Problem ze śledzeniem nr 4315

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));