如何在 Entity Framework Core cli 工具中使用 dotnet 6 最小 API 的配置

Krz*_*nek 7 c# entity-framework-core asp.net-core .net-6.0

我正在尝试使用 EF Core 构建一个 API 作为数据库访问,现在在 dotnet 6 RC1 上。我想使用 dotnet cli 工具来管理迁移(创建、更新数据库等),但这些工具不与模板中的最小 API 配合。

这是我的 Program.cs:

void ConfigureApp(WebApplication webApplication)
{
    // Configure the HTTP request pipeline.
    if (webApplication.Environment.IsDevelopment())
    {
        webApplication.UseSwagger();
        webApplication.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "Eyespert.Server v1"));
    }

    webApplication.UseHttpsRedirection();

    webApplication.UseAuthentication();
    webApplication.UseAuthorization();

    webApplication.MapControllers();
}

void RegisterServices(WebApplicationBuilder builder)
{
    builder.Services.AddControllers();
    builder.Services.AddSwaggerGen(c =>
    {
        c.SwaggerDoc("v1", new() { Title = "App", Version = "v1" });
    });
    
    builder.Services.AddDbContext<MyContext>(opt =>
    {
        string connectionString = builder.Configuration.GetConnectionString("MyConnectionString");
        opt.UseNpgsql(connectionString);
    });
}

WebApplicationBuilder builder = WebApplication.CreateBuilder(args);

RegisterServices(builder);

WebApplication app = builder.Build();

ConfigureApp(app);

app.Run();
Run Code Online (Sandbox Code Playgroud)

如果该代码使用 Program/Startup 类组合和旧构建器,我可以在控制台中输入dotnet ef migrations add InitialCreate,该工具将读取 appsettings.development.json (即使它是与上下文不同的项目)并在正确的数据库上运行迁移。对于最小的 API 风格来说,情况并非如此。

作为解决方案,我制作了一个设计时上下文工厂:

public class DesignTimeDbContextFactory : IDesignTimeDbContextFactory<MyContext>
    {
        public MyContextCreateDbContext(string[] args)
        {
            DbContextOptionsBuilder<MyContext> dbContextOptionsBuilder =
                new();

            dbContextOptionsBuilder.UseNpgsql(@"myconnectionstring");

            Console.WriteLine("Creating default MyContext");
            
            return new MyContext(dbContextOptionsBuilder.Options);
        }
    }
Run Code Online (Sandbox Code Playgroud)

如您所见,我对连接字符串进行了硬编码。我知道我可以构造ConfigurationBuilder并使用相对路径来查找正确的 json 文件并使用它来查找连接字符串,但这感觉像是一个肮脏的黑客。

使用 dotnet 6 的方法是什么?

Mes*_*ros 14

我使用 EFCore Context 创建了一个 MinimalAPI 项目,它运行良好,没有发生重大问题,除了更新 ef 工具 cli 等之外,请参阅完整的项目:

MinimalApi.csproj

<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>net6.0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
    <LangVersion>Preview</LangVersion>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.0-rc.1.21452.10">
      <PrivateAssets>all</PrivateAssets>
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
    </PackageReference>
    <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="6.0.0-rc.1.21452.10">
      <PrivateAssets>all</PrivateAssets>
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
    </PackageReference>
    <PackageReference Include="Swashbuckle.AspNetCore" Version="6.1.5" />
  </ItemGroup>

  <ItemGroup>
    <ProjectReference Include="..\Infra\Infra.csproj" />
  </ItemGroup>

</Project>

Run Code Online (Sandbox Code Playgroud)

程序.cs

using Infra;
using Microsoft.EntityFrameworkCore;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddDbContext<Context>(opts =>
{
    var connString = builder.Configuration.GetConnectionString("MyConnectionString");
    opts.UseSqlServer(connString, options =>
    {
        options.MigrationsAssembly(typeof(Context).Assembly.FullName.Split(',')[0]);
    });
});

await using var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
}

app.MapGet("/", (Func<string>)(() => "Hello World!"));

await app.RunAsync();
Run Code Online (Sandbox Code Playgroud)

基础设施.csproj

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>net6.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
    <LangVersion>Preview</LangVersion>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.0-rc.1.21452.10" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.0-rc.1.21452.10">
      <PrivateAssets>all</PrivateAssets>
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
    </PackageReference>
    <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="6.0.0-rc.1.21452.10" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="6.0.0-rc.1.21452.10">
      <PrivateAssets>all</PrivateAssets>
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
    </PackageReference>
  </ItemGroup>

  <ItemGroup>
    <ProjectReference Include="..\Domain\Domain.csproj" />
  </ItemGroup>

</Project>


Run Code Online (Sandbox Code Playgroud)

基础设施上下文.cs

using Domain;
using Microsoft.EntityFrameworkCore;

namespace Infra
{
    public class Context : DbContext
    {

        public Context(DbContextOptions options) : base(options)
        {
        }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
        }

        public DbSet<MyEntity> MyEntities { get; set; }
    }
}

Run Code Online (Sandbox Code Playgroud)

域.csproj

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>net6.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
    <LangVersion>Preview</LangVersion>
  </PropertyGroup>

</Project>

Run Code Online (Sandbox Code Playgroud)

实体示例 (MyEntity.cs)

