How to update / create many-to-many relational data in MVC code using EF first?

I looked through StackOverflow, Google, and asp.net, trying to find a clear cut, a basic example of how to do this. All examples were abstract or related to complications that did not apply. I could not extract from them a lot of useful things. So far, not one of them has fully answered my question or touched upon my problems.

I am working on an MVC project with the following model:

Article.cs:

public class Article { public int ArticleId { get; set; } public string Title { get; set; } . . . public virtual ICollection<Category> Categories { get; set; } public Article() { Categories = new HashSet<Category>(); } } 

Category.cs:

 public class Category { public int CategoryId { get; set; } public string Name { get; set; } public virtual ICollection<Article> Articles { get; set; } public Category() { Articles = new HashSet<Article>(); } } 

ArticleEntities.cs:

 public class ArticleEntities : DbContext { public DbSet<Article> Articles { get; set; } public DbSet<Category> Categories { get; set; } } 

An article can have many categories, and a category can belong to many articles.

So far I can save / update / create all fields of the article, except for categories.

I present them as flags in the view. I can get the values ​​for the selected checkboxes in the controller, but every attempt I made to store them in db with the article failed.

Like me:

1) When saving an edited article, update the existing relationship in the relationship table without creating duplicates?

2) When saving a new article, create the selected relationship in the relationship table?

+6
source share
1 answer

I assume that you will get a CategoryId from a post post action, a List<int> or more general only IEnumerable<int> .

1) When saving an edited article, update the existing relationships in the relationship table without creating duplicates?

 Article article; // from post action parameters IEnumerable<int> categoryIds; // from post action parameters using (var ctx = new MyDbContext()) { // Load original article from DB including its current categories var articleInDb = ctx.Articles.Include(a => a.Categories) .Single(a => a.ArticleId == article.ArticleId); // Update scalar properties of the article ctx.Entry(articleInDb).CurrentValues.SetValues(article); // Remove categories that are not in the id list anymore foreach (var categoryInDb in articleInDb.Categories.ToList()) { if (!categoryIds.Contains(categoryInDb.CategoryId)) articleInDb.Categories.Remove(categoryInDb); } // Add categories that are not in the DB list but in id list foreach (var categoryId in categoryIds) { if (!articleInDb.Categories.Any(c => c.CategoryId == categoryId)) { var category = new Category { CategoryId = categoryId }; ctx.Categories.Attach(category); // this avoids duplicate categories articleInDb.Categories.Add(category); } } ctx.SaveChanges(); } 

Note that the code also works if you have an ArticleViewModel instead of Article , given that the property names are the same ( SetValues accepts an arbitrary object ).

2) When saving a new article, create the selected relationship in relation to the table?

More or less the same idea as above, but simpler, because you do not need to compare with the initial state in the database:

 Article article; // from post action parameters IEnumerable<int> categoryIds; // from post action parameters using (var ctx = new MyDbContext()) { foreach (var categoryId in categoryIds) { var category = new Category { CategoryId = categoryId }; ctx.Categories.Attach(category); // this avoids duplicate categories article.Categories.Add(category); // I assume here that article.Categories was empty before } ctx.Articles.Add(article); ctx.SaveChanges(); } 
+9
source

Source: https://habr.com/ru/post/919984/


All Articles