Generování uživatelského rozhraní (zpětná analýza)

Zpětná analýza je proces generování tříd typů entit a DbContext třídy založené na schématu databáze. Analýza se provádí příkazem Scaffold-DbContext v nástrojích PMC (Package Manager Console) mapovače EF Core nebo příkazem dotnet ef dbcontext scaffold v nástrojích CLI (rozhraní příkazového řádku).

Poznámka:

Generování DbContext typů entit a typů entit zdokumentovaných zde se liší od generování řadičů v ASP.NET Core pomocí sady Visual Studio, které zde není zdokumentované.

Tip

Pokud používáte Visual Studio, vyzkoušejte komunitní rozšíření EF Core Power Tools . Tyto nástroje poskytují grafický nástroj, který je založen na nástrojích příkazového řádku EF Core a nabízí další možnosti pracovního postupu a přizpůsobení.

Požadavky

  • Před generováním uživatelského rozhraní budete muset nainstalovat buď nástroje PMC, které fungují jenom v sadě Visual Studio, nebo nástroje .NET CLI, které jsou na všech platformách podporovaných rozhraním .NET.
  • Balíček NuGet pro Microsoft.EntityFrameworkCore.Design nainstalujte do projektu, do kterého chcete třídy generovat.
  • Nainstalujte balíček NuGet pro zprostředkovatele databáze, který cílí na schéma databáze, ze kterého chcete vygenerovat generování.

Požadované argumenty

Příkazy PMC i .NET CLI mají dva povinné argumenty: připojovací řetězec k databázi a zprostředkovatele databáze EF Core, který se má použít.

Connection string

Prvním argumentem příkazu je připojovací řetězec databáze. Připojovací řetězec používají nástroje ke čtení schématu databáze.

Způsob uvozování a uvozování připojovací řetězec závisí na tom, které prostředí používáte ke spuštění příkazu. Další informace najdete v dokumentaci prostředí. Například v PowerShellu musíte řídicí znak vložit před znak $, ale nikoli před znak \.

Následující příklad vygeneruje typy entit a z DbContextChinook databáze umístěné v instanci SQL Server LocalDB počítače, která využívá Microsoft.EntityFrameworkCore.SqlServer zprostředkovatele databáze.

dotnet ef dbcontext scaffold "Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=Chinook" Microsoft.EntityFrameworkCore.SqlServer

Tajné kódy uživatelů pro připojovací řetězec

Pokud máte aplikaci .NET, která používá model hostování a konfigurační systém, například projekt ASP.NET Core, můžete ke čtení připojovacího řetězce z konfigurace použít syntaxi Name=<connection-string>.

Představte si například aplikaci ASP.NET Core s následujícím konfiguračním souborem:

{
  "ConnectionStrings": {
    "Chinook": "Data Source=(localdb)\\MSSQLLocalDB;Initial Catalog=Chinook"
  }
}

Tento připojovací řetězec v konfiguračním souboru lze použít k generování uživatelského rozhraní z databáze pomocí:

dotnet ef dbcontext scaffold "Name=ConnectionStrings:Chinook" Microsoft.EntityFrameworkCore.SqlServer

