Entity Framework 核心接收 0 作为 Id 作为播种,尽管我将其定义为其他内容

Entity Framework Core receiving 0 for seeding as Id although I defined it to be something else

尝试添加迁移时,ef core 出现以下错误:

The seed entity for entity type 'MyEntity' cannot be added because a non-zero value is required for property 'Id'. Consider providing a negative value to avoid collisions with non-seed data.

实体 MyEntity 及其 superclass/interface:

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 core nuget 依赖项的版本都是 3.1.5

classIEntityObject更改为接口

这是一个完全可用的控制台项目,它假定 StateChange 是您的一些内部 class 而不是 model/entity class:

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 也应该是一个模型 class/entity,那么您应该向它添加一个外键并设置关系。这可能看起来像这样:

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

我同意lauxjpn 提供的答案。加上我的两分钱来解释为什么会这样。

当实体之间定义了关系时,'HasData' 方法不适用于实体。请注意 'HasData' 接收的参数等同于在 table.

中创建的列

因此,根据所讨论的实体设计,为 MyEntity 对象创建的 table 将不包含 StateChange 实体关系数据。事实上,StateChange 实体将有一个 MyEntityId 列来引用外键形式的 Entity 对象 ID。

为了添加种子数据,您必须通过使用匿名类型(在实体属性和 table 列不匹配)基于生成的 table 列。

        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 文档网站。

data seeding