Uaktualnianie aplikacji z poprzednich wersji do programu EF Core 2.0

Podjęliśmy możliwość znacznego uściślinia istniejących interfejsów API i zachowań w wersji 2.0. Istnieje kilka ulepszeń, które mogą wymagać modyfikacji istniejącego kodu aplikacji, chociaż uważamy, że w przypadku większości aplikacji wpływ będzie niski, w większości przypadków wymagających ponownej kompilacji i minimalnych zmian z przewodnikiem w celu zastąpienia przestarzałych interfejsów API.

Aktualizowanie istniejącej aplikacji do programu EF Core 2.0 może wymagać:

  1. Uaktualnienie docelowej implementacji platformy .NET aplikacji do tej, która obsługuje platformę .NET Standard 2.0. Aby uzyskać więcej informacji, zobacz Obsługiwane implementacje platformy .NET.

  2. Zidentyfikuj dostawcę docelowej bazy danych zgodnej z programem EF Core 2.0. Zobacz EF Core 2.0 wymaga poniższego dostawcy bazy danych w wersji 2.0.

  3. Uaktualnienie wszystkich pakietów EF Core (środowisko uruchomieniowe i narzędzia) do wersji 2.0. Aby uzyskać więcej informacji, zobacz Instalowanie programu EF Core.

  4. Wprowadź wszelkie niezbędne zmiany kodu, aby zrekompensować zmiany powodujące niezgodność opisane w pozostałej części tego dokumentu.

ASP.NET Core obejmuje teraz platformę EF Core

Aplikacje przeznaczone dla platformy ASP.NET Core 2.0 mogą używać programu EF Core 2.0 bez dodatkowych zależności oprócz dostawców baz danych innych firm. Jednak aplikacje przeznaczone dla poprzednich wersji platformy ASP.NET Core muszą przeprowadzić uaktualnienie do wersji ASP.NET Core 2.0 w celu korzystania z programu EF Core 2.0. Aby uzyskać więcej informacji na temat uaktualniania aplikacji ASP.NET Core do wersji 2.0, zobacz dokumentację ASP.NET Core dotyczącą tematu.

Nowy sposób pobierania usług aplikacji w usłudze ASP.NET Core

Zalecany wzorzec dla aplikacji internetowych platformy ASP.NET Core został zaktualizowany dla wersji 2.0 w sposób, który złamał logikę czasu projektowania platformy EF Core używaną w wersji 1.x. Wcześniej w czasie projektowania program EF Core próbował wywołać Startup.ConfigureServices go bezpośrednio w celu uzyskania dostępu do dostawcy usług aplikacji. W ASP.NET Core 2.0 konfiguracja jest inicjowana poza klasą Startup . Aplikacje korzystające z platformy EF Core zwykle uzyskują dostęp do parametry połączenia z poziomu konfiguracji, więc Startup samo w sobie nie jest już wystarczające. W przypadku uaktualniania aplikacji ASP.NET Core 1.x podczas korzystania z narzędzi EF Core może wystąpić następujący błąd.

Nie znaleziono konstruktora bez parametrów w obiekcie "ApplicationContext". Dodaj konstruktor bez parametrów do elementu "ApplicationContext" lub dodaj implementację elementu "IDesignTimeDbContextFactory<ApplicationContext>" w tym samym zestawie co "ApplicationContext"

Nowy element zaczepienia w czasie projektowania został dodany w domyślnym szablonie ASP.NET Core 2.0. Metoda statyczna Program.BuildWebHost umożliwia programowi EF Core uzyskiwanie dostępu do dostawcy usług aplikacji w czasie projektowania. W przypadku uaktualniania aplikacji ASP.NET Core 1.x należy zaktualizować klasę Program tak, aby była podobna do poniższej.

using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;

namespace AspNetCoreDotNetCore2._0App
{
    public class Program
    {
        public static void Main(string[] args)
        {
            BuildWebHost(args).Run();
        }

        public static IWebHost BuildWebHost(string[] args) =>
            WebHost.CreateDefaultBuilder(args)
                .UseStartup<Startup>()
                .Build();
    }
}

Wdrożenie tego nowego wzorca podczas aktualizowania aplikacji do wersji 2.0 jest zdecydowanie zalecane i jest wymagane w celu działania funkcji produktu, takich jak migracje platformy Entity Framework Core. Inną typową alternatywą jest zaimplementowanie elementu IDesignTimeDbContextFactory TContexty<>.

Zmieniono nazwę elementu IDbContextFactory

Aby obsługiwać różne wzorce aplikacji i zapewnić użytkownikom większą kontrolę nad sposobem DbContext ich użycia w czasie projektowania, w przeszłości udostępniliśmy IDbContextFactory<TContext> interfejs. W czasie projektowania narzędzia EF Core odnajdą implementacje tego interfejsu w projekcie i używają ich do tworzenia DbContext obiektów.

Ten interfejs miał bardzo ogólną nazwę, która wprowadziła niektórych użytkowników w błąd, aby spróbować ponownie użyć go w przypadku innych DbContextscenariuszy tworzenia. Zostali złapani z osłony, gdy narzędzia EF Tools próbowały użyć ich implementacji w czasie projektowania i spowodowały, że polecenia takie jak Update-Database lub dotnet ef database update kończą się niepowodzeniem.

Aby komunikować się z silnymi semantykami czasu projektowania tego interfejsu, zmieniliśmy jego nazwę na IDesignTimeDbContextFactory<TContext>.

W przypadku wersji 2.0 nadal istnieje, IDbContextFactory<TContext> ale jest ona oznaczona jako przestarzała.

Usunięto element DbContextFactoryOptions

Ze względu na opisane powyżej zmiany ASP.NET Core 2.0 ustaliliśmy, że DbContextFactoryOptions nie jest już potrzebne w nowym IDesignTimeDbContextFactory<TContext> interfejsie. Oto alternatywy, których należy użyć zamiast tego.

DbContextFactoryOptions Alternatywne rozwiązanie
ApplicationBasePath AppContext.BaseDirectory
ContentRootPath Directory.GetCurrentDirectory()
NazwaŚrodowiska Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")

Katalog roboczy w czasie projektowania został zmieniony

Zmiany ASP.NET Core 2.0 również wymagały, aby katalog roboczy używany przez dotnet ef program Visual Studio był zgodny z katalogiem roboczym używanym przez program Visual Studio podczas uruchamiania aplikacji. Jednym z zauważalnych skutków ubocznych jest to, że nazwy plików SQLite są teraz względem katalogu projektu, a nie katalogu wyjściowego, jak kiedyś.

Program EF Core 2.0 wymaga dostawcy bazy danych w wersji 2.0

W przypadku platformy EF Core 2.0 wprowadziliśmy wiele ułatwień i ulepszeń w sposobie działania dostawców baz danych. Oznacza to, że dostawcy 1.0.x i 1.1.x nie będą działać z programem EF Core 2.0.

Dostawcy programu SQL Server i SQLite są dostarczane przez zespół EF, a wersje 2.0 będą dostępne w ramach wersji 2.0. Dostawcy innych firm typu open source dla programów SQL Compact, PostgreSQL i MySQL są aktualizowani dla wersji 2.0. W przypadku wszystkich innych dostawców skontaktuj się z modułem zapisywania dostawcy.

Zdarzenia rejestrowania i diagnostyki uległy zmianie

Uwaga: te zmiany nie powinny mieć wpływu na większość kodu aplikacji.

Identyfikatory zdarzeń dla komunikatów wysyłanych do ILogger zostały zmienione w wersji 2.0. Identyfikatory zdarzeń są teraz unikatowe w całym kodzie rozwiązania EF Core. Te komunikaty są teraz również zgodne ze standardowym wzorcem rejestrowania strukturalnego używanym przez np. wzorzec MVC.