using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace Domain
{
    public class MyEntity
    {
        [Key]
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public Guid Id { get; set; }
        public string Name { get; set; }
        public string Value { get; set; }
    }
}

Run Code Online (Sandbox Code Playgroud)

在 nuget 包管理器上创建迁移

PM> Add-Migration InitialMigration
Build started...
Build succeeded.
Microsoft.EntityFrameworkCore.Infrastructure[10403]
      Entity Framework Core 6.0.0-rc.1.21452.10 initialized 'Context' using provider 'Microsoft.EntityFrameworkCore.SqlServer:6.0.0-rc.1.21452.10' with options: MigrationsAssembly=Infra 
Run Code Online (Sandbox Code Playgroud)

更新 nuget 包管理器上的数据库

PM> Update-Database
Build started...
Build succeeded.
Microsoft.EntityFrameworkCore.Infrastructure[10403]
      Entity Framework Core 6.0.0-rc.1.21452.10 initialized 'Context' using provider 'Microsoft.EntityFrameworkCore.SqlServer:6.0.0-rc.1.21452.10' with options: MigrationsAssembly=Infra 
Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (2,338ms) [Parameters=[], CommandType='Text', CommandTimeout='60']
      CREATE DATABASE [MinimalApiDb];
Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (933ms) [Parameters=[], CommandType='Text', CommandTimeout='60']
      IF SERVERPROPERTY('EngineEdition') <> 5
      BEGIN
          ALTER DATABASE [MinimalApiDb] SET READ_COMMITTED_SNAPSHOT ON;
      END;
Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (29ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
      SELECT 1
Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (18ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
      CREATE TABLE [__EFMigrationsHistory] (
          [MigrationId] nvarchar(150) NOT NULL,
          [ProductVersion] nvarchar(32) NOT NULL,
          CONSTRAINT [PK___EFMigrationsHistory] PRIMARY KEY ([MigrationId])
      );
Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (3ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
      SELECT 1
Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (17ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
      SELECT OBJECT_ID(N'[__EFMigrationsHistory]');
Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (5ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
      SELECT [MigrationId], [ProductVersion]
      FROM [__EFMigrationsHistory]
      ORDER BY [MigrationId];
Microsoft.EntityFrameworkCore.Migrations[20402]
      Applying migration '20211001150743_InitialMigration'.
Applying migration '20211001150743_InitialMigration'.
Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (176ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
      CREATE TABLE [MyEntities] (
          [Id] uniqueidentifier NOT NULL,
          [Name] nvarchar(max) NOT NULL,
          [Value] nvarchar(max) NOT NULL,
          CONSTRAINT [PK_MyEntities] PRIMARY KEY ([Id])
      );
Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (23ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
      INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
      VALUES (N'20211001150743_InitialMigration', N'6.0.0-rc.1.21452.10');
Done.
PM> 
Run Code Online (Sandbox Code Playgroud)

要使用 dotnet ef 工具,您需要更新到最新的 rc 以匹配您的 projetc 设计器/工具

dotnet tool update --global dotnet-ef --version 6.0.0-rc.1.21452.10
Run Code Online (Sandbox Code Playgroud)
  • 请注意,这是您在项目包中使用的同一版本的包

通过 ef tools cli 创建迁移

PS D:\Repositorios\MinimalApi\MinimalApi> dotnet ef migrations add eftoolsmigration -s "D:\Repositorios\MinimalApi\MinimalApi\MinimalApi.csproj" -p "D:\Repositorios\MinimalApi\Infra\Infra.csproj"
Run Code Online (Sandbox Code Playgroud)
Build started...
Build succeeded.
info: Microsoft.EntityFrameworkCore.Infrastructure[10403]
      Entity Framework Core 6.0.0-rc.1.21452.10 initialized 'Context' using provider 'Microsoft.EntityFrameworkCore.SqlServer:6.0.0-rc.1.21452.10' with options: MigrationsAssembly=Infra
Done. To undo this action, use 'ef migrations remove'
PS D:\Repositorios\MinimalApi\MinimalApi>
Run Code Online (Sandbox Code Playgroud)

通过 ef tools cli 更新数据库

 dotnet ef database update -s "D:\Repositorios\MinimalApi\MinimalApi\MinimalApi.csproj" -p "D:\Repositorios\MinimalApi\Infra\Infra.csproj"
Run Code Online (Sandbox Code Playgroud)
  • 注意 -s 是你的 Startup 项目路径;
  • 请注意,-p 是您的目标项目路径(配置上下文的位置);

自动创建的 Migrations 文件夹

自动创建的 Migrations 文件夹

最终项目结构

最终项目结构

已创建数据库

已创建数据库

  • 请注意,您必须在包管理器上设置“包含预发布”才能获得与 .Net 6 兼容的版本

  • 请注意,您使用的是 Postgree DB,它必须有一个与 EF Core 6.xxx 兼容的客户端

安装运行时和 SDK(为了清楚起见,仅使用 .NET 6)

PS C:\Users\Daniel> dotnet --list-runtimes
Microsoft.NETCore.App 6.0.0-rc.1.21451.13 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.WindowsDesktop.App 6.0.0-rc.1.21451.3 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]

PS C:\Users\Daniel> dotnet --list-sdks
6.0.100-rc.1.21463.6 [C:\Program Files\dotnet\sdk]
Run Code Online (Sandbox Code Playgroud)