EFCore 在添加时更改实体的 ID。无法跟踪实体,因为具有键值的另一个实例已被跟踪

问题描述 投票:0回答:1

我正在开发一个为关系数据库生成随机数据的库。我使用反射在 DbContext 中添加实体实例,因为我无法显式指定实例的类型:

public class EntityAdder<TDbContext>
    where TDbContext : DbContext
{
    private readonly TDbContext _dbContext;
    private readonly MyOptions _options;
    public EntityAdder(
        TDbContext dbContext,
        MyOptionsProvider optionsProvider
        )
    {
        _dbContext = dbContext;
        _options = optionsProvider.GetOptions();
    }
    public async Task AddEntities(Dictionary<EntityInfo, List<object>> entities)
    {
        var dbSets = typeof(TDbContext)
            .GetProperties()
            .Where(x => x.PropertyType.IsGenericType)
            .Select(x => new DbSetInfo
            {
                DbSet = x.GetGetMethod()!.Invoke(_dbContext, [])!,
                DbSetProperty = x,
                AddMethod = x.PropertyType.GetMethod("Add")!
            })
            .ToList();

        foreach (var (entity, pool) in entities)
        {
            var dbSet = dbSets.Single(x => x.DbSetProperty.PropertyType.GetGenericArguments()[0] == entity.EntityType);
            if (_options.OverrideExistingData)
            {
                clearDbSet(dbSet);
            }
            foreach (var entityObject in pool)
            {
                dbSet.AddMethod.Invoke(dbSet.DbSet, [entityObject]);
            }
        }
        await _dbContext.SaveChangesAsync();
    }
    private void clearDbSet(DbSetInfo dbSet)
    {
        MethodInfo removeRangeMethod = dbSet.DbSetProperty.PropertyType
            .GetMethods()
            .Where(x => x.Name == "RemoveRange")
            .ElementAt(1);
        removeRangeMethod.Invoke(dbSet.DbSet, [dbSet.DbSet]);
    }
    class DbSetInfo
    {
        public object DbSet { get; set; }
        public PropertyInfo DbSetProperty { get; set; }
        public MethodInfo AddMethod { get; set; }
    }
}

实体

    public class TestEntity1
    {
        public int ID { get; set; }
        public int Value { get; set; }
        public string Value2 { get; set; }
        public bool Value3 { get; set; }
        public TestEntity2? Entity2 { get; set; }

    }
    public class TestEntity2
    {
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public string ID { get; set; }
        public string Value { get; set; }
        public string Value2 { get; set; }
        public TestEntity1? Entity1 { get; set; } 
        public int? Entity1ID { get; set; }  
    }
public class TestContext : DbContext
{
    public TestContext() { }
    public TestContext(DbContextOptions options) : base(options) { }
    public DbSet<TestEntity1> Entity1 { get; set; }
    public DbSet<TestEntity2> Entity2 { get; set; }

    protected override void OnConfiguring(DbContextOptionsBuilder builder)
    {
        if (builder.IsConfigured) return;

        builder.EnableSensitiveDataLogging();

        builder.UseInMemoryDatabase("TestDatabase");
    }
    protected override void OnModelCreating(ModelBuilder builder)
    {
        builder.Entity<TestEntity1>()
            .HasKey(e => e.ID);
        builder.Entity<TestEntity2>()
            .HasKey(e => e.ID);

        builder.Entity<TestEntity1>()
            .HasOne(e => e.Entity2)
            .WithOne(e => e.Entity1)
            .HasForeignKey<TestEntity2>(e => e.Entity1ID);
    }
}

EntityInfo 类包含一些有关实体的有用信息,例如类型、实例数量等。在我看来,这些信息与问题无关

池中第一个实例的 ID 为 0,但是当尝试将其添加到 dbSet 时,它突然变为 1。然后,当添加 ID 为 1 的下一个实例时,抛出异常,因为第一个实例的 ID 更改为1:

无法跟踪实体类型“TestEntity1”的实例,因为已跟踪键值为“{ID: 1}”的另一个实例。附加现有实体时,请确保仅附加一个具有给定键值的实体实例。

First step of iteration After adding

为什么会发生这种情况以及如何解决?

c# reflection entity-framework-core
1个回答
0
投票

简单答案:实体的ID不能为零

© www.soinside.com 2019 - 2024. All rights reserved.