EF6子实体在多对多关系中没有更新
本文关键字:更新 关系 实体 EF6 | 更新日期: 2023-09-27 18:12:27
我有两个多对多关系的实体:一个Map可以有多个Tag(一个Tag反过来可以被多个Map使用)。
我正在尝试更新父Map实体,包括从其子标签集合中删除项。当Map实体响应数据库中的更改时,对Tags集合的更改从不响应(除了它们的初始创建)。知道我哪里做错了吗?
数据库中有3个表:
- 标记
- MapTags
实体类:
public class Map
{
public Map()
{
Tags = new List<Tag>();
}
public string Id { get; set; }
...
public ICollection<Tag> Tags { get; set; }
}
public class Tag
{
public Tag()
{
Maps = new List<Map>();
}
public int Id { get; set; }
public string Text { get; set; }
public ICollection<Map> Maps { get; set; }
}
和EF6的映射:
public class MapMap : EntityTypeConfiguration<Map>
{
public MapMap()
{
// Primary Key
this.HasKey(t => new { t.Id });
// Properties
this.Property(t => t.Id)
.IsRequired()
.HasMaxLength(32);
...
this.ToTable("Map");
this.Property(t => t.Id).HasColumnName("Id");
...
// Relationships
this.HasMany(m => m.Tags)
.WithMany(t => t.Maps)
.Map(m =>
{
m.MapLeftKey("MapId");
m.MapRightKey("MapTagId");
m.ToTable("MapTags");
});
}
}
public class TagMap : EntityTypeConfiguration<Tag>
{
public TagMap()
{
// Primary Key
this.HasKey(t => new { t.Id });
// Properties
this.Property(t => t.Id)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
this.Property(t => t.Text)
.IsRequired()
.HasMaxLength(256);
// Table & Column Mappings
this.ToTable("Tag");
this.Property(t => t.Id).HasColumnName("Id");
this.Property(t => t.Text).HasColumnName("Text");
// Relationships
this.HasMany(t => t.Maps)
.WithMany(m => m.Tags)
.Map(m =>
{
m.MapLeftKey("TagId");
m.MapRightKey("MapId");
m.ToTable("MapTags");
});
}
}
更新地图标签的代码:
map.Tags = new List<Tag>();
foreach (string item in data.tags)
{
Tag tag = MapRepository.FindTagByText(item);
if (tag == null)
{
try
{
tag = WebMapRepository.CreateTag(new Tag()
{
Text = item
});
}
catch (DbEntityValidationException ex)
{
DisplayValidationErrors(ex, "Tag [" + item + "] validation errors:");
throw; // Abort
}
}
map.Tags.Add(tag);
}
和更新地图的DAL代码:
public static Map UpdateMap(Map map)
{
using (MapContext context = new MapContext())
{
context.Maps.Attach(map);
context.Entry(map).State = EntityState.Modified;
context.SaveChanges();
return GetMap(map.Id);
}
}
解决方案虽然我更喜欢更优雅的解决方案,但现在我只是直接运行SQL手动刷新我的关系。
在UpdateMap中查看您的代码,看起来您正在断开连接的场景中工作?
如果是,在断开连接的场景中重新连接实体有两个步骤:
- 将实体图重新附加到上下文,以便跟踪
- 设置图形 中每个实体的状态
在根Map实体上设置EntityState为Modified将把对象图中的所有实体附加到上下文。但是,它会用Unchanged状态标记所有子Tag实体。这将对应于您的评论,即对Map实体的更改正在持久化,但对Tag集合的更改没有。
这个问题的解决方案很大程度上取决于应用程序的体系结构。一种可能的解决方案是从数据库中获取相应的Map实体,然后根据更新后的地图遍历其对象图,并相应地绘制其状态。
下面的示例显示了被查询的映射实体集合中的标签,这些标签不在断开连接的映射的标签集合中,它们与映射的关系被删除:
public static Map UpdateMap(Map map)
{
// get map in its current state
var previousMap = context.Maps
.Where(m => m.Id == map.Id)
.Include(m => m.Tags)
.Single();
// work out tags deleted in the updated map
var deletedTags = previousMap.Tags.Except(map.Tags).ToList();
// remove the references to removed tags
deletedTags.ForEach(t => previousMap.Tags.Remove(t));
// .. deal with added tags
// very similar code to deleted so not showing
context.SaveChanges();
}
要做到这一点,你的标签类型需要实现IEquatable<Tag>
,以允许Except
操作在集合上正确工作(因为它是一个引用类型)。
注意我使用HashSets
而不是在Lists
的问题,但这只是一个实现细节。
。
public class Tag : IEquatable<Tag>
{
public Tag()
{
Maps = new HashSet<Map>();
}
public int Id { get; set; }
public string Text { get; set; }
public virtual ISet<Map> Maps { get; private set; }
public bool Equals(Tag other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return Id == other.Id;
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != GetType()) return false;
return Equals((Tag) obj);
}
public override int GetHashCode()
{
return Id;
}
public static bool operator ==(Tag left, Tag right)
{
return Equals(left, right);
}
public static bool operator !=(Tag left, Tag right)
{
return !Equals(left, right);
}
}
我将把测试项目放到GitHub上。
你的问题是你正在手动管理集合,并且与上下文分离,这是存储库模式的副作用,这就是为什么许多人说它是反模式的原因。
- 尝试删除新的列表和构造函数,并使iccollection为虚拟的。
- 因为标签有一个id匹配地图上的标签通过id而不是通过文本
- 你的更新地图正在附加地图,看看附加标签是否解决了问题。
- 我不知道,因为我在上下文中做了配置,但是你应该只需要在一个实体上映射多对多关系。
我认为你所有的问题都是因为你在使用存储库模式,我总是避免使用它,因为它产生的问题比它解决的问题更多,这只是我的观点,许多专家不同意。