"The type mapping for 'Instant' has not implemented code literal generation" 拆分实体时

"The type mapping for 'Instant' has not implemented code literal generation" when splitting entity

我需要重构一个实体并将它的一些属性移动到另一个具有 1:1 关系的实体。但是,当我创建一个新的 class 并将大部分 Instant 类型的属性移动到它时,创建 Entity1Id 作为它的键加上它们之间的虚拟导航 属性 ,当我想创建一个迁移时,我得到以下错误:

System.NotSupportedException: The type mapping for 'Instant' has not implemented code literal generation.

这是怎么回事?我没有播种(发现这个错误:https://github.com/npgsql/Npgsql.EntityFrameworkCore.PostgreSQL/issues/526

我创建了一个简单的存储库来演示此行为,您可以在其中使用 .Net Core 2.2 对其进行测试。我目前在 Mac;不知道有没有影响。

GitHub 回购:https://github.com/Slaviusz/EFCoreSplittingEntityProblem

编辑:

根据要求,Github repo 的实际内容是什么,代码示例如下。请注意,我在包含数十个实体的解决方案中遇到过此问题,但我能够 trim 将其简化为一个实体重构项目。

从一个简单的实体开始:

public class Table1
{
    public int Id { get; set; }
    public string Name { get; set; }
    public Instant Starts { get; set; }
    public Instant Ends { get; set; }
}

public class ApplicationDbContext : DbContext
{
    ... // other code like constructors and configure method overrides
    public DbSet<Table1> Table1s { get; set; }
}

创建迁移成功,随后成功 dotnet ef database update。 (但是根本没有必要触发它)

更多细节可以在第一次提交中看到:https://github.com/Slaviusz/EFCoreSplittingEntityProblem/commit/57562f0c978287e15d75ff1bead435501c28befc


下一步是执行重构,将两个 Instant 属性提取到辅助 class,从而在它们之间创建逻辑关系。 典型的用例是将实体详细信息提取到它自己的 table 中,仅在需要详细信息时需要 join/subselect。

在这种情况下,属性 Instant StartsInstant Ends 被移动到 Table2

public class Table1
{
    public int Id { get; set; }
    public string Name { get; set; }
    public virtual Table2 Table2 { get; set; }
}

public class Table2
{
    [Key] public int Table1Id { get; set; }
    public Instant Starts { get; set; }
    public Instant Ends { get; set; }
    public virtual Table1 Table1 { get; set; }
}

public class ApplicationDbContext : DbContext
{
    ... // other code like constructors and configure method overrides
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Table2>()
            .HasOne(p => p.Table1)
            .WithOne(p => p.Table2);
    }

    public DbSet<Table1> Table1s { get; set; }
    public DbSet<Table2> Table2s { get; set; }
}

全部可以在第三次提交中看到:https://github.com/Slaviusz/EFCoreSplittingEntityProblem/commit/04fe59563bd510df26a37e5938889557b9741673

注意:第二次提交是我添加 .gitignore 以排除为了重现此目的而不必要的文件。

此时执行 dotnet ef migrations add Split 结果:

$ dotnet ef migrations add Split
An operation was scaffolded that may result in the loss of data. Please review the migration for accuracy.
System.NotSupportedException: The type mapping for 'Instant' has not implemented code literal generation.
   at Microsoft.EntityFrameworkCore.Storage.CoreTypeMapping.GenerateCodeLiteral(Object value)
   at Microsoft.EntityFrameworkCore.Design.Internal.CSharpHelper.UnknownLiteral(Object value)
   at Microsoft.EntityFrameworkCore.Migrations.Design.CSharpMigrationOperationGenerator.Generate(AddColumnOperation operation, IndentedStringBuilder builder)
   at System.Dynamic.UpdateDelegates.UpdateAndExecuteVoid3[T0,T1,T2](CallSite site, T0 arg0, T1 arg1, T2 arg2)
   at Microsoft.EntityFrameworkCore.Migrations.Design.CSharpMigrationOperationGenerator.Generate(String builderName, IReadOnlyList`1 operations, IndentedStringBuilder builder)
   at Microsoft.EntityFrameworkCore.Migrations.Design.CSharpMigrationsGenerator.GenerateMigration(String migrationNamespace, String migrationName, IReadOnlyList`1 upOperations, IReadOnlyList`1 downOperations)
   at Microsoft.EntityFrameworkCore.Migrations.Design.MigrationsScaffolder.ScaffoldMigration(String migrationName, String rootNamespace, String subNamespace, String language)
   at Microsoft.EntityFrameworkCore.Design.Internal.MigrationsOperations.AddMigration(String name, String outputDir, String contextType)
   at Microsoft.EntityFrameworkCore.Design.OperationExecutor.AddMigrationImpl(String name, String outputDir, String contextType)
   at Microsoft.EntityFrameworkCore.Design.OperationExecutor.OperationBase.<>c__DisplayClass3_0`1.<Execute>b__0()
   at Microsoft.EntityFrameworkCore.Design.OperationExecutor.OperationBase.Execute(Action action)
The type mapping for 'Instant' has not implemented code literal generation.

我觉得需要注意的重要一点是,为了能够使用 EF Core In-Memory 提供程序进行测试,我使用了 2 个构造函数:

// constructor for mocking with InMemory provider
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options) { }

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
    // return if already configured (by mocking with InMemory provider)
    if (optionsBuilder.IsConfigured)
        return;
    ... // the rest of the code to init
}

然而,这会导致 dotnet 工具无法执行 cli EF Core 操作(迁移、数据库更新)。因此,我有额外的 class 扩展了 IDesignTimeDbContextFactory<>.

public class CliDbContext : IDesignTimeDbContextFactory<ApplicationDbContext>
{
    public ApplicationDbContext CreateDbContext(string[] args)
    {
    ... // code to init in cli cases
    }
}

虽然在测试项目中解决了这个问题,但似乎只是简单地从 POCO class 中删除 Instant 属性会触发此错误。

非常粗略的解决方法是将删除拆分为 2 个迁移:

  1. 在第一次迁移中将 Instant 道具转换为字符串 (varchar)
  2. 最后在第二次迁移
  3. 中从class中删除字符串道具

两次迁移都成功了,我能够 运行 dotnet ef database update 带有关于可能的数据丢失的小警告 - 这是预期的,因为我在之前编写了自定义 SQL 代码来迁移数据我在另一个 class.

中创建新 Instant 属性的迁移