在代码优先实体框架中,我应该如何重新分配多对子关系
本文关键字:分配 何重新 关系 我应该 代码 实体 框架 | 更新日期: 2023-09-27 18:01:09
我可以将子集合分配给新的父对象,在保存时,关系将按预期保存。但是,如果父对象已经存在,并且我尝试了同样的操作,则我希望创建的新关系不会存在,并且旧关系仍然存在。
对我来说,唯一的方法是首先计算出要删除/添加的项,然后分别调用Remove((和Add((吗?或者,我在下面的代码中遗漏了什么吗;
public class Tag
{
public int TagId { get; set; }
public virtual ICollection<Location> Locations { get; set;}
}
public class Location
{
public int LocationId { get; set; }
public virtual ICollection<Tag> Tags { get; set; }
}
public class Tests()
{
public void Create()
{
db.Locations.Add(new Location { Tags = db.Tags.Where(p => p.TagId == 2)}).ToList();
db.SaveChanges() // correctly saves the new location with TagId 2 attached
}
public void Edit()
{
var location = db.Locations.Single(p => p.LocationId == 1);
location.Tags = db.Tags.Where(p => p.TagId == 1).ToList();
db.Entry(location).State = EntityState.Modified;
db.SaveChanges(); // TagId 2 still attached, rather than TagId 1
}
}
您需要第三个链接标签和位置的表,其中包含您试图链接的两个表的id,这是数据库规则,您不能在每个表中都有无限和可变数量的外键。
集合需要调用Clear((。
public void Edit()
{
var location = db.Locations.Single(p => p.LocationId == 1);
location.Tags.Clear(); // added this line
location.Tags = db.Tags.Where(p => p.TagId == 1).ToList();
db.Entry(location).State = EntityState.Modified;
db.SaveChanges(); // TagId 1 now correctly attached
}
来自MSDN;
Clear执行以下操作:
- 将IsLoaded标志设置为false
- 从集合中删除所有实体
- 分离已删除实体和ObjectStateManager中EntityCollection的所有者
- 从相关的中删除EntityCollection的所有者实体
我刚刚运行了您的测试,这里的行为略有不同(EF 6.1.3(:
如果不调用Clear((,则新标记将添加到集合中。但是,如果存在已存在的标记,则会引发DbUpdateException。
如果调用Clear((,则只有新的标记会与该位置关联。
因此,使用Clear((通常是正确的做法
请参阅下面的示例代码:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Data.Common;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication1
{
public class Location
{
[Key]
public int LocationId { get; set; }
public virtual ICollection<Tag> Tags { get; set; }
}
public class Tag
{
[Key]
public int TagId { get; set; }
public virtual ICollection<Location> Locations { get; set; }
}
public class TagLocsDbContext : DbContext
{
public virtual DbSet<Tag> Tags { get; set; }
public virtual DbSet<Location> Locations { get; set; }
}
class DbInitializer : DropCreateDatabaseAlways<TagLocsDbContext>
{
protected override void Seed(TagLocsDbContext context)
{
base.Seed(context);
context.Tags.Add(new Tag() { });
context.Tags.Add(new Tag() { });
context.Tags.Add(new Tag() { });
context.Tags.Add(new Tag() { });
}
}
public class Tests
{
public void Create()
{
using (var db = new TagLocsDbContext())
{
db.Locations.Add(new Location { Tags = db.Tags.Where(p => p.TagId == 2).ToList() });
db.SaveChanges(); // correctly saves the new location with TagId 2 attached
}
}
public void Edit(bool clear)
{
using (var db = new TagLocsDbContext())
{
var location = db.Locations.Single(p => p.LocationId == 1);
if (clear) location.Tags.Clear();
location.Tags = db.Tags.Where(p => p.TagId == 1).ToList();
db.SaveChanges(); // if Clear ran locations = {1}, otherwise it is {1,2}
}
}
public void EditWithConflict()
{
using (var db = new TagLocsDbContext())
{
var location = db.Locations.Single(p => p.LocationId == 1);
location.Tags = db.Tags.ToList();
db.SaveChanges(); //Conflict - will throw an exception
}
}
}
class Program
{
static void Main(string[] args)
{
var initializer = new DbInitializer();
Database.SetInitializer(initializer);
var tests = new Tests();
tests.Create();
PrintLocation("After create: ", 1);
tests.Edit(false);
PrintLocation("After edit without clear: ", 1);
tests.Edit(true);
PrintLocation("After edit with clear: ", 1);
try
{
tests.EditWithConflict();
PrintLocation("After edit with clear: ", 1);
}
catch (DbUpdateException exc)
{
Console.WriteLine("Exception thrown : {0}",exc.Message);
PrintLocation("After edit with conflicting tags: ", 1);
}
Console.ReadLine();
}
private static void PrintLocation(string afterCreate, int i)
{
Console.WriteLine(afterCreate);
using (var db = new TagLocsDbContext())
{
var location = db.Locations.Single(a => a.LocationId == i);
var tags = string.Join(",", location.Tags.Select(a => a.TagId));
Console.WriteLine("Location {0} : Tags {1}", location.LocationId, tags);
}
}
}
}