简体   繁体   中英

EF Core 5.0 - How to seed entity with List<string> property in a migration?

It makes a few hour that I'm trying to figure this one out, but could find a solution.

I have an entity with a List<string> property and I'd like to data seeding it. I succeeding using modelBuilder.Entity<T>.HasData() , but it doesn't work with dotnet ef migrations .

Here is my entity:

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

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

And here's my DbContext.OnModelCreating override:

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

This works if I run my app, it creates the table and seed it with the wanted values. But if I run dotnet ef migrations add AddMyEntities --project src/MyProject --startup-project src/MyProject.Api , I get this output:

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.

How can I seed a simple List<string> property?


Edit: I think this question is different from other questions such as Entity Framework - Code First - Can't Store List because my program works fine if I launch it on a blank DB (using PostgreSQL, it creates a Names column of type text[] , as you would expect). The problem is when I use the dotnet ef migration tool.

EDIT: the OP mentions that it's using PostgreSQL with the corresponding EF provider, so the problem is not a missing value converter.

The issue seems to be related to a limitation in EF Core itself. The migration scaffolding process currently uses a built-in, hardcoded CSharpHelper class which knows how to generate C# literals for a closed set of types. For example, it knows how to generate object[] literals, but not IEnumerable<> . (Ref.: dotnet/efcore#12979 ). It seems that now it's possible for providers to add generation for code literals for other types, and I could found that it was added for some types in npgsql/efcore.pg#0deb6a23 , but not for List<> as far as I understsand... but I can be wrong since I haven't tested any of this myself.

My suggestion? Either use string[] or ditch HasData and provide some custom seeding logic.


OLD: The problem is not with the HasData functionality itself. The underlying problem is that EF Core does not natively know how to store a List<string> into the database, thus it fails.

What you can do is to define a custom ValueConverter that instructs EF Core how to store that specific property into your database column (You can check EF Core docs about this topic).

Probably the best way to go would be define this conversion using 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" } });
}

As a side note, I would like to mention that even though HasData is nice, in my personal experience was better to just write my own DatabaseInitializer class that would seed the data I need with any custom logic I want, without any of the limitations that HasData has (Ref.: https://docs.microsoft.com/en-us/ef/core/modeling/data-seeding#limitations-of-model-seed-data )

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM