我可以将子集合分配给 new 父对象,并在保存时按预期保存关系。但是,如果父对象已经存在并且我尝试相同,那么我希望创建的新关系不会,并且旧关系仍然存在。
我是唯一可以做这种更改的方法,首先找出要删除/添加的项目然后再调用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
}
}
答案 0 :(得分:0)
您需要第三个表链接标记和位置,包含您尝试链接的两个表的ID,这是一个数据库法,您不能拥有无限和可变数量的外键你的每一张桌子。
答案 1 :(得分:0)
集合需要调用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;
清除以下内容:
答案 2 :(得分:0)
我刚刚运行你的测试,这里的行为略有不同(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);
}
}
}
}