我正在开发一个为关系数据库生成随机数据的库。我使用反射在我的
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}”的另一个实例。附加现有实体时,请确保仅附加一个具有给定键值的实体实例。
迭代的第一步:
添加后:
为什么会发生这种情况以及如何解决?
简单答案:实体的 ID 不能为零