Co nowego w programie EF Core 5.0

Poniższa lista zawiera główne nowe funkcje w programie EF Core 5.0. Aby uzyskać pełną listę problemów w wydaniu, zobacz nasz monitor problemów.

Jako wersja główna program EF Core 5.0 zawiera również kilka zmian powodujących niezgodność, które są ulepszeniami interfejsu API lub zmianami zachowania, które mogą mieć negatywny wpływ na istniejące aplikacje.

Wiele do wielu

Program EF Core 5.0 obsługuje relacje wiele-do-wielu bez jawnego mapowania tabeli sprzężenia.

Rozważmy na przykład następujące typy jednostek:

public class Post
{
    public int Id { get; set; }
    public string Name { get; set; }
    public ICollection<Tag> Tags { get; set; }
}

public class Tag
{
    public int Id { get; set; }
    public string Text { get; set; }
    public ICollection<Post> Posts { get; set; }
}

Program EF Core 5.0 rozpoznaje tę relację według konwencji jako relację wiele-do-wielu i automatycznie tworzy tabelę PostTag sprzężenia w bazie danych. Dane można wykonywać zapytania i aktualizować bez jawnego odwoływania się do tabeli sprzężenia, co znacznie upraszcza kod. Tabelę sprzężenia można nadal dostosowywać i w razie potrzeby jawnie wykonywać zapytania.

Aby uzyskać więcej informacji, zobacz pełną dokumentację dotyczącą wielu do wielu.

Dzielenie zapytań

Począwszy od programu EF Core 3.0, program EF Core zawsze generuje pojedyncze zapytanie SQL dla każdego zapytania LINQ. Zapewnia to spójność danych zwracanych w ramach ograniczeń trybu transakcji w użyciu. Jednak może to stać się bardzo powolne, gdy zapytanie używa Include lub projekcji, aby przywrócić wiele powiązanych kolekcji.

Program EF Core 5.0 umożliwia teraz podzielenie pojedynczego zapytania LINQ, w tym powiązanych kolekcji na wiele zapytań SQL. Może to znacznie poprawić wydajność, ale może spowodować niespójność wyników zwróconych, jeśli dane zmienią się między dwoma zapytaniami. Transakcje z możliwością serializacji lub migawek mogą służyć do łagodzenia tego problemu i osiągnięcia spójności z podzielonymi zapytaniami, ale może to przynieść inne koszty wydajności i różnicę behawioralną.

Rozważmy na przykład zapytanie, które pobiera dwa poziomy powiązanych kolekcji przy użyciu polecenia Include:

var artists = context.Artists
    .Include(e => e.Albums)
    .ToList();

Domyślnie program EF Core wygeneruje następujący kod SQL podczas korzystania z dostawcy SQLite:

SELECT a."Id", a."Name", a0."Id", a0."ArtistId", a0."Title"
FROM "Artists" AS a
LEFT JOIN "Album" AS a0 ON a."Id" = a0."ArtistId"
ORDER BY a."Id", a0."Id"

W przypadku podzielonych zapytań zamiast tego jest generowany następujący kod SQL:

SELECT a."Id", a."Name"
FROM "Artists" AS a
ORDER BY a."Id"

SELECT a0."Id", a0."ArtistId", a0."Title", a."Id"
FROM "Artists" AS a
INNER JOIN "Album" AS a0 ON a."Id" = a0."ArtistId"
ORDER BY a."Id"

Dzielenie zapytań można włączyć, umieszczając nowy AsSplitQuery operator w dowolnym miejscu w zapytaniu LINQ lub globalnie w modelu OnConfiguring. Aby uzyskać więcej informacji, zobacz pełną dokumentację dotyczącą podzielonych zapytań.

Proste rejestrowanie i ulepszona diagnostyka

Program EF Core 5.0 wprowadza prosty sposób konfigurowania rejestrowania za pomocą nowej LogTo metody. Następujące elementy spowodują zapisanie komunikatów rejestrowania w konsoli, w tym wszystkich sql wygenerowanych przez program EF Core:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    => optionsBuilder.LogTo(Console.WriteLine);

Ponadto można teraz wywołać ToQueryString dowolne zapytanie LINQ, pobierając kod SQL, który wykona zapytanie:

Console.WriteLine(
    ctx.Artists
    .Where(a => a.Name == "Pink Floyd")
    .ToQueryString());