Ukládání připojovací řetězec v konfiguračních souborech ale není vhodné, protože je příliš snadné je neúmyslně vystavit, například vložením do správy zdrojového kódu. Místo toho by se připojovací řetězec měly ukládat zabezpečeným způsobem, jako je použití služby Azure Key Vault nebo při práci v místním prostředí, nástroj Secret Manager( neboli "Tajné kódy uživatelů".

Pokud například chcete použít tajné kódy uživatelů, nejprve odeberte připojovací řetězec z konfiguračního souboru ASP.NET Core. Dále inicializujete tajné kódy uživatele spuštěním následujícího příkazu ve stejném adresáři jako projekt ASP.NET Core:

dotnet user-secrets init

Tento příkaz nastaví úložiště v počítači odděleně od zdrojového kódu a přidá do projektu klíč pro toto úložiště.

Dále uložte připojovací řetězec do tajných kódů uživatelů. Příklad:

dotnet user-secrets set ConnectionStrings:Chinook "Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=Chinook"

Teď stejný příkaz, který používal pojmenovaný připojovací řetězec z konfiguračního souboru, bude místo toho používat připojovací řetězec uložené v tajných klíčích uživatelů. Příklad:

dotnet ef dbcontext scaffold "Name=ConnectionStrings:Chinook" Microsoft.EntityFrameworkCore.SqlServer

Připojení ionové řetězce v kódu vygenerovaného uživatelského rozhraní

Ve výchozím nastavení bude vygenerovaný kód obsahovat připojovací řetězec, ale s upozorněním. Příklad:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
#warning To protect potentially sensitive information in your connection string, you should move it out of source code. You can avoid scaffolding the connection string by using the Name= syntax to read it from configuration - see https://go.microsoft.com/fwlink/?linkid=2131148. For more guidance on storing connection strings, see http://go.microsoft.com/fwlink/?LinkId=723263.
    => optionsBuilder.UseSqlServer("Data Source=(LocalDb)\\MSSQLLocalDB;Database=AllTogetherNow");

To se provádí tak, aby se vygenerovaný kód při prvním použití nehroutí, což by bylo velmi špatné zkušenosti s učením. Jak ale uvádí upozornění, připojovací řetězec by v produkčním kódu neměly existovat. Různé způsoby, jak je možné spravovat připojovací řetězec, najdete v tématu Životnost, Konfigurace a Inicializace DbContext.

Tip

Možnost -NoOnConfiguring (Visual Studio PMC) nebo --no-onconfiguring (.NET CLI) lze předat, aby se potlačí vytvoření OnConfiguring metody obsahující připojovací řetězec.

Název poskytovatele

Druhým argumentem je název poskytovatele. Název poskytovatele je obvykle stejný jako název poskytovatele balíčku NuGet. Například pro SQL Server nebo Azure SQL použijte Microsoft.EntityFrameworkCore.SqlServer.

Možnosti příkazového řádku

Proces generování uživatelského rozhraní je možné řídit různými možnostmi příkazového řádku.

Určení tabulek a zobrazení

Ve výchozím nastavení se všechny tabulky a zobrazení ve schématu databáze vygenerují do typů entit. Počet vygenerovaných tabulek a zobrazení můžete omezit zadáním schémat a tabulek.

Argument -Schemas (PMC) nebo --schema (.NET CLI) (Visual Studio PMC) určuje schémata tabulek a zobrazení, pro které typy entit budou generovány. Pokud tento argument vynecháte, budou zahrnuta všechna schémata. Pokud použijete tuto možnost, budou všechny tabulky a zobrazení ve schématech zahrnuty do modelu, i když nejsou explicitně zahrnuty pomocí -Tables nebo --table.

Argument -Tables (PMC) nebo --table (.NET CLI) (Visual Studio PMC) zadal tabulky a zobrazení, pro které se budou generovat typy entit. Tabulky nebo zobrazení v určitém schématu lze zahrnout pomocí formátu schema.table nebo schema.view. Pokud tuto možnost vynecháte, budou zahrnuty všechny tabulky a zobrazení. |

Pokud například chcete vygenerovat jenom tabulky Artists a Albums tabulky:

dotnet ef dbcontext scaffold ... --table Artist --table Album

Generování všech tabulek a zobrazení ze Customer schémat a Contractor schémat:

dotnet ef dbcontext scaffold ... --schema Customer --schema Contractor

Například pro generování Purchases tabulky ze schématu Customer a AccountsContracts tabulek ze schématu Contractor :

dotnet ef dbcontext scaffold ... --table Customer.Purchases --table Contractor.Accounts --table Contractor.Contracts

Zachování názvů databází

Názvy tabulek a sloupců se automaticky opraví, aby lépe odpovídaly zásadám vytváření názvů typů a vlastností. Pokud -UseDatabaseNames zadáte (Visual Studio PMC) nebo --use-database-names (.NET CLI), toto chování zakážete, aby se zachovaly původní názvy databází co nejvíce. I přesto se opraví neplatné identifikátory .NET a syntetizované názvy, například navigační vlastnosti, aby vyhovovaly zásadám vytváření názvů v .NET.

Představte si například následující tabulky:

CREATE TABLE [BLOGS] (
    [ID] int NOT NULL IDENTITY,
    [Blog_Name] nvarchar(max) NOT NULL,
    CONSTRAINT [PK_Blogs] PRIMARY KEY ([ID]));

CREATE TABLE [posts] (
    [id] int NOT NULL IDENTITY,
    [postTitle] nvarchar(max) NOT NULL,
    [post content] nvarchar(max) NOT NULL,
    [1 PublishedON] datetime2 NOT NULL,
    [2 DeletedON] datetime2 NULL,
    [BlogID] int NOT NULL,
    CONSTRAINT [PK_Posts] PRIMARY KEY ([id]),
    CONSTRAINT [FK_Posts_Blogs_BlogId] FOREIGN KEY ([BlogID]) REFERENCES [Blogs] ([ID]) ON DELETE CASCADE);

Ve výchozím nastavení se z těchto tabulek vygenerují následující typy entit:

public partial class Blog
{
    public int Id { get; set; }
    public string BlogName { get; set; } = null!;
    public virtual ICollection<Post> Posts { get; set; } = new List<Post>();
}

public partial class Post
{
    public int Id { get; set; }
    public string PostTitle { get; set; } = null!;
    public string PostContent { get; set; } = null!;
    public DateTime _1PublishedOn { get; set; }
    public DateTime? _2DeletedOn { get; set; }
    public int BlogId { get; set; }
    public virtual Blog Blog { get; set; } = null!;
    public virtual ICollection<Tag> Tags { get; set; } = new List<Tag>();
}

Použití -UseDatabaseNames nebo --use-database-names výsledky však mají následující typy entit:

public partial class BLOG
{
    public int ID { get; set; }
    public string Blog_Name { get; set; } = null!;
    public virtual ICollection<post> posts { get; set; } = new List<post>();
}

public partial class post
{
    public int id { get; set; }
    public string postTitle { get; set; } = null!;
    public string post_content { get; set; } = null!;
    public DateTime _1_PublishedON { get; set; }
    public DateTime? _2_DeletedON { get; set; }
    public int BlogID { get; set; }
    public virtual BLOG Blog { get; set; } = null!;
}

Použití atributů mapování (neboli datových poznámek)

Typy entit se ve výchozím nastavení konfigurují pomocí rozhraní APIOnModelCreating.ModelBuilder Pokud je to možné, zadejte -DataAnnotations (PMC) nebo --data-annotations (.NET Core CLI), aby místo toho používaly atributy mapování.

Při použití Fluent API se vygeneruje například toto:

entity.Property(e => e.Title)
    .IsRequired()
    .HasMaxLength(160);

Ale při použití datových poznámek se vygeneruje toto:

[Required]
[StringLength(160)]
public string Title { get; set; }

Tip

Některé aspekty modelu nelze nakonfigurovat pomocí atributů mapování. Scaffolder bude stále používat rozhraní API pro vytváření modelů ke zpracování těchto případů.

Název DbContext

Název vygenerované DbContext třídy bude ve výchozím nastavení název databáze s příponou Context . Pokud chcete zadat jiný název, použijte v PMC možnost -Context a v rozhraní příkazového řádku .NET Core použijte --context.

Cílové adresáře a obory názvů

Třídy entit a třída DbContext se vygenerují do kořenového adresáře projektu s použitím výchozího oboru názvů projektu.

Pokud chcete zadat adresář, do kterého se budou třídy generovat, použijte --output-dir. Pokud chcete generovat třídu DbContext do adresáře odděleného od tříd typů entit, použijte --context-dir:

dotnet ef dbcontext scaffold ... --context-dir Data --output-dir Models

Jako obor názvů se automaticky použije kořenový obor názvů plus názvy podadresářů v kořenovém adresáři projektu. Obor názvů pro všechny výstupní třídy však můžete přepsat pomocí .--namespace K přepsání oboru názvů jen u třídy DbContext také můžete použít --context-namespace:

dotnet ef dbcontext scaffold ... --namespace Your.Namespace --context-namespace Your.DbContext.Namespace

Vygenerovaný kód

Výsledkem generování uživatelského rozhraní z existující databáze je:

  • Soubor obsahující třídu, která dědí z DbContext
  • Soubor pro každý typ entity

Tip

Od verze EF7 také můžete k přizpůsobení generovaného kódu používat šablony textu T4. Podrobnější informace najdete v šablonách vlastní zpětné analýzy.

Referenční typy C# s možnou hodnotou Null

Scaffolder může vytvořit model EF a typy entit, které používají odkazové typy C# s možnou hodnotou null (NRT). Použití NRT se automaticky vygeneruje, když je v projektu C# povolená podpora NRT, do kterého se kód vygeneruje.

Například následující Tags tabulka obsahuje oba sloupce řetězců s možnou hodnotou null:

CREATE TABLE [Tags] (
  [Id] int NOT NULL IDENTITY,
  [Name] nvarchar(max) NOT NULL,
  [Description] nvarchar(max) NULL,
  CONSTRAINT [PK_Tags] PRIMARY KEY ([Id]));

Výsledkem jsou odpovídající vlastnosti řetězce s možnou hodnotou null a nenulovou hodnotou ve vygenerované třídě:

public partial class Tag
{
    public Tag()
    {
        Posts = new HashSet<Post>();
    }

    public int Id { get; set; }
    public string Name { get; set; } = null!;
    public string? Description { get; set; }

    public virtual ICollection<Post> Posts { get; set; }
}

Podobně následující Posts tabulky obsahují požadovanou relaci s Blogs tabulkou:

CREATE TABLE [Posts] (
    [Id] int NOT NULL IDENTITY,
    [Title] nvarchar(max) NOT NULL,
    [Contents] nvarchar(max) NOT NULL,
    [PostedOn] datetime2 NOT NULL,
    [UpdatedOn] datetime2 NULL,
    [BlogId] int NOT NULL,
    CONSTRAINT [PK_Posts] PRIMARY KEY ([Id]),
    CONSTRAINT [FK_Posts_Blogs_BlogId] FOREIGN KEY ([BlogId]) REFERENCES [Blogs] ([Id]));

Výsledkem je generování relace bez hodnoty null (povinné) mezi blogy:

public partial class Blog
{
    public Blog()
    {
        Posts = new HashSet<Post>();
    }

    public int Id { get; set; }
    public string Name { get; set; } = null!;

    public virtual ICollection<Post> Posts { get; set; }
}

A příspěvky:

public partial class Post
{
    public Post()
    {
        Tags = new HashSet<Tag>();
    }

    public int Id { get; set; }
    public string Title { get; set; } = null!;
    public string Contents { get; set; } = null!;
    public DateTime PostedOn { get; set; }
    public DateTime? UpdatedOn { get; set; }
    public int BlogId { get; set; }

    public virtual Blog Blog { get; set; } = null!;

    public virtual ICollection<Tag> Tags { get; set; }
}

Relace M:N

Proces generování uživatelského rozhraní rozpozná jednoduché tabulky spojení a automaticky pro ně vygeneruje mapování M:N. Zvažte například tabulky pro Posts tabulky a Tagsspojovací tabulku PostTag , která je propojí:

CREATE TABLE [Tags] (
  [Id] int NOT NULL IDENTITY,
  [Name] nvarchar(max) NOT NULL,
  [Description] nvarchar(max) NULL,
  CONSTRAINT [PK_Tags] PRIMARY KEY ([Id]));

CREATE TABLE [Posts] (
    [Id] int NOT NULL IDENTITY,
    [Title] nvarchar(max) NOT NULL,
    [Contents] nvarchar(max) NOT NULL,
    [PostedOn] datetime2 NOT NULL,
    [UpdatedOn] datetime2 NULL,
    CONSTRAINT [PK_Posts] PRIMARY KEY ([Id]));

CREATE TABLE [PostTag] (
    [PostsId] int NOT NULL,
    [TagsId] int NOT NULL,
    CONSTRAINT [PK_PostTag] PRIMARY KEY ([PostsId], [TagsId]),
    CONSTRAINT [FK_PostTag_Posts_TagsId] FOREIGN KEY ([TagsId]) REFERENCES [Tags] ([Id]) ON DELETE CASCADE,
    CONSTRAINT [FK_PostTag_Tags_PostsId] FOREIGN KEY ([PostsId]) REFERENCES [Posts] ([Id]) ON DELETE CASCADE);

Když se vygeneruje, výsledkem bude třída Post:

public partial class Post
{
    public Post()
    {
        Tags = new HashSet<Tag>();
    }

    public int Id { get; set; }
    public string Title { get; set; } = null!;
    public string Contents { get; set; } = null!;
    public DateTime PostedOn { get; set; }
    public DateTime? UpdatedOn { get; set; }
    public int BlogId { get; set; }

    public virtual Blog Blog { get; set; } = null!;

    public virtual ICollection<Tag> Tags { get; set; }
}

A třída pro Tag:

public partial class Tag
{
    public Tag()
    {
        Posts = new HashSet<Post>();
    }

    public int Id { get; set; }
    public string Name { get; set; } = null!;
    public string? Description { get; set; }

    public virtual ICollection<Post> Posts { get; set; }
}

Ale žádná třída tabulky PostTag . Místo toho se konfigurace relace M:N vygeneruje:

entity.HasMany(d => d.Tags)
    .WithMany(p => p.Posts)
    .UsingEntity<Dictionary<string, object>>(
        "PostTag",
        l => l.HasOne<Tag>().WithMany().HasForeignKey("PostsId"),
        r => r.HasOne<Post>().WithMany().HasForeignKey("TagsId"),
        j =>
            {
                j.HasKey("PostsId", "TagsId");
                j.ToTable("PostTag");
                j.HasIndex(new[] { "TagsId" }, "IX_PostTag_TagsId");
            });

Další programovací jazyky

Balíčky EF Core publikované kódem C# uživatelského rozhraní Microsoftu. Základní systém generování uživatelského rozhraní však podporuje model modulu plug-in pro generování v jiných jazycích. Tento model modulu plug-in používá různé projekty spuštěné komunitou, například:

Přizpůsobení kódu

Od EF7 je jedním z nejlepších způsobů přizpůsobení vygenerovaného kódu přizpůsobením šablon T4 použitých k jeho vygenerování.

Kód lze také po vygenerování změnit, ale nejlepší způsob, jak to udělat, závisí na tom, jestli chcete znovu spustit proces generování při změně modelu databáze.

Generování uživatelského rozhraní pouze jednou

Díky tomuto přístupu poskytuje kód vygenerovaný vygenerovaný kód výchozí bod pro mapování založené na kódu. Všechny změny vygenerovaného kódu je možné provést podle potřeby – stane se normálním kódem stejně jako jakýkoli jiný kód v projektu.

Synchronizaci databáze a modelu EF je možné provádět jedním ze dvou způsobů:

  • Přepněte na migraci databází EF Core a jako zdroj pravdivých dat použijte typy entit a konfiguraci modelu EF. K řízení schématu použijte migrace.
  • Při změně databáze ručně aktualizujte typy entit a konfiguraci EF. Pokud je například do tabulky přidán nový sloupec, přidejte vlastnost sloupce k mapovanému typu entity a přidejte jakoukoli potřebnou konfiguraci pomocí atributů mapování a/nebo kódu v OnModelCreatingsouboru . To je poměrně snadné, protože jediným skutečným problémem je proces, který zajistí, aby se změny databáze zaznamenávaly nebo detekovaly nějakým způsobem, aby vývojáři zodpovědní za kód mohli reagovat.

Opakované generování uživatelského rozhraní

Alternativním přístupem k vygenerování jednou je opětovné generování při každé změně databáze. Tím se přepíše veškerý dříve vygenerovaný kód, což znamená, že všechny změny typů entit nebo konfigurace EF v tomto kódu budou ztraceny.

[TIP] Ve výchozím nastavení příkazy EF nepřepíšou žádný existující kód, který bude chránit před náhodnou ztrátou kódu. Argument -Force (PMC) nebo --force .NET CLI (Visual Studio PMC) lze použít k vynucení přepsání existujících souborů.

Vzhledem k tomu, že se vygenerovaný kód přepíše, je nejlepší ho přímo upravit, ale místo toho spoléhat na částečné třídy a metody a mechanismy v EF Core, které umožňují přepsání konfigurace. Konkrétně:

  • Třídy DbContext i třídy entit se generují jako částečné. To umožňuje zavést další členy a kód do samostatného souboru, který se při spuštění generování nepřepíše.
  • Třída DbContext obsahuje částečnou metodu volanou OnModelCreatingPartial. Implementace této metody lze přidat do částečné třídy pro DbContext. Potom bude volána po OnModelCreating zavolání.
  • Konfigurace modelu vytvořená pomocí ModelBuilder rozhraní API přepíše veškerou konfiguraci provedenou konvencemi nebo atributy mapování a také dřívější konfiguraci provedenou v tvůrci modelů. To znamená, že kód v OnModelCreatingPartial tomto kódu lze použít k přepsání konfigurace vygenerované procesem generování uživatelského rozhraní, aniž by bylo nutné tuto konfiguraci odebrat.

Nezapomeňte, že od EF7 je možné přizpůsobit šablony T4 použité ke generování kódu. To je často efektivnější přístup než generování s výchozími nastaveními a následné úpravy s částečnými třídami nebo metodami.

Jak to funguje

Zpětná analýza začíná čtením databázového schématu. Přečte informace o tabulkách, sloupcích, omezeních a indexech.

V dalším kroku analýza použije informace ze schématu k vytvoření modelu EF Core. Tabulky se použijí k vytvoření typů entit, sloupce se použijí k vytvoření vlastností a cizí klíče se použijí k vytvoření relací.

Nakonec se model použije ke generování kódu. Vygenerují se odpovídající třídy typů entit, rozhraní Fluent API a datové poznámky, aby z aplikace mohl být znovu vytvořen stejný model.

Omezení

  • Schéma databáze ale nemůže představovat všechny informace o modelu. Ve schématu databáze například chybí informace o hierarchii dědičnosti, vlastněných typech a dělení tabulek. Z tohoto důvodu se tyto konstrukce nikdy nevygenerují.
  • Dále platí, že poskytovatel mapovače EF Core nemusí podporovat některé typy sloupců. Tyto sloupce v modelu nebudou.
  • Tokeny souběžnosti můžete definovat v modelu EF Core, aby dva uživatelé nemohli aktualizovat stejnou entitu současně. Některé databáze mají speciální typ, který představuje tento typ sloupce (například rowversion v SQL Serveru), v takovém případě můžeme tyto informace zpětně analyzovat; Jiné tokeny souběžnosti však nebudou vygenerovány.