Architektura nástrojů pro návrh

Nástroje pro návrh jsou součástí EF, které spouští operace návrhu, jako je vygenerování modelu nebo správa migrací. Zodpovídají za vytvoření instance objektů DbContext pro použití v době návrhu.

Mermaid diagram toku nástrojů vysoké úrovně.

Existují dva primární vstupní body: dotnet-ef a nástroje Správce balíčků NuGet (PMC) EF Core. Obě tyto jsou zodpovědné za shromažďování informací o projektech uživatele, jejich kompilaci a následné volání ef.exe, které nakonec volá vstupní body v době návrhu uvnitř EFCore.Design.dll.

dotnet-ef

dotnet-ef je nástroj .NET . Předpona dotnet- umožňuje vyvolání jako součást hlavního příkazu dotnet: dotnet ef.

Tento příkaz má dva primární vstupy: spouštěný projekt a cílový projekt. dotnet-ef zodpovídá za čtení informací o těchto projektech a jejich kompilaci.

diagramu toku dotnet-ef.

Čte informace o projektech vložením souboru .targets pro MSBuild a voláním vlastního cíle nástroje MSBuild. Soubor .targets je zkompilován do dotnet-ef jako vložený prostředek. Zdroj se nachází v src/dotnet-ef/Resources/EntityFrameworkCore.targets.

Na začátku má určitou logiku pro obsluhu projektů s více cíli. V podstatě jednoduše vybere první cílovou architekturu a znovu vyvolá sama sebe. Po určení jediného cílového rámce se mu přiřadí několik vlastností MSBuild, jako je AssemblyName, OutputPath, RootNamespace atd.

Po shromáždění informací o projektu zkompilujeme spouštěný projekt. Předpokládáme, že cílový projekt bude také zkompilován tranzitivně.

Pak dotnet-ef vyvolá ef.exe.

Nástroje PMC

Nástroje PMC provádějí podobnou funkci jako dotnet-ef, ale místo nástroje MSBuild používají rozhraní API sady Visual Studio. Dodávají se jako balíček Microsoft.EntityFrameworkCore.Tools. Jedná se o speciální modul PowerShellu, který se automaticky načte do konzoly správce balíčků NuGet prostřednictvím init.ps1. Podobně jako dotnet-ef každý příkaz má dva primární vstupy: spouštěcí projekt a cílový projekt. Výchozí hodnoty z těchto hodnot jsou ale převzaty z integrovaného vývojového prostředí (IDE). Cílový projekt se ve výchozím nastavení nastaví na projekt zadaný jako Výchozí projekt uvnitř konzoly Správce balíčků. Ve výchozím nastavení je spouštěcím projektem ten, který je určen jako spouštěcí projekt (prostřednictvím Nastavit jako spouštěný projekt) v Průzkumníku řešení.

Diagram toku nástrojů PMC vytvořený pomocí Mermaid.

Nástroje PMC shromažďují informace o projektech prostřednictvím rozhraní API EnvDTE, kdykoli je to možné. V některých případech je potřeba přejít na systém běžných projektů (CPS) nebo API MSBuild. Moderní zdroj implementace systému projektů C# je k dispozici v projektu dotnet/project-system na GitHubu.

Po shromáždění informací sestaví celé řešení.

Spropitné

Problém #9716 se týká aktualizace pouze pro sestavení spouštěcího projektu.

Podobně jako dotnet-ef pak vyvolá ef.exe. Nástroje PMC obsahují určitý dodatečný mechanismus po vyvolání ef.exe, který otevírá všechny soubory vytvořené příkazem, aby uživatelský zážitek byl více integrovaný.

ef.exe

Někdy se označuje jako interní modul, ef.exe (kvůli nedostatku lepšího názvu) je dodáván jako součást nástrojů dotnet ef a PMC Tools jako sada binárních souborů. Existují různé binární soubory pro různé cílové architektury a platformy.

  • nářadí/
    • net461/
      • jakýkoliv/
        • ef.exe
      • win-x86/
        • ef.exe
      • win-arm64/
        • ef.exe
    • netcoreapp2.0/
      • jakýkoliv/
        • ef.dll

Sestavení rozhraní .NET Framework jsou vyvolána pouze pro projekty EF Core 3.1 a starší verze cílené na rozhraní .NET Framework. V projektech, které používají starší verze EF, můžete použít nejnovější verzi nástrojů. Neexistuje žádná x64, protože sestavení pod jakýmkoli adresářem cílí na platformu AnyCPU, která běží jako x64 ve verzích x64 i arm64 systému Windows.

Sestavení .NET Core 2.0 se používá pro projekty, které cílí na .NET Core nebo .NET 5 a novější.

Primární odpovědností ef.exe je načíst výstupní sestavení spouštěcího projektu a vyvolat návrhové vstupní body uvnitř EFCore.Design.dll.

V rozhraní .NET Framework používáme samostatnou doménu AppDomain k načtení sestavení projektu tak, že předáme soubor projektu App/Web.config, aby byla respektována případná přesměrování vazeb přidaná NuGetem nebo uživatelem.

V .NET Core/5 nebo novějších vyvoláme ef.dll pomocí souborů projektu .deps.json a .runtimeconfig.json k emulaci skutečného chování runtime modulu a načítání sestavení projektu.

