mysql 如何使用EF Core正确保存复杂对象

3htmauhk  于 2023-05-28  发布在  Mysql
关注(0)|答案(2)|浏览(224)

我在保存复杂对象时遇到了问题...(简化的)对象看起来像这样:

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)。数据库中已经存在ExcavatorTypeSparePart的示例(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.Typeexcavator.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(服务器端)应用程序,我在这里看到过这样做。

gpfsuwkq

gpfsuwkq1#

您可以使用context.Update(yourEntity)代替context.Add(yourEntity)
.Update()将检测它是否是现有实体的插入或更新,并将相应地采取行动。Doc

qybjjes1

qybjjes12#

看来我已经找到问题的原因了。这里的代码是简化的,实际的代码有点复杂。在代码的某些部分(这里没有显示),我从db上下文添加到跟踪实体示例的列表属性NOT。似乎这就是问题的根源。

相关问题