user2695433
user2695433

Reputation: 2153

Attaching an entity of type 'XXX.EnquiryLineItem' failed because another entity of the same type already has the same primary key value

Please don't mark it as duplicate since i have already gone through similar questions here ,here and here but didn't solve the issue.

Full error message quoted ,

This can happen when using the 'Attach' method or setting the state of an entity to 'Unchanged' or 'Modified' if any entities in the graph have conflicting key values. This may be because some entities are new and have not yet received database-generated key values. In this case use the 'Add' method or the 'Added' entity state to track the graph and then set the state of non-new entities to 'Unchanged' or 'Modified' as appropriate.

I have 2 related tables Enquiry and EnquiryLineItem , adding/editing dynamically. The edit get method populates and displays all the values bound including the EnquiryLineItems . Using viewmodel and partial views for showing both the related tables . Error occurs when Edit post method.

Models:

public class Enquiry : BaseEntity
    {
        public Enquiry()
        {
            EnquiryLineItems = new List<EnquiryLineItem>();
        }
        public int ID { get; set; }
        public DateTime? PreparedDate { get; set; }
        [StringLength(12)]
        public string EnquiryNumber { get; set; }
        public DateTime? ClosingDate { get; set; }
        public int DivisionID { get; set; }
        public Division Division { get; set; }
        public int ClientID { get; set; }
        public Client Client { get; set; }

        public virtual ICollection< EnquiryLineItem> EnquiryLineItems { get; set; }
        public DateTime? RFQSentDate { get; set; }
    }
public class EnquiryLineItem : BaseEntity
    {
        public int ID { get; set; }
        [StringLength(80)]
        public string ItemDesc { get; set; }
        [Range(1, int.MaxValue, ErrorMessage = "Please enter valid Quantity")]
        public int Quantity { get; set; }
        public int EnquiryID { get; set; }
        public Enquiry Enquiry { get; set; }
        public int? ManufacturerID { get; set; }
        public Manufacturer Manufacturer { get; set; }
    }

Controller method :

 [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Edit( EnquiryVM enquiryVM)
        {
            var ID = enquiryVM.ID;
            var enquiry = new Enquiry();
            enquiry.EnquiryNumber = enquiryVM.EnquiryNumber;
            enquiry.ClosingDate = enquiryVM.ClosingDate;
            enquiry.RFQSentDate = enquiryVM.RFQSentDate;
            enquiry.ClientID = enquiryVM.ClientID;
            enquiry.DivisionID = enquiryVM.DivisionID;

            if (ModelState.IsValid)
            {
                foreach (var lineItemVm in enquiryVM.LineItems)
                {

                        EnquiryLineItem enquiryLineItem = new EnquiryLineItem();
                        enquiryLineItem.ItemDesc = lineItemVm.ItemDesc;
                        enquiryLineItem.Quantity = lineItemVm.Quantity;
                        enquiryLineItem.ManufacturerID = lineItemVm.ManufacturerId;
                        enquiry.EnquiryLineItems.Add(enquiryLineItem);

                }

                db.Entry(enquiry).State = EntityState.Modified;

                db.SaveChanges();


                return RedirectToAction("Index");
            }
          //ConfigureViewModel();
            return View(enquiryVM);
        }

Context :

 public override int SaveChanges()
        {
            foreach (var entry in ChangeTracker.Entries())
            {
                var entity = entry.Entity;
                if (entry.State == EntityState.Added)
                {
                    entry.Property("AddedDate").CurrentValue = DateTime.Now;

                    entry.Property("AddedBy").CurrentValue = HttpContext.Current.User.Identity.Name;
                    entry.Property("AddedById").CurrentValue = HttpContext.Current.User.Identity.GetUserId();

                }
                else if (entry.State == EntityState.Modified)
                {
                    entry.Property("AddedDate").IsModified = false;
                    entry.Property("AddedBy").IsModified = false;
                    entry.Property("AddedById").IsModified = false;
                    entry.Property("ModifiedDate").CurrentValue = DateTime.Now;
                    entry.Property("ModifiedBy").CurrentValue = HttpContext.Current.User.Identity.Name;
                    entry.Property("ModifiedById").CurrentValue = HttpContext.Current.User.Identity.GetUserId();

                }
            }
            return base.SaveChanges();
        }

In Edit view there is a partial view for adding/editing/deleting LineItems in the Enquiry View itself. So the EnquiryModel Editing will have LineItems which can be edited too or even added/deleted .

Note : If I add db.Set<Enquiry>().AddOrUpdate(enquiry);then its adding Enquiry and LineItems as new insert instead of edit.

using MVC 5 and EF6 Codefirst approach. Appreciate some help on this. Thanks in advance.

Upvotes: 0

Views: 1995

Answers (1)

user3559349
user3559349

Reputation:

Your creating a new Enquiry data model an not setting its ID property or attaching it to the context. You should be getting the existing data model based on the ID of the view model, and then updating its properties and then saving.

Change your controller code to

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit( EnquiryVM enquiryVM)
{
    if(!ModelState.IsValid)
    {
        ConfigureViewModel(enquiryVM);
        return View(enquiryVM)
    }
    // Get the data model
    var enquiry = db.Enquiries.FirstOrDefault(x => x.ID == enquiryVM.ID.Value);
    // update properties of enquiry from the enquiryVM
    enquiry.EnquiryNumber = enquiryVM.EnquiryNumber; 
    enquiry.ClosingDate = enquiryVM.ClosingDate; 
    ....
    foreach (var lineItemVm in enquiryVM.LineItems)
    {
        if (lineItemVm.ID.HasValue)
        {
            var lineItem = enquiry.LineItems.FirstOrDefault(x => x.ID == lineItemVm.ID.Value);
            // update properties of lineItem from lineItemVm (but dont add it to the collection)
            lineItem.ItemDesc = lineItemVm.ItemDesc;
            lineItem.Quantity = lineItemVm.Quantity;
            lineItem.ManufacturerID = lineItemVm.ManufacturerId;
        }
        else
        {
            // create a new EnquiryLineItem data model based on lineItemVm and add it to the data models `LineItems` collection
            EnquiryLineItem enquiryLineItem = new EnquiryLineItem();
            enquiryLineItem.EnquiryID = enquiryVM.ID.Value; // modify this
            enquiryLineItem.ItemDesc = lineItemVm.ItemDesc;
            enquiryLineItem.Quantity = lineItemVm.Quantity;
            enquiryLineItem.ManufacturerID = lineItemVm.ManufacturerId;
            enquiry.EnquiryLineItems.Add(enquiryLineItem);
        }
        db.Entry(enquiry).State = EntityState.Modified;
        db.SaveChanges();
        return RedirectToAction("Index");
    }
}

Upvotes: 1

Related Questions