«Отображение типа для« Мгновенного »не реализовало генерацию литерала кода» при разбиении сущности - PullRequest
2 голосов
/ 02 мая 2019

Мне нужно провести рефакторинг сущности и перенести некоторые ее свойства в другую сущность с соотношением 1: 1. Однако, когда я создаю новый класс и перемещаю в него в основном свойства типа Instant, создавая Entity1Id в качестве его ключа плюс свойство виртуальной навигации между ними, когда я хочу создать миграцию, я получаю следующую ошибку:

System.NotSupportedException: в сопоставлении типов для 'Instant' не реализована генерация литералов кода.

Что происходит? Я не делаю посева (обнаружил эту ошибку: https://github.com/npgsql/Npgsql.EntityFrameworkCore.PostgreSQL/issues/526)

Я создал простой репозиторий, демонстрирующий такое поведение, где вы можете протестировать его с .Net Core 2.2. Я в настоящее время на Mac; Я не уверен, оказывает ли это какое-либо влияние.

GitHub репо: https://github.com/Slaviusz/EFCoreSplittingEntityProblem

Edit:

По запросу, что на самом деле является содержимым репозитория Github, приведен пример кода. Обратите внимание, что я столкнулся с этой проблемой в решении с десятками сущностей, но мне удалось урезать его до простого проекта рефакторинга сущностей.

Начиная с простой сущности:

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 во вторичный класс, создавая логическую связь между ними. Типичный вариант использования заключается в извлечении сведений об объекте в его собственную таблицу, требующую объединения / выборки, только когда необходимы сведения.

В этом случае реквизиты Instant Starts и Instant 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 (миграции, обновления баз). Таким образом, у меня есть дополнительный класс, который расширяет IDesignTimeDbContextFactory<>.

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

1 Ответ

1 голос
/ 07 мая 2019

Пока возится с проблемой в тестовом проекте, кажется, что простое удаление свойств Instant из класса POCO вызывает эту ошибку.

Очень грубый обходной путь - разделить удаление на 2 миграции:

  1. Применяйте Мгновенный подпор к струне (varchar) при первой миграции
  2. Наконец удалите реквизиты строки из класса во второй миграции

Обе миграции завершились успешно, и я смог запустить dotnet ef database update с небольшими предупреждениями о возможной потере данных - что ожидалось, поскольку я написал собственный код SQL для переноса данных в предыдущей миграции, где я создал новые свойства Instant в другом классе.

...