C# EF核心:更新对象图复制子实体
我们有一个相当复杂的领域模型,我们使用Entityframework核心作为ORM。更新总是在根实体上执行。如果需要添加或更新子对象,则加载根实体,修改子实体,然后保存根实体。与文件的这一部分类似: 我们使用guid作为实体的id,id是由insert上的数据库生成的 这很有效,但有一个问题我无法解决:C# EF核心:更新对象图复制子实体,c#,entity-framework-core,entity-framework-core-2.2,C#,Entity Framework Core,Entity Framework Core 2.2,我们有一个相当复杂的领域模型,我们使用Entityframework核心作为ORM。更新总是在根实体上执行。如果需要添加或更新子对象,则加载根实体,修改子实体,然后保存根实体。与文件的这一部分类似: 我们使用guid作为实体的id,id是由insert上的数据库生成的 这很有效,但有一个问题我无法解决: 我想向StructureTemplate类型的根实体添加一个新元素(GeneralElementTemplate类型) 我从数据库加载StructureTemplate实体和所有子实体(根实体
- 我想向StructureTemplate类型的根实体添加一个新元素(GeneralElementTemplate类型)
- 我从数据库加载StructureTemplate实体和所有子实体(根实体中已经有一个元素-->请参见屏幕截图#1)
- 我创建新元素(命名为elementTemplate)
- 我将新元素添加到根实体的Elements集合中(现在Elements集合中有两个实体-->请参见屏幕截图2)
- 我在DBContext上调用SaveChanges
- 一切都保存得很好
- 但是现在根实体的元素集合中有三个实体!新添加的实体在集合中有两次(请参见屏幕截图#3)李>
- 在数据库(SQL Server)中,按预期插入/更新所有内容。操作完成后,根对象有两个元素(而不是三个)
public class StructureTemplate : Document<StructureTemplate>
{
private HashSet<GeneralElementTemplate> _elements = new HashSet<GeneralElementTemplate>();
private HashSet<StructureTemplateTag> _structureTemplateTags = new HashSet<StructureTemplateTag>();
public StructureTemplate(
DocumentHeader header,
uint versionNumber = InitialLabel,
IEnumerable<GeneralElementTemplate> elements = null)
: base(header, versionNumber)
{
_elements = (elements != null) ? new HashSet<GeneralElementTemplate>(elements) : new HashSet<GeneralElementTemplate>();
}
/// <summary>
/// EF Core ctor
/// </summary>
protected StructureTemplate()
{
}
public IReadOnlyCollection<GeneralElementTemplate> Elements => _elements;
public IReadOnlyCollection<StructureTemplateTag> StructureTemplateTags => _structureTemplateTags;
public override IReadOnlyCollection<Tag> Tags => _structureTemplateTags.Select(x => x.Tag).ToList();
public void AddElementTemplate(GeneralElementTemplate elementTemplate)
{
CheckUnlocked();
_elements.Add(elementTemplate);
}
public override void AddTag(Tag tag) => _structureTemplateTags.Add(new StructureTemplateTag(this, tag));
public void RemoveElementTemplate(Guid elementTemplateId)
{
CheckUnlocked();
var elementTemplate = Elements.FirstOrDefault(x => x.Id == elementTemplateId);
_elements.Remove(elementTemplate);
}
public override void RemoveTag(Tag tag)
{
var existingEntity = _structureTemplateTags.SingleOrDefault(x => x.TagId == tag.Id);
_structureTemplateTags.Remove(existingEntity);
}
public void SetPartTemplateId(Guid? partTemplateId)
{
CheckUnlocked();
PartTemplateId = partTemplateId;
}
}
public class StructureTemplateTypeConfiguration : IEntityTypeConfiguration<StructureTemplate>
{
public void Configure(EntityTypeBuilder<StructureTemplate> builder)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
builder
.Property(e => e.Id)
.ValueGeneratedOnAdd();
builder
.OwnsOne(e => e.Header, headerBuilder =>
{
headerBuilder
.Property(e => e.Name)
.HasConversion<string>(x => x, x => EntityName.ToEntityName(x))
.HasMaxLength(EntityName.NameMaxLength)
.IsUnicode(false);
headerBuilder
.Property(e => e.Descriptions)
.HasConversion(
d => JsonConvert.SerializeObject(d.ToStringDictionary()),
d => d == null
? TranslationDictionary.Empty
: JsonConvert.DeserializeObject<Dictionary<EntityLang, string>>(d).ToTranslationDictionary())
.HasMaxLength((int)TranslatedEntry.EntryMaxLength * (Enum.GetValues(typeof(EntityLang)).Length + 1));
});
builder
.Property(e => e.VersionNumber);
builder
.HasMany(e => e.Elements)
.WithOne();
builder.Metadata.FindNavigation(nameof(StructureTemplate.Elements)).SetPropertyAccessMode(PropertyAccessMode.Field);
// TAGS
builder
.Ignore(e => e.Tags);
builder
.HasMany(e => e.StructureTemplateTags);
builder.Metadata
.FindNavigation(nameof(StructureTemplate.StructureTemplateTags))
.SetPropertyAccessMode(PropertyAccessMode.Field);
}
}
public class StructureElementTemplateTypeConfiguration : IEntityTypeConfiguration<GeneralElementTemplate>
{
public void Configure(EntityTypeBuilder<GeneralElementTemplate> builder)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
builder.ToTable("StructureTemplateElements");
builder
.Property(e => e.Id)
.ValueGeneratedOnAdd();
builder
.Property(e => e.Type);
builder
.HasMany(e => e.Attributes)
.WithOne();
builder.Metadata.FindNavigation(nameof(GeneralElementTemplate.Attributes)).SetPropertyAccessMode(PropertyAccessMode.Field);
builder
.HasMany(e => e.Groups)
.WithOne();
builder.Metadata.FindNavigation(nameof(GeneralElementTemplate.Groups)).SetPropertyAccessMode(PropertyAccessMode.Field);
builder
.HasMany(e => e.Materials)
.WithOne();
builder.Metadata.FindNavigation(nameof(GeneralElementTemplate.Materials)).SetPropertyAccessMode(PropertyAccessMode.Field);
builder
.HasMany(e => e.Points)
.WithOne();
builder.Metadata.FindNavigation(nameof(GeneralElementTemplate.Points)).SetPropertyAccessMode(PropertyAccessMode.Field);
builder
.HasMany(e => e.Sections)
.WithOne();
builder.Metadata.FindNavigation(nameof(GeneralElementTemplate.Sections)).SetPropertyAccessMode(PropertyAccessMode.Field);
}
}
公共类结构模板:文档
{
私有HashSet_元素=新HashSet();
私有HashSet _structureTemplateTags=新HashSet();
公共结构模板(
文档标题,
uint版本号=初始标签,
IEnumerable元素=空)
:base(标题、版本号)
{
_elements=(elements!=null)?新建哈希集(elements):新建哈希集();
}
///
///EF堆芯导体
///
受保护的结构模板()
{
}
公共IReadOnlyCollection元素=>\u元素;
公共IReadOnlyCollection StructureTemplateTags=>\u StructureTemplateTags;
public override IReadOnlyCollection Tags=>_structureTemplateTags.Select(x=>x.Tag.ToList();
公共无效AddElementTemplate(GeneralElementTemplate elementTemplate)
{
选中解锁();
_元素。添加(元素模板);
}
public override void AddTag(Tag Tag)=>\u structureTemplateTags.Add(new StructureTemplateTag(this,Tag));
公共void RemoveElementTemplate(Guid elementTemplateId)
{
选中解锁();
var elementTemplate=Elements.FirstOrDefault(x=>x.Id==elementTemplateId);
_元素。删除(elementTemplate);
}
公共覆盖无效移除标记(标记标记)
{
var existingEntity=_structureTemplateTags.SingleOrDefault(x=>x.TagId==tag.Id);
_structureTemplateTags.Remove(现有实体);
}
公共无效SetPartTemplateId(Guid?partTemplateId)
{
选中解锁();
PartTemplateId=PartTemplateId;
}
}
GeneralElementTemplate实现:
public class StructureTemplate : Document<StructureTemplate>
{
private HashSet<GeneralElementTemplate> _elements = new HashSet<GeneralElementTemplate>();
private HashSet<StructureTemplateTag> _structureTemplateTags = new HashSet<StructureTemplateTag>();
public StructureTemplate(
DocumentHeader header,
uint versionNumber = InitialLabel,
IEnumerable<GeneralElementTemplate> elements = null)
: base(header, versionNumber)
{
_elements = (elements != null) ? new HashSet<GeneralElementTemplate>(elements) : new HashSet<GeneralElementTemplate>();
}
/// <summary>
/// EF Core ctor
/// </summary>
protected StructureTemplate()
{
}
public IReadOnlyCollection<GeneralElementTemplate> Elements => _elements;
public IReadOnlyCollection<StructureTemplateTag> StructureTemplateTags => _structureTemplateTags;
public override IReadOnlyCollection<Tag> Tags => _structureTemplateTags.Select(x => x.Tag).ToList();
public void AddElementTemplate(GeneralElementTemplate elementTemplate)
{
CheckUnlocked();
_elements.Add(elementTemplate);
}
public override void AddTag(Tag tag) => _structureTemplateTags.Add(new StructureTemplateTag(this, tag));
public void RemoveElementTemplate(Guid elementTemplateId)
{
CheckUnlocked();
var elementTemplate = Elements.FirstOrDefault(x => x.Id == elementTemplateId);
_elements.Remove(elementTemplate);
}
public override void RemoveTag(Tag tag)
{
var existingEntity = _structureTemplateTags.SingleOrDefault(x => x.TagId == tag.Id);
_structureTemplateTags.Remove(existingEntity);
}
public void SetPartTemplateId(Guid? partTemplateId)
{
CheckUnlocked();
PartTemplateId = partTemplateId;
}
}
public class StructureTemplateTypeConfiguration : IEntityTypeConfiguration<StructureTemplate>
{
public void Configure(EntityTypeBuilder<StructureTemplate> builder)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
builder
.Property(e => e.Id)
.ValueGeneratedOnAdd();
builder
.OwnsOne(e => e.Header, headerBuilder =>
{
headerBuilder
.Property(e => e.Name)
.HasConversion<string>(x => x, x => EntityName.ToEntityName(x))
.HasMaxLength(EntityName.NameMaxLength)
.IsUnicode(false);
headerBuilder
.Property(e => e.Descriptions)
.HasConversion(
d => JsonConvert.SerializeObject(d.ToStringDictionary()),
d => d == null
? TranslationDictionary.Empty
: JsonConvert.DeserializeObject<Dictionary<EntityLang, string>>(d).ToTranslationDictionary())
.HasMaxLength((int)TranslatedEntry.EntryMaxLength * (Enum.GetValues(typeof(EntityLang)).Length + 1));
});
builder
.Property(e => e.VersionNumber);
builder
.HasMany(e => e.Elements)
.WithOne();
builder.Metadata.FindNavigation(nameof(StructureTemplate.Elements)).SetPropertyAccessMode(PropertyAccessMode.Field);
// TAGS
builder
.Ignore(e => e.Tags);
builder
.HasMany(e => e.StructureTemplateTags);
builder.Metadata
.FindNavigation(nameof(StructureTemplate.StructureTemplateTags))
.SetPropertyAccessMode(PropertyAccessMode.Field);
}
}
public class StructureElementTemplateTypeConfiguration : IEntityTypeConfiguration<GeneralElementTemplate>
{
public void Configure(EntityTypeBuilder<GeneralElementTemplate> builder)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
builder.ToTable("StructureTemplateElements");
builder
.Property(e => e.Id)
.ValueGeneratedOnAdd();
builder
.Property(e => e.Type);
builder
.HasMany(e => e.Attributes)
.WithOne();
builder.Metadata.FindNavigation(nameof(GeneralElementTemplate.Attributes)).SetPropertyAccessMode(PropertyAccessMode.Field);
builder
.HasMany(e => e.Groups)
.WithOne();
builder.Metadata.FindNavigation(nameof(GeneralElementTemplate.Groups)).SetPropertyAccessMode(PropertyAccessMode.Field);
builder
.HasMany(e => e.Materials)
.WithOne();
builder.Metadata.FindNavigation(nameof(GeneralElementTemplate.Materials)).SetPropertyAccessMode(PropertyAccessMode.Field);
builder
.HasMany(e => e.Points)
.WithOne();
builder.Metadata.FindNavigation(nameof(GeneralElementTemplate.Points)).SetPropertyAccessMode(PropertyAccessMode.Field);
builder
.HasMany(e => e.Sections)
.WithOne();
builder.Metadata.FindNavigation(nameof(GeneralElementTemplate.Sections)).SetPropertyAccessMode(PropertyAccessMode.Field);
}
}
公共类GeneralElementTemplate:实体
{
私有HashSet_attributes=新HashSet();
私有HashSet_groups=新HashSet();
私有HashSet_materials=新HashSet();
私有HashSet_points=新HashSet();
私有HashSet_sections=新HashSet()
公共GeneralElementTemplate(
ElementTemplateType类型,
IEnumerable points=null,
IEnumerable sections=null,
IEnumerable materials=null,
IEnumerable groups=null,
IEnumerable属性=空)
:base()
{
类型=类型;
_points=points!=null?新哈希集(points):新哈希集();
_sections=sections!=null?新哈希集(sections):新哈希集();
_materials=materials!=null?新哈希集(materials):新哈希集();
_groups=groups!=null?新建哈希集(组):新建哈希集();
_attributes=属性!=null?新哈希集(属性):新哈希集();
}
///
///EF堆芯导体
///
受保护的GeneralElementTemplate()
{
}
公共IReadOnlyCollection属性=>\u属性;
公共IReadOnlyCollection组=>\u组;
公共IReadOnlyCollection物料=>\u物料;
公共IReadOnlyCollection点数=>\u点数;
公共IReadOnlyCollection节=>\u节;
public ElementTemplateType类型{get;private set;}
公共虚拟GeneralElementTemplate转世()
{
返回新的GeneralElementTemplate(
类型,
要点,,
部分,
材料,
组,
属性);
}
}
结构模板的实体类型配置:
public class StructureTemplate : Document<StructureTemplate>
{
private HashSet<GeneralElementTemplate> _elements = new HashSet<GeneralElementTemplate>();
private HashSet<StructureTemplateTag> _structureTemplateTags = new HashSet<StructureTemplateTag>();
public StructureTemplate(
DocumentHeader header,
uint versionNumber = InitialLabel,
IEnumerable<GeneralElementTemplate> elements = null)
: base(header, versionNumber)
{
_elements = (elements != null) ? new HashSet<GeneralElementTemplate>(elements) : new HashSet<GeneralElementTemplate>();
}
/// <summary>
/// EF Core ctor
/// </summary>
protected StructureTemplate()
{
}
public IReadOnlyCollection<GeneralElementTemplate> Elements => _elements;
public IReadOnlyCollection<StructureTemplateTag> StructureTemplateTags => _structureTemplateTags;
public override IReadOnlyCollection<Tag> Tags => _structureTemplateTags.Select(x => x.Tag).ToList();
public void AddElementTemplate(GeneralElementTemplate elementTemplate)
{
CheckUnlocked();
_elements.Add(elementTemplate);
}
public override void AddTag(Tag tag) => _structureTemplateTags.Add(new StructureTemplateTag(this, tag));
public void RemoveElementTemplate(Guid elementTemplateId)
{
CheckUnlocked();
var elementTemplate = Elements.FirstOrDefault(x => x.Id == elementTemplateId);
_elements.Remove(elementTemplate);
}
public override void RemoveTag(Tag tag)
{
var existingEntity = _structureTemplateTags.SingleOrDefault(x => x.TagId == tag.Id);
_structureTemplateTags.Remove(existingEntity);
}
public void SetPartTemplateId(Guid? partTemplateId)
{
CheckUnlocked();
PartTemplateId = partTemplateId;
}
}
public class StructureTemplateTypeConfiguration : IEntityTypeConfiguration<StructureTemplate>
{
public void Configure(EntityTypeBuilder<StructureTemplate> builder)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
builder
.Property(e => e.Id)
.ValueGeneratedOnAdd();
builder
.OwnsOne(e => e.Header, headerBuilder =>
{
headerBuilder
.Property(e => e.Name)
.HasConversion<string>(x => x, x => EntityName.ToEntityName(x))
.HasMaxLength(EntityName.NameMaxLength)
.IsUnicode(false);
headerBuilder
.Property(e => e.Descriptions)
.HasConversion(
d => JsonConvert.SerializeObject(d.ToStringDictionary()),
d => d == null
? TranslationDictionary.Empty
: JsonConvert.DeserializeObject<Dictionary<EntityLang, string>>(d).ToTranslationDictionary())
.HasMaxLength((int)TranslatedEntry.EntryMaxLength * (Enum.GetValues(typeof(EntityLang)).Length + 1));
});
builder
.Property(e => e.VersionNumber);
builder
.HasMany(e => e.Elements)
.WithOne();
builder.Metadata.FindNavigation(nameof(StructureTemplate.Elements)).SetPropertyAccessMode(PropertyAccessMode.Field);
// TAGS
builder
.Ignore(e => e.Tags);
builder
.HasMany(e => e.StructureTemplateTags);
builder.Metadata
.FindNavigation(nameof(StructureTemplate.StructureTemplateTags))
.SetPropertyAccessMode(PropertyAccessMode.Field);
}
}
public class StructureElementTemplateTypeConfiguration : IEntityTypeConfiguration<GeneralElementTemplate>
{
public void Configure(EntityTypeBuilder<GeneralElementTemplate> builder)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
builder.ToTable("StructureTemplateElements");
builder
.Property(e => e.Id)
.ValueGeneratedOnAdd();
builder
.Property(e => e.Type);
builder
.HasMany(e => e.Attributes)
.WithOne();
builder.Metadata.FindNavigation(nameof(GeneralElementTemplate.Attributes)).SetPropertyAccessMode(PropertyAccessMode.Field);
builder
.HasMany(e => e.Groups)
.WithOne();
builder.Metadata.FindNavigation(nameof(GeneralElementTemplate.Groups)).SetPropertyAccessMode(PropertyAccessMode.Field);
builder
.HasMany(e => e.Materials)
.WithOne();
builder.Metadata.FindNavigation(nameof(GeneralElementTemplate.Materials)).SetPropertyAccessMode(PropertyAccessMode.Field);
builder
.HasMany(e => e.Points)
.WithOne();
builder.Metadata.FindNavigation(nameof(GeneralElementTemplate.Points)).SetPropertyAccessMode(PropertyAccessMode.Field);
builder
.HasMany(e => e.Sections)
.WithOne();
builder.Metadata.FindNavigation(nameof(GeneralElementTemplate.Sections)).SetPropertyAccessMode(PropertyAccessMode.Field);
}
}
公共类结构TemplateTypeConfiguration:IEntityTypeConfiguration
{
公共void配置(EntityTypeBuilder)
{
if(builder==null)
{
抛出新ArgumentNullException(nameof(builder));
}
建设者
.Property(e=>e.Id)
.ValueGeneratedOnAdd();
建设者
.OwnsOne(e=>e.标题,标题生成器=>
{
海德堡
.Property(e=>e.Name)
.HasConversion(x=>x,x=>EntityName.ToEntityName(x))
.HasMaxLength(EntityName.NameMaxLength)
.IsUnicode(假);
海德堡
.Property(e=>e.Descriptions)
.哈斯转换(
d=>JsonConvert.SerializedObject(d.ToString目录()),
d=>d==null
?TranslationDictionary。为空