Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Entity Framework 4 Not Saving My Many-To-Many Rows

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.

like image 978
Gary Avatar asked Mar 01 '12 16:03

Gary


People also ask

How do I save changes in Entity Framework?

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.

How do I save a list of objects in Entity Framework?

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).

How do you save a record in Entity Framework?

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.


1 Answers

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.

like image 190
Slauma Avatar answered Sep 17 '22 13:09

Slauma