Kategorie rejestratorów również uległy zmianie. Istnieje teraz dobrze znany zestaw kategorii, do których można uzyskać dostęp za pośrednictwem usługi DbLoggerCategory.

DiagnosticSource zdarzenia używają teraz tych samych nazw identyfikatorów zdarzeń co odpowiadające im ILogger komunikaty. Ładunki zdarzeń są wszystkimi typami nominalnymi pochodzącymi z eventData.

Identyfikatory zdarzeń, typy ładunków i kategorie są udokumentowane w klasach CoreEventId i RelationalEventId .

Identyfikatory zostały również przeniesione z microsoft.EntityFrameworkCore.Infrastructure do nowej przestrzeni nazw Microsoft.EntityFrameworkCore.Diagnostics.

Zmiany interfejsu API metadanych relacyjnych platformy EF Core

Rozwiązanie EF Core 2.0 tworzy teraz inny interfejs IModel dla każdego używanego dostawcy. Jest to zwykle transparentne dla aplikacji. Ułatwiło to uproszczenie interfejsów API metadanych niższego poziomu, tak aby każdy dostęp do typowych pojęć związanych z metadanymi relacyjnymi był zawsze wykonywane za pomocą wywołania metody .Relational zamiast .SqlServer, .Sqliteitp. Na przykład kod 1.1.x podobny do następującego:

var tableName = context.Model.FindEntityType(typeof(User)).SqlServer().TableName;

Powinny być teraz napisane w następujący sposób:

var tableName = context.Model.FindEntityType(typeof(User)).Relational().TableName;

Zamiast używać metod, takich jak ForSqlServerToTable, metody rozszerzenia są teraz dostępne do pisania kodu warunkowego na podstawie bieżącego dostawcy w użyciu. Przykład:

modelBuilder.Entity<User>().ToTable(
    Database.IsSqlServer() ? "SqlServerName" : "OtherName");

Należy pamiętać, że ta zmiana dotyczy tylko interfejsów API/metadanych zdefiniowanych dla wszystkich dostawców relacyjnych. Interfejs API i metadane pozostają takie same, gdy są specyficzne dla tylko jednego dostawcy. Na przykład indeksy klastrowane są specyficzne dla serwera SQL, więc ForSqlServerIsClustered.SqlServer().IsClustered() i nadal muszą być używane.

Nie przejmij kontroli nad dostawcą usług EF

Program EF Core używa wewnętrznego IServiceProvider (kontenera iniekcji zależności) do wewnętrznej implementacji. Aplikacje powinny zezwalać programowi EF Core na tworzenie tego dostawcy i zarządzanie nim, z wyjątkiem przypadków specjalnych. Zdecydowanie rozważ usunięcie wszystkich wywołań do UseInternalServiceProvider. Jeśli aplikacja musi wywołać metodę UseInternalServiceProvider, rozważ zgłoszenie problemu , abyśmy mogli zbadać inne sposoby obsługi danego scenariusza.

Wywoływanie metody AddEntityFramework, AddEntityFrameworkSqlServeritp. nie jest wymagane przez kod aplikacji, chyba że UseInternalServiceProvider jest również wywoływane. Usuń wszelkie istniejące wywołania do AddEntityFramework lub AddEntityFrameworkSqlServer, itp. AddDbContext powinny być nadal używane w taki sam sposób jak poprzednio.

Bazy danych w pamięci muszą mieć nazwę

Globalna nienazwana baza danych w pamięci została usunięta, a zamiast tego wszystkie bazy danych w pamięci muszą mieć nazwę . Przykład:

optionsBuilder.UseInMemoryDatabase("MyDatabase");

Spowoduje to utworzenie/użycie bazy danych o nazwie "MyDatabase". Jeśli UseInMemoryDatabase zostanie wywołana ponownie o tej samej nazwie, zostanie użyta ta sama baza danych w pamięci, umożliwiając jej współużytkowanie przez wiele wystąpień kontekstu.

