在代码优先实体框架中,我应该如何重新分配多对子关系

本文关键字:分配 何重新 关系 我应该 代码 实体 框架 | 更新日期: 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);
            }
        }
    }
}