我应该如何在Code First Entity Framework中重新分配多对多的子关系?

时间:2015-07-06 13:44:35

标签: c# entity-framework ef-code-first entity-framework-6

我可以将子集合分配给 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
    }
}

3 个答案:

答案 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;

清除以下内容:

  • 将IsLoaded标志设置为false。
  • 从集合中删除所有实体。
  • 分离已删除实体与之间的关系 来自ObjectStateManager的EntityCollection的所有者。
  • 从相关内容中删除EntityCollection的所有者 实体。

答案 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);

            }
        }
    }
}