Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Entity Framework 5.0 Code First Many to Many Update

I have a Many to Many relationship between Apple and Orange tables.

public class Apple
{
    [Key]
    public int AppleID { get; set; }

    [Required]
    public string AppleName { get; set; }

    [NotMapped]
    public int[] SelectedOranges { get; set; }

    public virtual ICollection<Orange> Oranges { get; set; }

}

public class Orange
{

    [Key]
    public int OrangeID { get; set; }
    public String OrangeName { get; set; }
    public virtual ICollection<Apple> Apples { get; set; }
}

In the Data Context class, OnModelCreating method is overridden like below

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);

        //----------------------------------------------------------------
        //Creating a Association (Intermediate Table) which
        //will hold M2M relations from Student to Course.
        //----------------------------------------------------------------

        modelBuilder.Entity<Apple>()
            .HasMany(c => c.Oranges)
            .WithMany(t => t.Apples);
    }

When I save the edited values from controller in my ASP.NET MVC application, I get the following exception:

An error occurred while saving entities that do not expose foreign key properties for their relationships. The EntityEntries property will return null because a single entity cannot be identified as the source of the exception. Handling of exceptions while saving can be made easier by exposing foreign key properties in your entity types. See the InnerException for details.

Inner exception is:

Violation of PRIMARY KEY constraint 'PK_dbo.AppleOranges'. Cannot insert duplicate key in object 'dbo.AppleOranges'. The duplicate key value is (1, 3).\r\nThe statement has been terminated.

Any idea?

AppleController:

    //
    // GET: /Apple/Edit/5

    public ActionResult Edit(int id = 0)
    {

        Apple Apple = db.Apples.Find(id);
        if (Apple == null)
        {
            return HttpNotFound();
        }
        var selectedvalues = new List<Orange>();
        Apple.SelectedOranges = new int[Apple.Oranges.Count];
        int j=0;
        foreach (var i in Apple.Oranges)
        {
            selectedvalues.Add(i);
            Apple.SelectedOranges[j++] = db.Oranges.ToList().IndexOf(i)+1;
        }
        ViewBag.OrangesList = new MultiSelectList(db.Oranges, "OrangeID", "OrangeName", selectedvalues);
        return View(Apple);
    }

    //
    // POST: /Apple/Edit/5

    [HttpPost]
    public ActionResult Edit(Apple Apple)
    {
        if (ModelState.IsValid)
        {
            db.Entry(Apple).State = EntityState.Modified;
            foreach (var i in Apple.SelectedOranges)
            {
                if (Apple.Oranges == null)
                    Apple.Oranges = new List<Orange>();
                Apple.Oranges.Add(db.Oranges.Find(i));
            }
            db.SaveChanges();
            return RedirectToAction("Index");
        }
        ViewBag.OrangesList = new MultiSelectList(db.Oranges, "OrangeID", "OrangeName", Apple.SelectedOranges);
        return View(Apple);
    }

Apple Edit View:

@using (Html.BeginForm()) {
@Html.ValidationSummary(true)

<fieldset>
    <legend>Apple</legend>

    @Html.HiddenFor(model => model.AppleID)

    <div class="editor-label">
        @Html.LabelFor(model => model.AppleName)
    </div>
    <div class="editor-field">
        @Html.EditorFor(model => model.AppleName)
        @Html.ValidationMessageFor(model => model.AppleName)
    </div>


    <div class="editor-label">
        @Html.LabelFor(model => model.Oranges)
    </div>
    <div class="editor-field">
        @Html.ListBoxFor(m=>m.SelectedOranges,(MultiSelectList)ViewBag.OrangesList)
        @Html.ValidationMessageFor(model => model.Oranges)
    </div>

    <p>
        <input type="submit" value="Save" />
    </p>
</fieldset>

}

like image 648
Srinivas Avatar asked May 03 '26 11:05

Srinivas


1 Answers

You must load Apple.Oranges first, before adding items to it. If the collection is loaded, EF's change tracker will know which associations are new and which already exist and only the new associations will be saved.

You can load the collection by

db.Entry(Apple).Collection(a => a.Oranges).Load()

and you can remove the code

if (Apple.Oranges == null)
    Apple.Oranges = new List<Orange>();
like image 174
Gert Arnold Avatar answered May 06 '26 03:05

Gert Arnold



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!