Na koniec różne typy platformy EF Core zostały wyposażone w ulepszoną DebugView właściwość, która zapewnia szczegółowy wgląd w wewnętrzne elementy. Na przykład można skonsultować się z dokładnie tym, ChangeTracker.DebugView które jednostki są śledzone w danym momencie.

Aby uzyskać więcej informacji, zobacz dokumentację dotyczącą rejestrowania i przechwytywania.

Filtrowane dołączanie

Metoda Include obsługuje teraz filtrowanie uwzględnionych jednostek:

var blogs = context.Blogs
    .Include(e => e.Posts.Where(p => p.Title.Contains("Cheese")))
    .ToList();

To zapytanie zwróci blogi wraz z każdym skojarzonym wpisem, ale tylko wtedy, gdy tytuł wpisu zawiera "Cheese".

Aby uzyskać więcej informacji, zobacz pełną dokumentację dotyczącą filtrowanego dołączania.

Mapowanie tabeli na typ (TPT)

Domyślnie program EF Core mapuje hierarchię dziedziczenia typów platformy .NET na jedną tabelę bazy danych. Jest to nazywane mapowaniem tabeli na hierarchię (TPH). Program EF Core 5.0 umożliwia również mapowanie każdego typu platformy .NET w hierarchii dziedziczenia na inną tabelę bazy danych; znane jako mapowanie tabeli na typ (TPT).

Rozważmy na przykład ten model z zamapowanym hierarchią:

public class Animal
{
    public int Id { get; set; }
    public string Name { get; set; }
}

public class Cat : Animal
{
    public string EducationLevel { get; set; }
}

public class Dog : Animal
{
    public string FavoriteToy { get; set; }
}

W przypadku TPT tabela bazy danych jest tworzona dla każdego typu w hierarchii:

CREATE TABLE [Animals] (
    [Id] int NOT NULL IDENTITY,
    [Name] nvarchar(max) NULL,
    CONSTRAINT [PK_Animals] PRIMARY KEY ([Id])
);

CREATE TABLE [Cats] (
    [Id] int NOT NULL,
    [EducationLevel] nvarchar(max) NULL,
    CONSTRAINT [PK_Cats] PRIMARY KEY ([Id]),
    CONSTRAINT [FK_Cats_Animals_Id] FOREIGN KEY ([Id]) REFERENCES [Animals] ([Id]) ON DELETE NO ACTION,
);

CREATE TABLE [Dogs] (
    [Id] int NOT NULL,
    [FavoriteToy] nvarchar(max) NULL,
    CONSTRAINT [PK_Dogs] PRIMARY KEY ([Id]),
    CONSTRAINT [FK_Dogs_Animals_Id] FOREIGN KEY ([Id]) REFERENCES [Animals] ([Id]) ON DELETE NO ACTION,
);

Aby uzyskać więcej informacji, zobacz pełną dokumentację dotyczącą TPT.

Elastyczne mapowanie jednostek

Typy jednostek są często mapowane na tabele lub widoki, tak aby program EF Core wycofał zawartość tabeli lub widoku podczas wykonywania zapytań dotyczących tego typu. Program EF Core 5.0 dodaje dodatkowe opcje mapowania, w których jednostka może być mapowana na zapytanie SQL (nazywane "zapytaniem definiującym") lub do funkcji wartości tabeli (TVF):

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Post>().ToSqlQuery(
        @"SELECT Id, Name, Category, BlogId FROM posts
          UNION ALL
          SELECT Id, Name, ""Legacy"", BlogId from legacy_posts");

    modelBuilder.Entity<Blog>().ToFunction("BlogsReturningFunction");
}

Funkcje wartości tabeli mogą być również mapowane na metodę .NET, a nie do zestawu DbSet, co pozwala na przekazywanie parametrów; mapowanie można skonfigurować za pomocą HasDbFunctionpolecenia .

Na koniec istnieje możliwość mapowania jednostki na widok podczas wykonywania zapytań (lub do funkcji lub definiowania zapytania), ale do tabeli podczas aktualizowania:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder
        .Entity<Blog>()
        .ToTable("Blogs")
        .ToView("BlogsView");
}

Typy jednostek typu współużytkowanego i torby właściwości

Program EF Core 5.0 umożliwia mapowanie tego samego typu clR na wiele różnych typów jednostek; takie typy są nazywane typami jednostek typu współużytkowanego. Chociaż dowolny typ CLR może być używany z tą funkcją, platforma .NET Dictionary oferuje szczególnie atrakcyjny przypadek użycia, który nazywamy "torbami właściwości":

