Mur*_*ock 6 c# domain-driven-design entity-framework-core ef-core-6.0
想象一下我有以下代码。
var myEntity = Context.MyEntities.GetById(id);
myEntity.SomeNavigationProperty = new MyNavigationProperty(...);
await Context.SaveChangesAsync();
Run Code Online (Sandbox Code Playgroud)
我有以下 MyEntity 映射
builder.HasOne(o => o.SomeNavigationProperty).WithOne().HasForeignKey<MyEntity>(o => o.SomeNavigationPropertyId);
Run Code Online (Sandbox Code Playgroud)
SaveChangesAsync 失败并显示:
数据库操作预计影响1行,但实际影响0行;自加载实体以来,数据可能已被修改或删除。
如果我在保存后查看更改跟踪器,MyNavigationProperty已被标记为已修改且未添加。
有效的版本
var myEntity = new MyEntity(...);
myEntity.SomeNavigationProperty = new MyNavigationProperty(...);
await Context.AddAsync(myEntity);
await Context.SaveChangesAsync();
Run Code Online (Sandbox Code Playgroud)
和
var myEntity = Context.MyEntities.GetById(id);
myEntity.SomeNavigationProperty = new MyNavigationProperty(...);
await Context.AddAsync(myEntity.SomeNavigationProperty);
await Context.SaveChangesAsync();
Run Code Online (Sandbox Code Playgroud)
如果我阅读https://learn.microsoft.com/en-us/dotnet/api/microsoft.entityframeworkcore.dbcontext.addasync?view=efcore-6.0 ,这种行为在某种程度上是有意义的。我不完全理解为什么 ef core 决定将未跟踪的实体标记为已修改而不是已添加。
不管怎样......我的问题是我的域和存储库是分开的。换句话说,当属性被设置时,我无权访问上下文。
有没有一种方法可以告诉 EF Core 上下文重新添加所有内容(如果尚未添加)而不抱怨重复项?
就像是
await Context.RefreshAddAsync(myEntity);
Run Code Online (Sandbox Code Playgroud)
其中RefreshAddAsync将跳过MyEntity(如果已添加)并标记MyNavigationProperty为已添加。
任何其他建议表示赞赏。最糟糕的情况是,我将编写一个RefreshAddAsync使用反射的版本,但如果有的话,我希望有一个更干净的解决方案。
我最终做到了这一点,效果非常好。
public static async Task AddOrUpdateEntityAsync<T>(this ArtBankContext context, T entity)
where T : DomainEntity
{
context.ThrowIfNull();
entity.ThrowIfNull();
var toProcessQueue = new Queue<DomainEntity>();
var processed = new List<DomainEntity>();
var tracked = RunWithAutoDetectChangesDisabled(() => context.ChangeTracker
.Entries<DomainEntity>()
.Select(o => o.Entity));
toProcessQueue.Enqueue(entity);
while (toProcessQueue.Any())
{
var toProcess = toProcessQueue.Dequeue();
if (!tracked.Any(o => o == toProcess))
{
await context
.AddEntityAsync(toProcess)
.ContinueOnAnyContext();
}
foreach (var toProcessPropertyInfo in toProcess
.GetType()
.GetProperties()
.Where(o => o.PropertyType.IsAssignableTo(typeof(DomainEntity))))
{
var toProcessProperty = (DomainEntity?)toProcessPropertyInfo.GetValue(toProcess);
if (toProcessProperty == null || processed.Any(o => o == toProcessProperty))
continue;
toProcessQueue.Enqueue(toProcessProperty);
}
processed.Add(toProcess);
}
TResult RunWithAutoDetectChangesDisabled<TResult>(Func<TResult> func)
{
//Ef auto detects changes when you call methods like context.ChangeTracker.Entries
//In this specific use case the entities that we want to add dynamically gets detected and incorreectly added to the contexts.
//Hence we disable the flag before executing
var originalAutoDetectChangesEnabledValue = context.ChangeTracker.AutoDetectChangesEnabled;
context.ChangeTracker.AutoDetectChangesEnabled = false;
var result = func();
context.ChangeTracker.AutoDetectChangesEnabled = originalAutoDetectChangesEnabledValue;
return result;
}
}
Run Code Online (Sandbox Code Playgroud)
这是用过的
var myEntity = Context.MyEntities.GetById(id);
myEntity.SomeNavigationProperty = new MyNavigationProperty(...);
await Context.AddOrUpdateEntityAsync(myEntity);
await Context.SaveChangesAsync();
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
2139 次 |
| 最近记录: |