Operacja "Uwzględnij" dostawcy w pamięci nie zwraca już wyników, jeśli dołączona nawigacja jest wymagana, ale jej wartość ma wartość null

Podczas próby dołączenia wymaganej nawigacji, a dołączona nawigacja ma wartość null, zapytanie nie zwraca już wyniku dla jednostki, na której jest stosowana operacja Dołączanie. Aby uniknąć tego problemu, podaj wartość wymaganej nawigacji lub zmień nawigację na opcjonalną.

public class Person
{
    public int Id { get; set; }
    public Language NativeLanguage { get; set;} // required navigation
    public Person Sibling { get; set; } // optional navigation
}
...
var person = new Person();
context.People.Add(person);
context.SaveChanges();
...

// returns one result
context.People.ToList();

// returns no results because 'NativeLanguage' navigation is required but has not been provided
context.People.Include(p => p.NativeLanguage).ToList(); 

// returns one result because 'Sibling' navigation is optional so it doesn't have to be provided
context.People.Include(p => p.Sibling).ToList(); 

Zmiany interfejsu API tylko do odczytu

IsReadOnlyBeforeSave, IsReadOnlyAfterSavei zostały przestarzałe i IsStoreGeneratedAlways zastąpione beforeSaveBehavior i AfterSaveBehavior. Te zachowania mają zastosowanie do dowolnej właściwości (nie tylko właściwości wygenerowanej przez magazyn) i określają, jak wartość właściwości należy użyć podczas wstawiania do wiersza bazy danych (BeforeSaveBehavior) lub podczas aktualizowania istniejącego wiersza bazy danych (AfterSaveBehavior).

Właściwości oznaczone jako ValueGenerated.OnAddOrUpdate (na przykład dla kolumn obliczeniowych) domyślnie ignorują dowolną wartość ustawioną obecnie dla właściwości . Oznacza to, że wartość wygenerowana przez magazyn będzie zawsze uzyskiwana niezależnie od tego, czy jakakolwiek wartość została ustawiona, czy zmodyfikowana w śledzonej jednostce. Można to zmienić, ustawiając inny Before\AfterSaveBehaviorelement .

Nowe zachowanie usuwania ClientSetNull

W poprzednich wersjach funkcja DeleteBehavior.Restrict miała zachowanie dla jednostek śledzonych przez kontekst, który bardziej zamknięty pasował SetNull do semantyki. W programie EF Core 2.0 wprowadzono nowe ClientSetNull zachowanie jako domyślne dla relacji opcjonalnych. To zachowanie ma SetNull semantyka śledzonych jednostek i Restrict zachowania baz danych utworzonych przy użyciu programu EF Core. W naszym środowisku są to najbardziej oczekiwane/przydatne zachowania dotyczące śledzonych jednostek i bazy danych. DeleteBehavior.Restrict Jest teraz honorowany dla śledzonych jednostek po ustawieniu dla relacji opcjonalnych.

Usunięte pakiety czasu projektowania dostawcy

Pakiet Microsoft.EntityFrameworkCore.Relational.Design został usunięty. Zawartość została skonsolidowana w plikach Microsoft.EntityFrameworkCore.Relational i Microsoft.EntityFrameworkCore.Design.

Jest to propagowane do pakietów czasu projektowania dostawcy. Te pakiety (Microsoft.EntityFrameworkCore.Sqlite.Design, Microsoft.EntityFrameworkCore.SqlServer.Designitp.) zostały usunięte, a ich zawartość została skonsolidowana w głównych pakietach dostawcy.

Aby włączyć Scaffold-DbContext program EF Core 2.0 lub dotnet ef dbcontext scaffold w wersji 2.0, wystarczy odwołać się tylko do pojedynczego pakietu dostawcy:

<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer"
    Version="2.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools"
    Version="2.0.0"
    PrivateAssets="All" />
<DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet"
    Version="2.0.0" />