public class ProductsContext : DbContext
{
    public DbSet<Dictionary<string, object>> Products => Set<Dictionary<string, object>>("Product");

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.SharedTypeEntity<Dictionary<string, object>>("Product", b =>
        {
            b.IndexerProperty<int>("Id");
            b.IndexerProperty<string>("Name").IsRequired();
            b.IndexerProperty<decimal>("Price");
        });
    }
}

Te jednostki można następnie wykonywać zapytania i aktualizować tak samo jak typy jednostek normalnych z własnym, dedykowanym typem CLR. Więcej informacji można znaleźć w dokumentacji dotyczącej torby na nieruchomości.

Wymagane 1:1 zależne

W programie EF Core 3.1 zależny koniec relacji jeden do jednego był zawsze uznawany za opcjonalny. Było to najbardziej widoczne w przypadku używania jednostek będących własnością, ponieważ wszystkie kolumny jednostki będącej własnością zostały utworzone jako dopuszczane do wartości null w bazie danych, nawet jeśli zostały skonfigurowane zgodnie z wymaganiami w modelu.

W programie EF Core 5.0 nawigacja do jednostki należącej może być skonfigurowana jako wymagana zależność. Przykład:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Person>(b =>
    {
        b.OwnsOne(e => e.HomeAddress,
            b =>
            {
                b.Property(e => e.City).IsRequired();
                b.Property(e => e.Postcode).IsRequired();
            });
        b.Navigation(e => e.HomeAddress).IsRequired();
    });
}

DbContextFactory

Program EF Core 5.0 wprowadza AddDbContextFactory i AddPooledDbContextFactory rejestruje fabrykę na potrzeby tworzenia wystąpień DbContext w kontenerze wstrzykiwania zależności aplikacji (D.I.). Może to być przydatne, gdy kod aplikacji musi ręcznie tworzyć i usuwać wystąpienia kontekstu.

