Entity Framework Core получает 0 для заполнения в качестве идентификатора, хотя я определил это как что-то еще - PullRequest
0 голосов
/ 12 июля 2020

При попытке добавить миграцию ef core выдает следующую ошибку:

Не удается добавить начальную сущность для типа сущности MyEntity, потому что для свойства требуется ненулевое значение Мне бы'. Рассмотрите возможность предоставления отрицательного значения, чтобы избежать конфликтов с данными, не являющимися начальными.

Entity MyEntity и его суперкласс / интерфейс:

public class MyEntity: EntityObject
{
    public long OrderId { get; set; }
    public List<StateChange> StateChanges { get; set; }
}

public class EntityObject : IEntityObject
{
    [Key]
    public int Id { get; set; }        
    [Timestamp]
    public byte[] RowVersion
    {
        get;
        set;
    }
}
public class IEntityObject
{
    int Id { get; set; 
    byte[] RowVersion { get; set; }
}

Начальное значение:

 var seed = new MyEntity{ Id = -2, StateChanges = new List<StateChange>(), OrderId = -2 };
 modelBuilder.Entity<MyEntity>().HasData(seed);

Я пробовал разные вещи с [Key] и [DatabaseGenerated], но не мог найти решения. Версия всех зависимостей ядра ef nuget - 3.1.5

Ответы [ 2 ]

1 голос
/ 13 июля 2020

Я согласен с ответом lauxjpn. Добавляю два цента за объяснение, почему это так.

Метод 'HasData' плохо работает с сущностями, когда между ними определены отношения. Обратите внимание, что 'HasData' принимает параметры, эквивалентные столбцам, созданным в таблице.

Таким образом, согласно рассматриваемому дизайну объекта, таблица, созданная для объекта MyEntity, не будет содержать StateChange данные отношения сущности. Фактически, у объекта StateChange будет столбец MyEntityId для ссылки на идентификатор объекта Entity в форме внешнего ключа.

Чтобы добавить начальные данные, вы должны вызвать 'HasData' для обеих сущностей с использованием анонимного типа (используется, если свойства сущности и столбцы таблицы не совпадают) на основе сгенерированных столбцов таблицы.

        modelBuilder.Entity<MyEntity>().HasData(
            new
            {
                Id = 1,
                OrderId = 2,
            });

        modelBuilder.Entity<StateChange>().HasData(
            new
            {
                MyEntityId = 1,
                //Fill in remaining properties of State Change
            });

Дополнительная информация о данных заполнение с использованием 'HasData' можно найти на веб-сайте microsoft docs.

заполнение данных

1 голос
/ 12 июля 2020

Измените class IEntityObject на интерфейс .

Вот полностью рабочий консольный проект, который предполагает, что StateChange - это ваш внутренний класс а не класс модели / объекта:

using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Diagnostics;
using System.Linq;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;

namespace IssueConsoleTemplate
{
    public interface IEntityObject // <-- changed from `class` to `interface`
    {
        int Id { get; set; }
        byte[] RowVersion { get; set; }
    }

    public class EntityObject : IEntityObject
    {
        [Key]
        public int Id { get; set; }   
        
        [Timestamp]
        public byte[] RowVersion
        {
            get;
            set;
        }
    }

    public class MyEntity : EntityObject
    {
        public long OrderId { get; set; }
        public List<StateChange> StateChanges { get; set; }
    }
        
    public class StateChange
    {
    }
    
    public class Context : DbContext
    {
        public DbSet<MyEntity> MyEntities { get; set; }
        
        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder
                .UseSqlServer(@"Data Source=.\MSSQL14;Integrated Security=SSPI;Initial Catalog=So62865284")
                .UseLoggerFactory(
                    LoggerFactory.Create(
                        b => b
                            .AddConsole()
                            .AddFilter(level => level >= LogLevel.Information)))
                .EnableSensitiveDataLogging()
                .EnableDetailedErrors();
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<MyEntity>(
                entity =>
                {
                    entity.Ignore(e => e.StateChanges); // <-- ignore your internal class
                    
                    entity.HasData(
                        new MyEntity
                        {
                            Id = 2,
                            StateChanges = new List<StateChange>(),
                            OrderId = 2
                        });
                });
        }
    }

    internal static class Program
    {
        private static void Main()
        {
            using var context = new Context();

            context.Database.EnsureDeleted();
            context.Database.EnsureCreated();

            var myEntitiesWithStateChanges = context.MyEntities
                .OrderBy(i => i.Id)
                .ToList();
            
            Debug.Assert(myEntitiesWithStateChanges.Count == 1);
        }
    }
}

В случае, если StateChange также должен быть классом / объектом модели, тогда вы должны добавить к нему внешний ключ и настроить связь. Это может выглядеть так:

using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Diagnostics;
using System.Linq;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;

namespace IssueConsoleTemplate
{
    public interface IEntityObject // <-- changed from `class` to `interface`
    {
        int Id { get; set; }
        byte[] RowVersion { get; set; }
    }

    public class EntityObject : IEntityObject
    {
        [Key]
        public int Id { get; set; }   
        
        [Timestamp]
        public byte[] RowVersion
        {
            get;
            set;
        }
    }

    public class MyEntity : EntityObject
    {
        public long OrderId { get; set; }
        public List<StateChange> StateChanges { get; set; } = new List<StateChanges>();
    }
        
    public class StateChange : EntityObject
    {
        public int MyEntityId { get; set; } // <-- added FK
        public MyEntity MyEntity { get; set; } // <-- added navigation property
    }
    
    public class Context : DbContext
    {
        public DbSet<MyEntity> MyEntities { get; set; }
        public DbSet<StateChange> StateChanges { get; set; }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder
                .UseSqlServer(@"Data Source=.\MSSQL14;Integrated Security=SSPI;Initial Catalog=So62865284")
                .UseLoggerFactory(
                    LoggerFactory.Create(
                        b => b
                            .AddConsole()
                            .AddFilter(level => level >= LogLevel.Information)))
                .EnableSensitiveDataLogging()
                .EnableDetailedErrors();
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<MyEntity>(
                entity =>
                {
                    // Added the relationship definition (not really necessary here,
                    // because this would also work by convention).
                    entity.HasMany(e => e.StateChanges)
                        .WithOne(s => s.MyEntity)
                        .HasForeignKey(s => s.MyEntityId);

                    entity.HasData(
                        new MyEntity
                        {
                            Id = 2,
                            OrderId = 2
                        });
                });
            
            modelBuilder.Entity<StateChange>(
                entity =>
                {
                    entity.HasData(
                        new StateChange
                        {
                            Id = 2,
                            MyEntityId = 2,
                        });
                });
        }
    }

    internal static class Program
    {
        private static void Main()
        {
            using var context = new Context();

            context.Database.EnsureDeleted();
            context.Database.EnsureCreated();

            var myEntitiesWithStateChanges = context.MyEntities
                .Include(e => e.StateChanges)
                .OrderBy(i => i.Id)
                .ToList();
            
            Debug.Assert(myEntitiesWithStateChanges.Count == 1);
            Debug.Assert(myEntitiesWithStateChanges[0].StateChanges.Count == 1);
        }
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...