我正在尝试在EntityFramework CodeFirst中编辑数据透视表的内容。
我正在使用.NET Framework 4.6.1和EntityFramework 6.1.3。
我的模型设置如下:
Threads
正如您所看到的,public class Post
{
/// <summary>
/// The post's id in the database
/// </summary>
public int Id { get; set; }
/// <summary>
/// The the categories the post belongs to
/// </summary>
[InverseProperty("Posts")]
public virtual ICollection<Category> Categories { get; set; }
/// <summary>
/// The title of the post
/// </summary>
[Required]
public string Title { get; set; }
/// <summary>
/// The content of the post
/// </summary>
[Required]
public string Content { get; set; }
/// <summary>
/// The moment the post was made
/// </summary>
public DateTime? TimeStamp { get; set; }
}
public class Category
{
/// <summary>
/// The id of the category in the database
/// </summary>
public int Id { get; set; }
/// <summary>
/// The posts that belong to this category
/// </summary>
[InverseProperty("Categories")]
public virtual ICollection<Post> Posts { get; set; }
/// <summary>
/// The name of the category
/// </summary>
[Required]
public string Name { get; set; }
}
可以属于多个Post
,Category
可以有多个Category
当我尝试更新帖子并将其更改为Post
时,会出现问题。
控制器:
Category
问题:
一切正常:我可以更新[HttpPut]
[ResponseType(typeof(void))]
public IHttpActionResult PutPost(int id, Post post)
{
if (id != post.Id)
{
return BadRequest();
}
var entry = db.Entry(post);
entry.State = EntityState.Modified;
// Created at must not be updated.
entry.Property(x => x.TimeStamp).IsModified = false;
// Also attatch categories
foreach (var category in post.Categories) // For the example, I assume all user input is correct.
{
db.Categories.Attatch(category); // This is not working
}
try
{
db.SaveChanges();
}
catch (DbUpdateConcurrencyException)
{
if (!PostExists(id))
{
return NotFound();
}
else
{
throw;
}
}
return StatusCode(HttpStatusCode.NoContent);
}
的{{1}}和Title
没问题。但是Content
未在数据库中更新。
我见过人们通过首先从数据库中获取实体并使用Post
然后编辑该对象内的所有内容来更新多对多关系。但是,这需要从数据库中加载大量数据,这些数据在之后被否定。这对我来说似乎是不好的做法。
问题: 如何正确更新EntityFramework中的多对多关系,而无需从数据库中加载不必要的数据?
答案 0 :(得分:1)
如果我理解你的意图,你应该在你的DBContext中尝试这样的事情。它将创建一个名为PostsCategories的第三个表,您将在其中拥有Category和Post之间的关系。
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Category>()
.HasMany(t => t.Posts)
.WithMany(t => t.Categories)
.Map(m =>
{
m.ToTable("PostsCategories");
m.MapLeftKey("CategoryId");
m.MapRightKey("PostId");
});
}
编辑:
所以试试这个: 在DbContext中:
public void UpdateManyToMany<TSingle, TMany>(
TSingle localItem,
Func<TSingle, ICollection<TMany>> collectionSelector,
int[] collectionIds)
where TSingle : class
where TMany : class
{
if (collectionIds == null)
collectionIds = new int[0];
DbSet<TMany> manyItemDbSet = Set(typeof(TMany)).Cast<TMany>();
ICollection<TMany> dbColl = collectionSelector(localItem);
dbColl.Clear();
foreach (var keyValue in collectionIds)
{
var entity = manyItemDbSet.Find(keyValue);
dbColl.Add(entity);
}
}
然后在你的控制器
db.UpdateManyToMany(post, e => e.Categories, post.MultipleCategories);
其中post.MultipleCategories是类别的ID,因此您可能必须在Post模型中添加一些字段。
[NotMapped]
public int[] MultipleCategories{ get; set; }
答案 1 :(得分:1)
不确定你会得到你想要的东西。在任何情况下,您都必须将实体图加载到实体框架中,以便它可以跟踪更改。
如您的示例中所述,Attach(顺便拼写错误)只会将对象添加到Entity Framework的更改跟踪器中,默认状态为Unchanged。
您需要告诉Entity Framework该记录已更改,以便EF更新记录。
如果你知道所提供的类别已经存在(这就是我所说的那样,假设所有用户输入都是正确的&#34;)那么你可以使用以下内容:< / p>
foreach (var category in post.Categories)
{
// This will attach the entity and set the entity state to modified
// Note this will throw an exception if the underlying entity doesn't
// exist when the "SaveChanges" method is called.
context.Entry(category).State = EntityState.Modified;
}
我假设你要实施某种检查和平衡以确保在上述代码之前存在记录,但是如果你不想在这里遇到麻烦,那么应该是一个片段为你工作。一个&#34; Else&#34;可以添加块以添加类别,如果它也不存在。
foreach (var category in post.Categories)
{
var existingRecord = context.Categories.SingleOrDefault(x => x.ID == category.Id);
if (existingRecord != null)
{
context.Entry(existingRecord).CurrentValues.SetValues(category)
}
}