services.AddDbContextFactory<SomeDbContext>(b =>
    b.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=Test"));

W tym momencie usługi aplikacji, takie jak kontrolery ASP.NET Core, można następnie wprowadzać IDbContextFactory<TContext>za pomocą polecenia i używać ich do tworzenia wystąpień kontekstu:

public class MyController : Controller
{
    private readonly IDbContextFactory<SomeDbContext> _contextFactory;

    public MyController(IDbContextFactory<SomeDbContext> contextFactory)
        => _contextFactory = contextFactory;

    public void DoSomeThing()
    {
        using (var context = _contextFactory.CreateDbContext())
        {
            // ...
        }
    }
}

Aby uzyskać więcej informacji, zobacz pełną dokumentację dotyczącą elementu DbContextFactory.

Kompilowanie tabeli SQLite

W porównaniu z innymi bazami danych sqLite jest stosunkowo ograniczona w możliwościach manipulowania schematem; na przykład usunięcie kolumny z istniejącej tabeli nie jest obsługiwane. Program EF Core 5.0 działa w ramach tych ograniczeń, automatycznie tworząc nową tabelę, kopiując dane ze starej tabeli, upuszczając starą tabelę i zmieniając nazwę nowej. To "ponownie kompiluje" tabelę i umożliwia bezpieczne stosowanie wcześniej nieobsługiwanych operacji migracji.

Aby uzyskać szczegółowe informacje o tym, które operacje migracji są teraz obsługiwane za pośrednictwem ponownych kompilacji tabeli, zobacz tę stronę dokumentacji.

Sortowania baz danych

Program EF Core 5.0 wprowadza obsługę określania sortowania tekstu na poziomie bazy danych, kolumny lub zapytania. Dzięki temu można skonfigurować wielkość liter i inne aspekty tekstowe w sposób elastyczny i nie narusza wydajności zapytań.

Na przykład następujące polecenie skonfiguruje kolumnę Name tak, aby uwzględniała wielkość liter w programie SQL Server, a wszystkie indeksy utworzone w kolumnie będą działać odpowiednio:

modelBuilder
    .Entity<User>()
    .Property(e => e.Name)
    .UseCollation("SQL_Latin1_General_CP1_CS_AS");

Aby uzyskać więcej informacji, zobacz pełną dokumentację dotyczącą sortowania i poufności liter.

Liczniki zdarzeń

Program EF Core 5.0 uwidacznia liczniki zdarzeń, które mogą służyć do śledzenia wydajności aplikacji i wykrywania różnych anomalii. Wystarczy dołączyć do procesu działającego na platformie EF za pomocą narzędzia dotnet-counters :

> dotnet counters monitor Microsoft.EntityFrameworkCore -p 49496

[Microsoft.EntityFrameworkCore]
    Active DbContexts                                               1
    Execution Strategy Operation Failures (Count / 1 sec)           0
    Execution Strategy Operation Failures (Total)                   0
    Optimistic Concurrency Failures (Count / 1 sec)                 0
    Optimistic Concurrency Failures (Total)                         0
    Queries (Count / 1 sec)                                     1,755
    Queries (Total)                                            98,402
    Query Cache Hit Rate (%)                                      100
    SaveChanges (Count / 1 sec)                                     0
    SaveChanges (Total)                                             1

Aby uzyskać więcej informacji, zobacz pełną dokumentację dotyczącą liczników zdarzeń.

Inne funkcje

Kompilowanie modelu

  • Interfejsy API tworzenia modeli zostały wprowadzone w celu łatwiejszej konfiguracji porównań wartości.
  • Obliczone kolumny można teraz skonfigurować jako przechowywane lub wirtualne.
  • Precyzja i skala można teraz skonfigurować za pośrednictwem interfejsu API Fluent.
  • Wprowadzono nowe interfejsy API tworzenia modelu dla właściwości nawigacji.
  • Wprowadzono nowe interfejsy API tworzenia modelu dla pól, podobnie jak właściwości.
  • Typy .NET PhysicalAddress i IPAddress można teraz mapować na kolumny ciągów bazy danych.
  • Pole kopii zapasowej można teraz skonfigurować za pomocą nowego [BackingField] atrybutu.
  • Pola kopii zapasowej z możliwością wartości null są teraz dozwolone, zapewniając lepszą obsługę domyślnych wartości generowanych przez magazyn, w których wartość domyślna CLR nie jest dobrą wartością sentinel (godną booluwagi).
  • Nowy [Index] atrybut można użyć na typie jednostki, aby określić indeks, zamiast używać interfejsu API Fluent.
  • Nowy [Keyless] atrybut może służyć do konfigurowania typu jednostki jako bez klucza.
  • Domyślnie program EF Core traktuje teraz dyskryminujące jako kompletne, co oznacza, że oczekuje, że nigdy nie widzi wartości dyskryminujących, które nie są skonfigurowane przez aplikację w modelu. Dzięki temu można poprawić wydajność i można je wyłączyć, jeśli kolumna dyskryminująca może zawierać nieznane wartości.

Zapytanie

  • Wyjątki błędów tłumaczenia zapytań zawierają teraz bardziej wyraźne przyczyny niepowodzenia, aby pomóc w określeniu problemu.
  • Zapytania śledzenia nie mogą teraz wykonywać rozpoznawania tożsamości, co pozwala uniknąć zwracania wielu wystąpień jednostki dla tego samego obiektu bazy danych.
  • Dodano obsługę funkcji GroupBy z agregacjami warunkowymi (np. GroupBy(o => o.OrderDate).Select(g => g.Count(i => i.OrderDate != null))).
  • Dodano obsługę tłumaczenia operatora Distinct na elementy grupy przed agregacji.
  • Tłumaczenie elementu Reverse.
  • Ulepszone tłumaczenie DateTime dla programu SQL Server (np. DateDiffWeek, DateFromParts).
  • Tłumaczenie nowych metod na tablicach bajtów (np. Contains, Length, SequenceEqual).
  • Tłumaczenie niektórych dodatkowych operatorów bitowych, takich jak uzupełnienie dwóch.
  • FirstOrDefault Translacja ciągów.
  • Ulepszone tłumaczenie zapytań wokół semantyki o wartości null, co powoduje ostrzejsze i bardziej wydajne zapytania.
  • Funkcje mapowane przez użytkownika można teraz dodawać adnotacje, aby kontrolować propagację wartości null, co ponownie powoduje ostrzejsze i bardziej wydajne zapytania.
  • Baza danych SQL zawierająca bloki CASE jest teraz znacznie bardziej zwięzła.
  • Funkcja programu SQL Server DATALENGTH może być teraz wywoływana w zapytaniach za pośrednictwem nowej EF.Functions.DataLength metody.
  • EnableDetailedErrors dodaje dodatkowe szczegóły do wyjątków.

Zapisywanie

  • Funkcja SaveChanges przechwytuje i zdarzenia.
  • Wprowadzono interfejsy API do kontrolowania punktów zapisywania transakcji. Ponadto program EF Core automatycznie utworzy punkt zapisywania, gdy SaveChanges jest wywoływany, a transakcja jest już w toku i cofa się do niej w przypadku awarii.
  • Identyfikator transakcji można jawnie ustawić przez aplikację, co ułatwia korelację zdarzeń transakcji w rejestrowaniu i w innych miejscach.
  • Domyślny maksymalny rozmiar partii dla programu SQL Server został zmieniony na 42 na podstawie analizy wydajności dzielenia na partie.

Migracje i tworzenie szkieletów

  • Tabele można teraz wykluczyć z migracji.
  • Nowe dotnet ef migrations list polecenie pokazuje teraz, które migracje nie zostały jeszcze zastosowane do bazy danych (Get-Migration robi to samo w konsoli zarządzania pakietami).
  • Skrypty migracji zawierają teraz instrukcje transakcji tam, gdzie jest to właściwe, aby poprawić przypadki obsługi, w których aplikacja migracji kończy się niepowodzeniem.
  • Kolumny dla niemapowanych klas bazowych są teraz uporządkowane po innych kolumnach dla mapowanych typów jednostek. Należy pamiętać, że ma to wpływ tylko na nowo utworzone tabele; kolejność kolumn dla istniejących tabel pozostaje niezmieniona.
  • Generowanie migracji może teraz mieć świadomość, czy generowana migracja jest idempotentna i czy dane wyjściowe zostaną wykonane natychmiast, czy wygenerowane jako skrypt.
  • Dodano nowe parametry wiersza polecenia do określania przestrzeni nazw w obszarze Migracje i tworzenie szkieletów.
  • Polecenie dotnet ef database update akceptuje teraz nowy --connection parametr określający parametry połączenia.
  • Tworzenie szkieletu istniejących baz danych teraz klasyfikuje nazwy tabel, więc tabele o nazwie i będą szkieletowe dla typów jednostek o nazwie PeoplePerson i AddressesAddress. Oryginalne nazwy baz danych nadal można zachować.
  • Nowa --no-onconfiguring opcja może instruować program EF Core, aby wykluczył OnConfiguring podczas tworzenia szkieletu modelu.

Azure Cosmos DB

Sqlite

  • Kolumny obliczane są teraz obsługiwane.
  • Pobieranie danych binarnych i ciągów przy użyciu metod GetBytes, GetChars i GetTextReader jest teraz bardziej wydajne dzięki użyciu obiektów SqliteBlob i strumieni.
  • Inicjowanie sqlite Połączenie ion jest teraz leniwe.

Inne

  • Można wygenerować serwery proxy śledzenia zmian, które automatycznie implementują interfejs INotifyPropertyChanging i INotifyPropertyChanged. Zapewnia to alternatywne podejście do śledzenia zmian, które nie skanuje pod kątem zmian po SaveChanges wywołaniu.
  • Można teraz zmienić obiekt DbConnection lub parametry połączenia w już zainicjowanym obiekcie DbContext.
  • Nowa ChangeTracker.Clear metoda czyści element DbContext wszystkich śledzonych jednostek. Zwykle nie należy tego używać podczas tworzenia nowego, krótkotrwałego wystąpienia kontekstu dla każdej jednostki pracy. Jeśli jednak istnieje potrzeba zresetowania stanu wystąpienia DbContext, użycie nowej Clear() metody jest bardziej wydajne i niezawodne niż masowe odłączanie wszystkich jednostek.
  • Narzędzia wiersza polecenia platformy EF Core automatycznie konfigurują ASPNETCORE_ENVIRONMENTzmienne środowiskowe iDOTNET_ENVIRONMENT na "Programowanie". Zapewnia to doświadczenie podczas korzystania z hosta ogólnego zgodnie z doświadczeniem ASP.NET Core podczas programowania.
  • Niestandardowe argumenty wiersza polecenia można przepływać do IDesignTimeDbContextFactory<TContext>elementu , co umożliwia aplikacjom kontrolowanie sposobu tworzenia i inicjowania kontekstu.
  • Współczynnik wypełnienia indeksu można teraz skonfigurować w programie SQL Server.
  • Nowa IsRelational właściwość może służyć do rozróżniania w przypadku używania dostawcy relacyjnego i dostawcy nierelacyjnego (takiego jak w pamięci).