如何使用EF6更新多对多表 [英] How can I use EF6 to update a many to many table

查看:297
本文介绍了如何使用EF6更新多对多表的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有两个类:

 公共部分类ObjectiveDetail {
    公共ObjectiveDetail(){
        this.SubTopics =新的List<&子话题GT;();
    }
    公众诠释ObjectiveDetailId {搞定;组; }
    公众诠释号码{搞定;组; }
    公共字符串文本{搞定;组; }
    公共虚拟的ICollection<&子话题GT;子专题{搞定;组; }
}
公共部分类子话题{
    公众诠释SubTopicId {搞定;组; }
    公共字符串名称{;组; }
}

我有来自用户的ObjectiveDetail对象:

  VAR网络= {
 objectiveDetailId:1,
 1号,
 文:datafromweb
 副题:[
              {subTopicId:1,
               名:一
              },
              {subTopicId:3,
               名:三
              }
             ]
}

和从数据库中ObjectiveDetail:

 变种DB = {
 objectiveDetailId:1,
 1号,
 文:datafromdb
 副题:[
              {subTopicId:1,
               名:一
              },
              {subTopicId:2,
               名:两节,
              }
             ]
}

通过实体框架6我知道我可以使用更新的ObjectiveDetail类文本:

  _uow.ObjectiveDetails.Update(网络));

但我怎么能更新到ObjectiveDetail和副主题中的多对多表联接这两个表中的引用。这里例如我希望它使得用于ObjectiveDetail 1的多对多改变为引用subTopicId 1和3的代替值1和2。注意,ObjectiveDetail和子话题存储在表中,它们之间的另一个表。这里的DDL:

  CREATE TABLE [DBO]。[ObjectiveDetail](
    [ObjectiveDetailId] INT IDENTITY(1,1)NOT NULL,
    【正文】NVARCHAR(MAX)NOT NULL,
    [ObjectiveTopicId] INT NULL,
    约束[PK_ObjectiveDetail] PRIMARY KEY CLUSTERED([ObjectiveDetailId] ASC)
);CREATE TABLE [DBO]。[ObjectiveTopic](
    [ObjectiveDetailId] INT NOT NULL,
    [SubTopicId] INT NOT NULL,
    约束[FK_ObjectiveTopicObjectiveDetail]外键([ObjectiveDetailId])参考文献[DBO]。[ObjectiveDetail]([ObjectiveDetailId]),
    约束[FK_ObjectiveTopicSubTopic]外键([SubTopicId])参考文献[DBO]。[子话题]([SubTopicId])
);CREATE TABLE [DBO]。[子话题](
    [SubTopicId] INT IDENTITY(1,1)NOT NULL,
    [名] NVARCHAR(150)NOT NULL,
    约束[PK_SubTopic] PRIMARY KEY CLUSTERED([SubTopicId] ASC)
);

下面是我有EF映射:

 公共类ObjectiveDetailMap:EntityTypeConfiguration< ObjectiveDetail>
{
    公共ObjectiveDetailMap()
    {
        // 首要的关键
        this.HasKey(T => t.ObjectiveDetailId);
        //关系
        this.HasMany(T => t.SubTopics)
           .WithMany(T => t.ObjectiveDetails)
           .MAP(M =>
           {
               m.ToTable(ObjectiveTopic);
               m.MapLeftKey(ObjectiveDetailId);
               m.MapRightKey(SubTopicId);
           });    }
}


解决方案

