[英]How can I use EF6 to update a many to many table
我有兩節課:
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; }
}
我有一個來自用戶的ObjectiveDetail對象:
var web = {
"objectiveDetailId":1,
"number":1,
"text":"datafromweb",
"subTopics":[
{"subTopicId":1,
"name":"one"
},
{"subTopicId":3,
"name":"three",
}
]
}
和數據庫中的ObjectiveDetail:
var db = {
"objectiveDetailId":1,
"number":1,
"text":"datafromdb",
"subTopics":[
{"subTopicId":1,
"name":"one"
},
{"subTopicId":2,
"name":"two",
}
]
}
使用Entity Framework 6,我知道我可以使用以下命令更新ObjectiveDetail類中的文本:
_uow.ObjectiveDetails.Update(web));
但是如何在連接這兩個表的多對多表中更新對ObjectiveDetail和SubTopics的引用。 這里例如我想要它,以便對於ObjectiveDetail 1,將many-many更改為引用subTopicId 1和3而不是值1和2.請注意,ObjectiveDetail和SubTopic存儲在表中,並且它們之間有另一個表。 這是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),
);
這是我的EF Mapping:
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");
});
}
}
我認為您正在嘗試模擬為您的用戶工作的離線模式。 因此,當您從用戶那里獲得某些內容時,您希望將數據庫與用戶數據同步。 我做了一個例子,把你的問題提高了一步:)我添加了一個需要在數據庫中更新的Subtopic。 好的,這里是代碼:
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.
}
結果:
1: 1 datafromweb
---------
1: one
4: new four
3: three
而已。 您可以像這樣同步數據庫和用戶數據。 而AutoMapper和ValueInjecter都是非常有用和強大的工具,我深深建議你看看那些。 我希望你喜歡,快樂編碼:)
這是一個方法,它接受目標ObjectiveDetail
的ID和要添加到目標ObjectiveDetail
的SubTopic
ID的IEnumerable<int>
。
public void UpdateSubTopics( int objectiveDetailId, IEnumerable<int> newSubTopicIds )
{
using( var db = new YourDbContext() )
{
// load SubTopics to add from DB
var subTopicsToAdd = db.SubTopics
.Where( st => newSubTopicIds.Contains( st.SubTopicId ) );
// load target ObjectiveDetail from DB
var targetObjDetail = db.ObjectiveDetail.Find( objectiveDetailId );
// should check for targetObjDetail == null here
// remove currently referenced SubTopics not found in subTopicsToAdd
foreach( var cst in targetObjDetail.SubTopics.Except( subTopicsToAdd ) )
{
cst.SubTopics.Remove( cst );
}
// add subTopicsToAdd not currently found in referenced SubTopics
foreach( var nst in subTopicsToAdd.Except( targetObjDetail.SubTopics ) )
{
targetObjDetail.SubTopics.Add( nst );
}
// save changes
db.SaveChanges();
}
}
我只使用EF代碼優先,並定義3個表,你要么定義所有3個表,要么只是定義2個表,每個表中都有一個集合,就像這樣
public class ObjectiveDetail
{
public ObjectiveDetail() {
this.SubTopics = new HashSet<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 SubTopic() {
this.ObjectiveDetail = new HashSet<ObjectiveDetail>();
}
public int SubTopicId { get; set; }
public string Name { get; set; }
public virtual ICollection<ObjectiveDetail> ObjectiveDetails { get; set; }
}
如果您有3個表,則可以輕松地使用新的ID更新中間表。 您必須獲取要更新的所有ObjectiveTopics,並更改ID,然后執行更新
ObjectiveTopic objectiveTopic = _uow.ObjectiveTopic.Get(1);
ObjectiveTopic.SubTopicId = 2;
ObjectiveTopic.ObjectiveDetailId = 1;
_uow.ObjectiveTopic.Update(objectiveTopic);
如果您沒有將第三個表定義為實體,並且您只能訪問ObjectiveDetail和SubTopic表,那么您可以獲取兩個實體並刪除不再需要的實體,並添加一個你要。
ObjectiveDetail objectiveD = _uow.ObjectiveDetail.Get(1);
SubTopic subTopic = _uow.SubTopic.Get(1); //SubTopic to remove
SubTopic topicToAdd = _uow.SubTopic.Get(2); //SubTopic to add
ObjectiveDetail.SubTopics.Remove(subTopic); //Remove the entity from the ObjectiveTopic table
ObjectiveDetail.SubTopics.Add(topicToAdd); //Add the new entity, will create a new row in ObjectiveTopic Table
_uow.ObjectiveDetail.Update(objectiveD);
如果你想(也許應該),你可以在objectiveD上使用linq從集合中獲取實體,而不是從數據庫中檢索它。
SubTopic subTopic = objectiveD.SubTopics.Single(x => x.SubTopicId == 1); //Instead of _uow.SubTopic.Get(1);
...
您可以使用通用方法,以便它可以用於任何多對多關系。 您只需在主對象的多對多集合中提供此整數列表,表示要更新的字段的ID:
protected void UpdateManyToMany<T>(YourDBContext db, ICollection<T> collection, List<int> idList) where T : class
{
//update a many to many collection given a list of key IDs
collection.Clear();
var source = db.Set<T>();
if (idList != null)
{
foreach (int i in idList)
{
var record = source.Find(i);
collection.Add(record);
}
}
}
你會這樣稱呼它:
UpdateManyToMany(db, objectiveDetail.SubTopics, subTopicIDList);
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.