Hopefully a very simple question. But I'm using Code First with an MVC app and I have a Category and ServiceType object that have a many to many relationship:
public class Category
{
public Category()
{
ServiceTypes = new HashSet<ServiceType>();
}
public Guid CategoryId { get; set; }
[Required(ErrorMessage = "Name is required")]
public string Name { get; set; }
public virtual ICollection<ServiceType> ServiceTypes { get; set; }
}
The database has been generated correctly and contains a linking table called CategoryServiceTypes. My issue is I add items to my ServiceTypes collection and call save and although no error takes place, no rows are added to CategoryServiceTypes. When the below code gets to SaveChanges the count on category.ServiceTypes is 1, so something is definitely in the collection:
[HttpPost]
public ActionResult Edit(Category category, Guid[] serviceTypeIds)
{
if (ModelState.IsValid)
{
// Clear existing ServiceTypes
category.ServiceTypes.Clear();
// Add checked ServiceTypes
foreach (Guid serviceType in serviceTypeIds)
{
ServiceType st = db.ServiceTypes.Find(serviceType);
category.ServiceTypes.Add(st);
}
db.Entry(category).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(category);
}
I hope I'm doing something obviously wrong here. Any ideas?
Thanks.
EDIT:
Although the below response is indeed the correct answer I thought I'd add the following final version of the Edit post method:
[HttpPost]
public ActionResult Edit(Category category, Guid[] serviceTypeIds)
{
if (ModelState.IsValid)
{
// Must set to modified or adding child records does not set to modified
db.Entry(category).State = EntityState.Modified;
// Force loading of ServiceTypes collection due to lazy loading
db.Entry(category).Collection(st => st.ServiceTypes).Load();
// Clear existing ServiceTypes
category.ServiceTypes.Clear();
// Set checked ServiceTypes
if (serviceTypeIds != null)
{
foreach (Guid serviceType in serviceTypeIds)
{
ServiceType st = db.ServiceTypes.Find(serviceType);
category.ServiceTypes.Add(st);
}
}
db.SaveChanges();
return RedirectToAction("Index");
}
return View(category);
}
Notice the line that forces the loading of the ServiceTypes collection, this is needed as lazy loading does not include those child items, which means clearing the ServiceTypes collection did nothing.
Entity Framework Core Save Changes to the database using the SaveChanges method of DbContext. When we use the SaveChanges it prepares the corresponding insert , update , delete queries. It then wraps them in a Transaction and sends them to the database. If any of the queries fails all the statements are rolled back.
You can use Entity Framework's . AddRange method to add a collection of objects to your Db. List<T> implements IEnumerable MSDN - msdn.microsoft.com/en-us/library/6sh2ey19(v=vs. 110).
Insert Data The DbSet. Add and DbContext. Add methods add a new entity to a context (instance of DbContext) which will insert a new record in the database when you call the SaveChanges() method. In the above example, context.
Try to move the line where you attach the category to the context in front of the loop:
[HttpPost]
public ActionResult Edit(Category category, Guid[] serviceTypeIds)
{
if (ModelState.IsValid)
{
// Clear existing ServiceTypes
category.ServiceTypes.Clear();
db.Entry(category).State = EntityState.Modified;
// category attached now, state Modified
// Add checked ServiceTypes
foreach (Guid serviceType in serviceTypeIds)
{
ServiceType st = db.ServiceTypes.Find(serviceType);
// st attached now, state Unchanged
category.ServiceTypes.Add(st);
// EF will detect this as a change of category and create SQL
// to add rows to the link table when you call SaveChanges
}
db.SaveChanges();
return RedirectToAction("Index");
}
return View(category);
}
In your code EF doesn't notice that you have added the servicetypes because you attach the category to the context when the servicetypes are already in the category.ServiceTypes
collection and all servicetypes are already attached to the context in state Unchanged
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With