我认为你正试图模拟离线模式为用户的工作。因此,当你从你的用户的东西,你想与用户数据同步数据库。我做出了榜样,并采取了你的问题仅有一步之:)我说,需要在数据库中更新一个副主题。确定这里是code:

 静态无效的主要(字串[] args)
{
    //数据库
    VAR ObjectiveDetails =新的List< ObjectiveDetail>()
    {
        新ObjectiveDetail()
        {
            ObjectiveDetailId = 1,
            数= 1,
            文本=datafromdb
            子专题=新的List<&子话题GT;()
            {
                新的子话题(){SubTopicId = 1,名称=一} //没有变化
                新的子话题(){SubTopicId = 2,名称=两节} //将被删除
                新的子话题(){SubTopicId = 4,名称=四个一} //进行更新
            }
        }
    };    //对象当属JSON和序列化定义的对象。
    VAR网络=新ObjectiveDetail()
    {
        ObjectiveDetailId = 1,
        数= 1,
        文本=datafromweb
        子专题=新的List<&子话题GT;()
        {
            新的子话题(){SubTopicId = 1,名称=一} //没有变化
            新的子话题(){SubTopicId = 3,名称=三大} //新行
            新的子话题(){SubTopicId = 4,名称=新四} //必须更新
        }
    };    VAR objDet = ObjectiveDetails.FirstOrDefault(X => x.ObjectiveDetailId == web.ObjectiveDetailId);
    如果(objDet!= NULL)
    {
        //你可以使用AutoMapper或ValueInjecter映射和绑定相同的对象
        //但它超出了这个问题的范围
        //更新ObjectDetail
        objDet.Number = web.Number;
        objDet.Text = web.Text;
        变种subtops = objDet.SubTopics.ToList();        //删除从数据库中删除参数
        //实体框架可以通过更改跟踪为您处理
        // subtopicId = 2已经被删除
        subtops.RemoveAll(X =>!web.SubTopics.Select(Y => y.SubTopicId)。载有(x.SubTopicId));        //添加其中来自网络新项目
        //添加subtopicId = 3到列表
        VAR newItems = web.SubTopics.Where(X =>!subtops.Select(Y => y.SubTopicId)。载有(x.SubTopicId))。了ToList();
        subtops.AddRange(newItems);        //这个项目必须更新
        变种updatedItems = web.SubTopics.Except(newItems).ToList();        的foreach(在updatedItems VAR项)
        {
            VAR dbItem = subtops.First(X => x.SubTopicId == item.SubTopicId);
            dbItem.Name = item.Name;
        }        //让我们看到的是它的工作
        Console.WriteLine({0} \\ t {1} \\ t {2} \\ n ---------,objDet.ObjectiveDetailId,objDet.Number,objDet.Text);
        的foreach(在subtops VAR项)
        {
            Console.WriteLine({0}:{1},item.SubTopicId,item.Name);
        }
    }
    其他
    {
         //插入新ObjectiveDetail
    }    //在真实的情景所做的一切后,你需要调用的SaveChanges或者它在你的工作单位相等。
}

结果:

  1:1 datafromweb
---------
1:一
4:新的四
3:三

就是这样。您可以同步你这样的数据库和用户数据。也 AutoMapper ValueInjecter 都是非常有用和强大的工具,我深深建议你去看看那些。我希望你喜欢,快乐编码:)

I have two classes:

public partial class ObjectiveDetail {
    public ObjectiveDetail() {
        this.SubTopics = new List<SubTopic>();
    }
    public int ObjectiveDetailId { get; set; }
    public int Number { get; set; }
    public string Text { get; set; }
    public virtual ICollection<SubTopic> SubTopics { get; set; }
}
public partial class SubTopic {
    public int SubTopicId { get; set; }
    public string Name { get; set; }
}

I have an ObjectiveDetail object from the user:

var web = {
 "objectiveDetailId":1,
 "number":1,
 "text":"datafromweb",
 "subTopics":[
              {"subTopicId":1,
               "name":"one"
              },
              {"subTopicId":3,
               "name":"three",
              }
             ]
}

And an ObjectiveDetail from the database:

var db = {
 "objectiveDetailId":1,
 "number":1,
 "text":"datafromdb",
 "subTopics":[
              {"subTopicId":1,
               "name":"one"
              },
              {"subTopicId":2,
               "name":"two",
              }
             ]
}

With Entity Framework 6 I know I can update the text in the ObjectiveDetail class using:

_uow.ObjectiveDetails.Update(web));

But how can I update the references to ObjectiveDetail and SubTopics in the many to many table that joins these two table. Here for example I would want it so that for ObjectiveDetail 1 the many-many is changed to reference subTopicId 1 and 3 instead of the values 1 and 2. Note that ObjectiveDetail and SubTopic are stored in tables with another table between them. Here's the DDL:

CREATE TABLE [dbo].[ObjectiveDetail] (
    [ObjectiveDetailId] INT            IDENTITY (1, 1) NOT NULL,
    [Text]              NVARCHAR (MAX) NOT NULL,
    [ObjectiveTopicId]  INT            NULL,
    CONSTRAINT [PK_ObjectiveDetail] PRIMARY KEY CLUSTERED ([ObjectiveDetailId] ASC),
);

