c# - EF Core 是否可以将 OnModelCreating/ConfigureConventions 中的代码作为迁移代码生成的一部分?

标签 c# sql-server entity-framework-core

我不知道我是否期望过高,但在尝试在 OnModelCreating 和/或中配置 EF Core 6 行为时,我遇到了一些不同的情况创建新迁移时似乎根本不遵守 ConfigureConventions

例如,我想强制所有字符串属性为 varchar(而不是 nvarchar)。有许多通过模型属性属性处理此问题的示例,这些属性似乎在迁移代码生成时受到尊重,但不幸的是它们都处于属性级别。我还看到过通过覆盖 ConfigureConventions 设置 IsUnicode(false) 的示例,如下所示:

protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
{
    configurationBuilder
        .DefaultTypeMapping<string>()
        .HasColumnType("varchar")
        .IsUnicode(false);
}

但是在迁移代码生成时似乎没有遵守此代码。它只是被忽略了。我意识到在生成查询时它将在运行时受到尊重,但我特别讨论的是在开发过程中生成新的迁移代码时。

在创建新的迁移代码时,是否应该遵守 ConfigureConventions 中的此代码,以便如果我向模型添加新的字符串属性,然后添加新的迁移,则生成的代码应显示新的列类型为 varchar

根据 @David Brown 请求重现示例进行更新:

我使用以下内容创建了一个新的 .NET6 Core Console 应用程序:

Program.cs

using ConsoleApp1;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

IHost host = Host.CreateDefaultBuilder(args)
    .ConfigureServices((hostContext, services) =>
    {
        services.AddDbContext<MyDbContext>();
    })
    .Build();

host.Run();

MyDbContext.cs

using Microsoft.EntityFrameworkCore;

namespace ConsoleApp1
{
    public class MyDbContext : DbContext
    {
        public MyDbContext(DbContextOptions<MyDbContext> options) : base(options)
        {
        }

        public DbSet<WorkRequest> WorkRequests { get; set; }

        protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
        {
            configurationBuilder
                .DefaultTypeMapping<string>()
                .HasColumnType("varchar")
                .IsUnicode(false);

            base.ConfigureConventions(configurationBuilder);
        }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=MyDb;Trusted_Connection=True;");
        }
    }
}

WorkRequest.cs

namespace ConsoleApp1
{
    public class WorkRequest
    {
        public Int64 Id { get; set; }

        //[Unicode(false), MaxLength(256)]  
        public string SubmitterId { get; set; }
    }
}

ConsoleApp1.csproj

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net6.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.8" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.8">
      <PrivateAssets>all</PrivateAssets>
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
    </PackageReference>
    <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="6.0.8" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="6.0.8">
      <PrivateAssets>all</PrivateAssets>
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
    </PackageReference>
    <PackageReference Include="Microsoft.Extensions.Hosting" Version="6.0.1" />
    <PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="6.0.0" />
  </ItemGroup>
  <ItemGroup>
    <Folder Include="Migrations\" />
  </ItemGroup>
</Project>

然后,我只需转到程序包管理器控制台并输入:

add-migration InitialCreate -verbose

生成的迁移代码始终将 SubmitterId 显示为 nvarchar(max),除非我使用带注释的属性属性而不是 ConfigureConventions 代码:

SubmitterId = table.Column<string>(type: "nvarchar(max)", nullable: false),

最佳答案

OnModelCreating/ConfigureConventions 中的代码当然在迁移中受到尊重。

您遇到的问题是 DefaultTypeMapping方法,特别是期望与实际效果之间的差异。

只要看一下方法名称,人们就会想到您所做的一切 - 它指定 CLR 类型的默认映射。从另一面来看,还有另一种方法称为 Properties它返回具有类似方法的构建器(只是以 Are/Have 开头,而不是 Is/Has ),所以目前还不清楚有什么区别以及您应该使用哪一个。

DefaultTypeMapping 的文档没有多大帮助:

Marks the given type as a scalar, even when used outside of entity types. This allows values of this type to be used in queries that are not referencing property of this type.

Unlike Properties(Type) this method should only be called on a non-nullable concrete type. Calling it on a base type will not apply the configuration to the derived types.

Calling this is rarely needed. If there are properties of the given type calling Properties(Type) should be enough in most cases.

它显示了行为上的微小差异,并表示很少需要使用该方法,但没有说明原因。

Default type mapping文档的部分更清晰一些:

Generally, EF is able to translate queries with constants of a type that is not supported by the provider, as long as you have specified a value converter for a property of this type. However, in queries that don't involve any properties of this type, there is no way for EF to find the correct value converter. In this case, it's possible to call DefaultTypeMapping to add or override a provider type mapping

因此,事实证明此方法和关联的流畅配置调用仅影响查询翻译,而不影响实体属性映射(因此迁移)。

简单地说,在 ConfigureConventions 内部使用 Properties 方法和 Fluent API 来配置模型默认值,并且它们将在任何地方受到尊重,包括迁移,例如在你的例子中:

configurationBuilder
    .Properties<string>()
    //.HaveColumnType("varchar") // not needed, see below
    .AreUnicode(false); // abstract way of specifying varchar vs nvarchar

作为旁注,还请注意文档中的以下小注释

Data annotations do not override pre-convention configuration.

这意味着,如果您对通过 Properties 配置的类型的非默认列使用数据注释(属性),它们将不会受到尊重(您必须使用流畅的 API 和 OnModelCreating )。这与“默认”EF Core 约定非常重要,后者的优先级低于数据注释,并且只是记录不佳的糟糕设计决策的另一个例子(他们说,对我来说这是实现错误)逻辑期望之间的差异(我们只是重写默认约定,对吧?)和实际行为。

关于c# - EF Core 是否可以将 OnModelCreating/ConfigureConventions 中的代码作为迁移代码生成的一部分?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/73533656/

相关文章:

c# - 按键分组并使用 LINQ 将值发送到列表中

json - 如何在 MS-SQL 2016 中将列中的 JSON 值查询到一个 JSON 数组中?

sql - T-SQL 修剪 &nbsp(和其他非字母数字字符)

c# - .NET Entity Framework Core、依赖注入(inject)和线程的 DBContext System.ObjectDisposed 异常

c# - 通过异步方法使用 View 上的数据

c# - 使用 Linq 获取唯一条目

sql-server - 为什么我的 Web 应用程序不使用 AppPoolIdentity 登录到同一计算机上的 SQL Server?

entity-framework - Entity Framework 核心数据库在初始脚手架后首次更新?

c# - EFCore 2.0 默认所有日期字段使用 datetime2

c# - 在这种情况下,EF Core 生成的 SQL 效率很低,有什么解决方法吗?