如何在 Entity Framework Core cli 工具中使用来自 dotnet 6 最小 API 的配置
Posted
技术标签:
【中文标题】如何在 Entity Framework Core cli 工具中使用来自 dotnet 6 最小 API 的配置【英文标题】:How to use configuration from dotnet 6 minimal API in Entity Framework Core cli tools 【发布时间】:2021-09-21 21:35:01 【问题描述】:我正在尝试构建一个使用 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();
如果该代码使用 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);
如您所见,我对连接字符串进行了硬编码。我知道我可以构造ConfigurationBuilder
并使用相对路径来找到正确的json文件并使用它来查找连接字符串,但感觉就像一个肮脏的hack。
使用 dotnet 6 的方法是什么?
【问题讨论】:
“用 dotnet 6 怎么办?” 目前还没有 net6 和 EFC6 这样的东西。只是一些预览、RC 或他们所说的任何东西。重要的是他们没有被释放。因此,您不能指望文档或 EFC 工具等工作。当他们发布它时,他们很可能会更新 supported Design-time DbContext Creation patterns 的文档(如果有)。在那之前,只要使用你所拥有的。 【参考方案1】:我使用 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>
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="..\Domain\Domain.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 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"
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>
通过 ef tools cli 更新数据库
dotnet ef database update -s "D:\Repositorios\MinimalApi\MinimalApi\MinimalApi.csproj" -p "D:\Repositorios\MinimalApi\Infra\Infra.csproj"
请注意,-s 是您的启动项目路径;
请注意,-p 是您的目标项目路径(配置上下文的位置);
自动创建的迁移文件夹
最终项目结构
创建的数据库
请注意,您必须在包管理器上设置“包含预发布”才能获得与 .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]
【讨论】:
我错过了 cli 工具调用上的-p
和 -s
开关,非常感谢!
由于其他人开始问同样的问题(例如***.com/questions/69474533/…),在将他们重定向到这里之前,我有义务询问是否有人可以确认这真的有效(我可以'不要用 MS 实验污染我的生产环境)这是非常值得怀疑的。所有这些样板文件,包括 -p
和 -s
选项都不是新的,与最小 API 无关。 SqlServer 和 MinimalApiDb 听起来就像“合理的默认值”。
唯一的证明是如果您使用硬编码的“MyFooBar”数据库名称(一个不遵循任何命名约定或配置源的字符串)硬编码.UseSomeDb(...)
,并且该工具会创建/更新此类数据库。从逻辑和技术上讲,我看不出怎么会发生(除非他们以某种方式使用 Roslyn 来真正分析您的 source code 并提取 DbContext
配置代码)。使用“旧”方法,他们搜索(通过反射)特定的静态方法,并*执行它以便从 DI 获取配置的 DbContext
实例。
我认为诀窍也在<LangVersion>Preview</LangVersion>
中,因为运行时必须将主Program.cs
识别为具有main 方法的静态类,并以某种方式提取构建器(不确定它是如何工作的)
这个答案真的很清楚很完整。谈到 ef 工具,我不喜欢您必须将与供应商相关的软件包安装到可能与此无关的 Infra 项目中。以上是关于如何在 Entity Framework Core cli 工具中使用来自 dotnet 6 最小 API 的配置的主要内容,如果未能解决你的问题,请参考以下文章