dotnet exec ef.dll --depsfile startupProject.deps.json --runtimeconfig startupProject.runtimeconfig.json

Spropitné

Problém #18840 se zaměřuje především na použití AssemblyLoadContext místo dotnet exec k načtení sestavení uživatele. Díky tomu by měly nástroje pracovat s více typy projektů, včetně těch, které cílí na Android a iOS.

Po připravení všeho k načtení, provede ef.exe volání do EFCore.Design.dll pomocí reflexe a Activator.CreateInstance (nebo AppDomain.CreateInstance v rozhraní .NET Framework).

EFCore.Design.dll

EFCore.Design.dllnebo přesněji, Microsoft.EntityFrameworkCore.Design.dll obsahuje veškerou logiku návrhu pro EF Core. Všechny vstupní body jsou uvnitř třídy OperationExecutor. Hodně podivnosti v návrhu této třídy (MarshallByRefObject, vnořené typy atd.) vychází z nutnosti vyvolat ji napříč doménami AppDomains v rozhraní .NET Framework. Pokud byl tento požadavek odebrán, může se hodně zjednodušit. Všechny podpisy jsou slabě typované, aby byla zajištěna dopředná a zpětná kompatibilita s nástroji. Mějte na paměti, že různé verze nástrojů lze použít k vyvolání projektů pomocí různých verzí EF.

Kromě exekutoru DbContextActivator je dalším důležitým typem v tomto sestavení. Komponenty některých webových nástrojů ASP.NET se používají k instanciaci uživatelského DbContextu během návrhové fáze.

Vytvoření DbContext

Před spuštěním konkrétní logiky návrhu se obvykle vyžaduje instance DbContext. Uživatel může zadat jednoduchý nebo plně kvalifikovaný název typu bez rozlišování velkých a malých písmen pro DbContext, nebo nemůže zadat jeden, pokud existuje pouze jeden typ DbContext. V obou směrech musíme zjistit všechny typy DbContext, než ho zužíme na jeden. Logika pro zjišťování typů DbContext žije v metodě FindContextTypes DbContextOperations.

Vyhledáme typy DbContext pomocí různých zdrojů.

  • Odkazované implementace IDesignTimeDbContextFactory<T> ve startovním sestavení.
  • DbContexty přidané do poskytovatele aplikačních služeb. Abychom získali seznam všech typů kontextu, získáme vše zaregistrované jako DbContextOptions a podíváme se na vlastnost ContextType. (Podívejte se níže, jak získáme poskytovatele aplikačních služeb.)
  • Typy odvozené z DbContextu ve spouštěcích i cílových sestaveních

Používáme také různé způsoby vytvoření instance typu. Tady jsou v pořadí podle priority.

  1. Použití implementace IDesignTimeDbContextFactory<T>
  2. Použití IDbContextFactory<T> od poskytovatele aplikační služby
  3. Použití ActivatorUtilities.CreateInstance

Vyhledání aplikačních služeb

Kvůli vysoké věrnosti chování modulu runtime se pokusíme získat instanci DbContext přímo od poskytovatele aplikační služby. Tuto logiku sdílíme s nástroji ASP.NET Core. Udržuje se jako součást projektu dotnet/runtime na GitHubu v rámci adresáře Microsoft.Extensions.HostFactoryResolver.

Tady je několik strategií, které používá.

  • Vyhledejte metodu s názvem BuildWebHost, CreateWebHostBuilder nebo CreateHostBuilder vedle vstupního bodu sestavení.
    • Postavte hostitele a získejte služby z vlastnosti Služby
  • Volání vstupního bodu sestavení
    • Zachytit služby během sestavování hostitele a ukončit je před skutečným spuštěním hostitele.

Služby v době návrhu

Kromě aplikačních služeb a interních služeb DbContext existuje třetí sada služby v době návrhu. Nejsou přidány k internímu poskytovateli služeb, protože nejsou nikdy potřeba v době běhu programu. Služby doby návrhu jsou sestaveny DesignTimeServicesBuilder. Existují dvě hlavní cesty – jedna s instancí kontextu a jedna bez ní. Ten bez se primárně používá při generování nového dbContext. Existuje několik bodů rozšiřitelnosti, které uživatelům, poskytovatelům a rozšířením umožňují přepsat a přizpůsobit služby.

Uživatel může služby přizpůsobit přidáním implementace IDesignTimeServices do spouštěcího sestavení.

Poskytovatelé mohou služby přizpůsobit přidáním atributu DesignTimeProviderServices do sestavení. To odkazuje na implementaci IDesignTimeServices.

Rozšíření mohou přizpůsobit služby přidáním atributů DesignTimeServicesReference do cílového nebo spouštěcího sestavení. Pokud atribut určuje zprostředkovatele, přidá se pouze v případě, že se tento zprostředkovatel používá.

Protokolování a výjimky

Po instanciaci DbContext připojíme jeho protokolování na výstup nástroje. To umožňuje vygenerovat výstup z modulu runtime. Všechny neošetřené výjimky budou také zapsány do výstupu. Existuje zvláštní typ výjimky OperationException, který může být vyvolán k řádnému ukončení práce nástrojů a zobrazení jednoduché chybové zprávy bez trasování zásobníku.