EF Core 在添加时更改实体的 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; }  
}

DbContext

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.