CREATE TABLE [dbo].[ObjectiveTopic] (
    [ObjectiveDetailId] INT NOT NULL,
    [SubTopicId]        INT NOT NULL,
    CONSTRAINT [FK_ObjectiveTopicObjectiveDetail] FOREIGN KEY ([ObjectiveDetailId]) REFERENCES [dbo].[ObjectiveDetail] ([ObjectiveDetailId]),
    CONSTRAINT [FK_ObjectiveTopicSubTopic] FOREIGN KEY ([SubTopicId]) REFERENCES [dbo].[SubTopic] ([SubTopicId])
);

CREATE TABLE [dbo].[SubTopic] (
    [SubTopicId] INT             IDENTITY (1, 1) NOT NULL,
    [Name]       NVARCHAR (150)  NOT NULL,
    CONSTRAINT [PK_SubTopic] PRIMARY KEY CLUSTERED ([SubTopicId] ASC),
);

Here's the EF Mapping that I have:

public class ObjectiveDetailMap : EntityTypeConfiguration<ObjectiveDetail>
{
    public ObjectiveDetailMap()
    {
        // Primary Key
        this.HasKey(t => t.ObjectiveDetailId);
        // Relationships
        this.HasMany(t => t.SubTopics)
           .WithMany(t => t.ObjectiveDetails)
           .Map(m =>
           {
               m.ToTable("ObjectiveTopic");
               m.MapLeftKey("ObjectiveDetailId");
               m.MapRightKey("SubTopicId");
           });

    }
}

解决方案

I think you are trying to simulating offline mode working for your users. So when you get something from your users, you want to sync database with user data. I make an example and take your question one step beyond :) I added a Subtopic which needs to be updated in database. Ok here is the code:

static void Main(string[] args)
{
    //the database
    var ObjectiveDetails = new List<ObjectiveDetail>()
    {
        new ObjectiveDetail()
        {
            ObjectiveDetailId = 1,
            Number = 1,
            Text = "datafromdb",
            SubTopics = new List<SubTopic>()
            {
                new SubTopic(){ SubTopicId = 1, Name="one"}, //no change
                new SubTopic(){ SubTopicId = 2, Name="two"}, //to be deleted
                new SubTopic(){ SubTopicId = 4, Name="four"} //to be updated
            }
        }
    };

    //the object comes as json and serialized to defined object.
    var web = new ObjectiveDetail()
    {
        ObjectiveDetailId = 1,
        Number = 1,
        Text = "datafromweb",
        SubTopics = new List<SubTopic>()
        {
            new SubTopic(){ SubTopicId = 1, Name="one"}, //no change
            new SubTopic(){ SubTopicId = 3, Name="three"}, //new row
            new SubTopic(){ SubTopicId = 4, Name="new four"} //must be updated
        }
    };

    var objDet = ObjectiveDetails.FirstOrDefault(x => x.ObjectiveDetailId == web.ObjectiveDetailId);
    if (objDet != null)
    {
        //you can use AutoMapper or ValueInjecter for mapping and binding same objects
        //but it is out of scope of this question
        //update ObjectDetail
        objDet.Number = web.Number;
        objDet.Text = web.Text;
        var subtops = objDet.SubTopics.ToList();

        //Delete removed parameters from database
        //Entity framework can handle it for you via change tracking
        //subtopicId = 2 has been deleted 
        subtops.RemoveAll(x => !web.SubTopics.Select(y => y.SubTopicId).Contains(x.SubTopicId));

        //adds new items which comes from web
        //adds subtopicId = 3 to the list
        var newItems = web.SubTopics.Where(x => !subtops.Select(y => y.SubTopicId).Contains(x.SubTopicId)).ToList();
        subtops.AddRange(newItems);

        //this items must be updated
        var updatedItems = web.SubTopics.Except(newItems).ToList();

        foreach (var item in updatedItems)
        {
            var dbItem = subtops.First(x => x.SubTopicId == item.SubTopicId);
            dbItem.Name = item.Name;
        }

        //let's see is it working
        Console.WriteLine("{0}:\t{1}\t{2}\n---------",objDet.ObjectiveDetailId, objDet.Number, objDet.Text);
        foreach (var item in subtops)
        {
            Console.WriteLine("{0}: {1}", item.SubTopicId, item.Name);
        }
    }
    else
    {
         //insert new ObjectiveDetail
    }

    //In real scenario after doing everything you need to call SaveChanges or it's equal in your Unit of Work.
}

The result:

1:      1       datafromweb
---------
1: one
4: new four
3: three

That's it. You can sync your database and user data like this. And also AutoMapper and ValueInjecter both are very useful and powerful tools, I deeply recommend you to take a look at those. I hope you enjoyed, happy coding :)

这篇关于如何使用EF6更新多对多表的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