我在保存复杂对象时遇到了问题...(简化的)对象看起来像这样:
public class Excavator
{
public int Id { get; set; }
public ExcavatorType Type { get; set; } = new();
public IList<ExcavatorProperty> Properties { get; set; } = new List<ExcavatorProperty>();
public IList<SparePart> SpareParts { get; set; } = new List<SparePart>();
}
public class ExcavatorType
{
public int Id { get; set; }
public IList<ExcavatorPropertyType> PropertyTypes { get; set; } = new List<ExcavatorPropertyType>();
public IList<Excavator> ExcavatorsOfThisType { get; set; } = new List<Excavator>();
}
public class ExcavatorProperty
{
public int Id { get; set; }
public ExcavatorPropertyType PropertyType { get; set; } = null!;
}
public class SparePart
{
public int Id { get; set; }
public IList<Excavator> Excavators { get; set; } = new List<Excavator>();
}
public class ExcavatorPropertyType
{
public int Id { get; set; }
public IList<ExcavatorType> ExcavatorTypesWithThisProperty { get; set; } = new List<ExcavatorType>()!;
}
我想做的是保存excavator
(类型为Excavator
)。数据库中已经存在ExcavatorType
和SparePart
的示例(ExcavatorPropertyType
也是如此)。
我用表单中的数据填充了excavator
,在阅读了这个问题的答案后,我尝试了以下操作:
var excavatorTypeTmp = await context.ExcavatorTypes
.FirstAsync(et => et.Id == excavator.Type.Id);
excavator.Type = excavatorTypeTmp;
var sparePartsIds = excavator.SpareParts.Select(sp => sp.Id);
excavator.SpareParts = await context.SpareParts
.Where(sp => sparePartsIds.Contains(sp.Id))
.ToListAsync();
context.Add(excavator);
await context.SaveChangesAsync();
我得到的错误:“* 无法跟踪实体类型'ExcavatorType'的示例,因为已在跟踪另一个具有'Id'}的相同键值的示例。附加现有实体时,请确保仅附加一个具有给定键值的实体示例。*”
我也尝试了.Attach()
和.AttachRange()
:
context.Attach(excavator.Type);
context.AttachRange(excavator.SpareParts);
context.Add(excavator);
await context.SaveChangesAsync();
我得到的错误:“* 无法跟踪实体类型'Excavator'的示例,因为已在跟踪另一个具有'Id'}的相同键值的示例。附加现有实体时,请确保仅附加一个具有给定键值的实体示例。*”
然后我甚至尝试了(因为前面提到的问题中的一些评论):
var typeTmp = excavator.Type;
excavator.Type = null!;
context.Attach(typeTmp);
excavator.Type = typeTmp;
var sparePartsTmp = excavator.SpareParts;
excavator.SpareParts = null!;
context.AttachRange(sparePartsTmp);
excavator.SpareParts = sparePartsTmp;
context.Add(excavator);
await context.SaveChangesAsync();
我得到的错误:和前一个一样。
如果我没有弄错的话,问题发生在我们试图连接备件(context.AttachRange(sparePartsTmp);
)时。
我发现的另一件事是,当我不节省备件时,就没有问题。挖掘机似乎保存正确(但没有备件当然)。
所以我想问题可能是我引用了来自excavator.Type
和excavator.SpareParts
的相同挖掘机实体(我尝试使用相同的id附加多个挖掘机)。当我试图以这种方式保存它时(我通过分配null从备用部件中删除了对excavator
s的引用)...:
context.Attach(excavator.Type);
var excavatorsTmp = new List<IList<Excavator>>(excavator.SpareParts.Count);
for (int i = 0; i < excavator.SpareParts.Count; i++)
{
var sparePart = excavator.SpareParts[i];
excavatorsTmp.Add(sparePart.Excavators);
sparePart.Excavators = null!;
}
context.AttachRange(excavator.SpareParts);
context.Add(excavator);
await context.SaveChangesAsync();
...它似乎工作得很好-挖掘机被正确保存了。但我相信有一些其他的(更合理的)方法如何保存excavator
。
编辑(更多信息):
我使用的是using
语句(using declaration 更精确)。我创建context
如下:
using var context = factory.CreateDbContext();
.CreateDbContext()
看起来像这样:
var connectionString = GetConnectionString();
var optionsBuilder = new DbContextOptionsBuilder<MyDbContext>();
optionsBuilder.UseMySql(connectionString, ServerVersion.AutoDetect(connectionString));
return new MyDbContext(optionsBuilder.Options);
我这样做是因为它是一个Blazor(服务器端)应用程序,我在这里看到过这样做。
2条答案
按热度按时间gpfsuwkq1#
您可以使用
context.Update(yourEntity)
代替context.Add(yourEntity)
。.Update()
将检测它是否是现有实体的插入或更新,并将相应地采取行动。Docqybjjes12#
看来我已经找到问题的原因了。这里的代码是简化的,实际的代码有点复杂。在代码的某些部分(这里没有显示),我从db上下文添加到跟踪实体示例的列表属性NOT。似乎这就是问题的根源。