如何在 Entity Framework Core cli 工具中使用来自 dotnet 6 最小 API 的配置 [英] How to use configuration from dotnet 6 minimal API in Entity Framework Core cli tools

查看:14
本文介绍了如何在 Entity Framework Core cli 工具中使用来自 dotnet 6 最小 API 的配置的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm trying to build an API with EF Core as database access, right now on dotnet 6 RC1. I want to use dotnet cli tools to manage migrations (creating, updating the database etc), but the tools do not cooperate with minimal API from the template.

这是我的 Program.cs:

Here is my 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();

如果该代码使用程序/启动类组合和旧构建器,我可以在控制台输入 dotnet ef migrations add InitialCreate 并且该工具将读取 appsettings.development.json(即使它不同项目而不是上下文)并在适当的数据库上运行迁移.使用最少的 API 风格,情况并非如此.

If that code was using the Program/Startup class combo and old builders, I could type in console dotnet ef migrations add InitialCreate and the tool would read appsettings.development.json (even though it's different project than the context) and run the migration on proper database. With minimal API style this is not the case.

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

As a solution, I made a design time context factory:

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);
        }
    }

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

As you can see, I hardcoded the connection string. I know that I can construct the ConfigurationBuilder and use relative paths to find the correct json file and use it to find the connection string, but it feels like a dirty hack.

如何使用 dotnet 6 做到这一点?

What would THE way to do it with dotnet 6?

推荐答案

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

I created an MinimalAPI project using EFCore Context and it runs fine no major problem ocourred, other than updating ef tools cli and so on, see the complete project:

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="..InfraInfra.csproj" />
  </ItemGroup>

</Project>

Program.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();

Infra.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="..DomainDomain.csproj" />
  </ItemGroup>

</Project>


Infra.Context.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; }
    }
}

Domain.csproj

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

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

</Project>

实体示例 (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; }
    }
}

在 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 

在 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> 

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

dotnet tool update --global dotnet-ef --version 6.0.0-rc.1.21452.10

  • 请注意,这与您在项目包中使用的包版本相同
  • 通过 ef 工具 cli 创建迁移

    PS D:RepositoriosMinimalApiMinimalApi> dotnet ef migrations add eftoolsmigration -s "D:RepositoriosMinimalApiMinimalApiMinimalApi.csproj" -p "D:RepositoriosMinimalApiInfraInfra.csproj"
    

    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:RepositoriosMinimalApiMinimalApi>
    

    通过 ef 工具 cli 更新数据库

     dotnet ef database update -s "D:RepositoriosMinimalApiMinimalApiMinimalApi.csproj" -p "D:RepositoriosMinimalApiInfraInfra.csproj"
    

    • 注意 -s 是你的启动项目路径;
    • 请注意,-p 是您的目标项目路径(其中配置了上下文);
    • 自动创建的迁移文件夹

      最终项目结构

      创建数据库

      • 请注意,您必须设置include prerelease";在你的包管理器上获得与 .Net 6 兼容的版本

      • Note that you will have to set "include prerelease" on you package manager to get the versions compatibles with .Net 6

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

      Note tha you are using Postgree DB, it will have to have a client compatible with EF Core 6.xxx

      安装了运行时和 SDK(.NET 6 仅为清晰起见)

      PS C:UsersDaniel> dotnet --list-runtimes
      Microsoft.NETCore.App 6.0.0-rc.1.21451.13 [C:Program FilesdotnetsharedMicrosoft.NETCore.App]
      Microsoft.WindowsDesktop.App 6.0.0-rc.1.21451.3 [C:Program FilesdotnetsharedMicrosoft.WindowsDesktop.App]
      
      PS C:UsersDaniel> dotnet --list-sdks
      6.0.100-rc.1.21463.6 [C:Program Filesdotnetsdk]
      

      这篇关于如何在 Entity Framework Core cli 工具中使用来自 dotnet 6 最小 API 的配置的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