首页 > 解决方案 > EF Core 5.0 - 如何使用列表播种实体迁移中的财产?

问题描述

我试图解决这个问题需要几个小时,但可以找到解决方案。

我有一个具有List<string>属性的实体,我想为它播种数据。我成功使用modelBuilder.Entity<T>.HasData()了 ,但它不适用于dotnet ef migrations.

这是我的实体:

public class MyEntity
{
    public int Id { get; set; }

    public List<string> Names { get; set; }
}

这是我的DbContext.OnModelCreating覆盖:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<MyEntity>()
        .HasData(
            new MyEntity { Id = 1, Names = new List<string> { "text1", "text2" } },
            new MyEntity { Id = 2, Names = new List<string> { "text1", "text2", "text3" } },
            new MyEntity { Id = 3, Names = new List<string> { "text1", "text2" } },
            new MyEntity { Id = 4, Names = new List<string> { "text1", "text2" } },
            new MyEntity { Id = 5, Names = new List<string> { "text1", "text2", "text3" } });
}

如果我运行我的应用程序,这将起作用,它会创建表并使用所需的值对其进行播种。但是如果我运行dotnet ef migrations add AddMyEntities --project src/MyProject --startup-project src/MyProject.Api,我会得到这个输出:

Build started...
Build succeeded.
System.NotSupportedException: The type mapping for 'List<string>' 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.Design.Internal.CSharpHelper.Literal(Object[,] values)
   at Microsoft.EntityFrameworkCore.Migrations.Design.CSharpMigrationOperationGenerator.Generate(InsertDataOperation operation, IndentedStringBuilder builder)
   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, String namespace)
   at Microsoft.EntityFrameworkCore.Design.OperationExecutor.AddMigrationImpl(String name, String outputDir, String contextType, String namespace)
   at Microsoft.EntityFrameworkCore.Design.OperationExecutor.AddMigration.<>c__DisplayClass0_0.<.ctor>b__0()
   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 'List<string>' has not implemented code literal generation.

如何播种一个简单的List<string>属性?


编辑:我认为这个问题与实体框架 - 代码优先 - 无法存储列表等其他问题不同,因为如果我在空白数据库上启动它(使用 PostgreSQL,它会创建一个Names类型的列text[],就像你一样),我的程序可以正常工作会期望)。问题是当我使用dotnet ef迁移工具时。

标签: c#entity-framework-core

解决方案


编辑: OP 提到它使用 PostgreSQL 和相应的 EF 提供程序,所以问题不在于缺少值转换器。

该问题似乎与 EF Core 本身的限制有关。迁移脚手架过程当前使用内置的硬编码CSharpHelper类,该类知道如何为一组封闭的类型生成 C# 文字。例如,它知道如何生成object[]文字,但不知道IEnumerable<>. (参考:dotnet/efcore#12979)。似乎现在提供者可以为其他类型添加代码文字的生成,我可以发现它是为npgsql/efcore.pg#0deb6a23中的某些类型添加的,但List<>据我所知不是......但是我可能是错的,因为我自己没有测试过这些。

我的建议?使用string[]或放弃HasData并提供一些自定义播种逻辑。


OLD:问题不在于HasData功能本身。根本问题是 EF Core 本身并不知道如何将 a 存储List<string>到数据库中,因此它失败了。

您可以做的是定义一个自定义ValueConverter,指示 EF Core 如何将该特定属性存储到您的数据库列中(您可以查看有关此主题的EF Core 文档)。

可能最好的方法是使用Newtonsoft JSONConvert定义这种转换:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<MyEntity>()
        .Property(p => p.Names)
        .HasConversion(
            listOfStringNames => JsonConvert.SerializeObject(listOfStringNames),
            namesJsonRepresentation => JsonConvert.DeserializeObject<List<string>>(namesJsonRepresentation));
        .HasData(
            new MyEntity { Id = 1, Names = new List<string> { "text1", "text2" } },
            new MyEntity { Id = 2, Names = new List<string> { "text1", "text2", "text3" } },
            new MyEntity { Id = 3, Names = new List<string> { "text1", "text2" } },
            new MyEntity { Id = 4, Names = new List<string> { "text1", "text2" } },
            new MyEntity { Id = 5, Names = new List<string> { "text1", "text2", "text3" } });
}

作为旁注,我想提一下,即使HasData很好,根据我的个人经验,最好只编写我自己的DatabaseInitializer类,用我想要的任何自定义逻辑播种我需要的数据,而没有任何限制HasData(参考:https ://docs.microsoft.com/en-us/ef/core/modeling/data-seeding#limitations-of-model-seed-data )


推荐阅读