简体   繁体   中英

What is the correct way to PUT a complex entity type in MVC Entity Framework

I am sending a json request to my ProductController through Angularjs $resource using method PUT. Product is recieved successfully. Problem is updating the complex properties of the product. Here is the product model.

 Product{
    //other properties
    public virtual List<Bookmark> Bookmarks { get; set; }
    public virtual List<ProductCounter> Counters { get; set; }
 }

Now here is my PUT method of the controller.

 public HttpResponseMessage PutProduct(int key, Product product)
    {
        if (!ModelState.IsValid)
        {
            return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState);
        }

        if (key != product.ID)
        {
            return Request.CreateResponse(HttpStatusCode.BadRequest);
        }

        product.ProductDocuments.Clear();
        var counters = new List<ProductCounter>(product.Counters);
        product.Counters.Clear();
        foreach (var counter in counters)
        {
            product.Counters.Add(counter);
            db.Entry(counter).State = EntityState.Added;

        }
        db.Entry(product).State = EntityState.Modified;

        try
        {
            db.SaveChanges();
        }
        catch (DbUpdateConcurrencyException ex)
        {
            return Request.CreateErrorResponse(HttpStatusCode.NotFound, ex);
        }

        return Request.CreateResponse(HttpStatusCode.OK);
    }

If I remove the foreach block, all counters are deleted.

If I add the foreach block, if product contains any counters they are all duplicated. 2 counters in product will become 4 and so on. What is the problem? even I am cloning the list of counters but dont know why clear without foreach works and with foreach cleared counters come back again. Even I tried setting ID to 0 of counters in foreach but that also duplicated the records.

I have tried this version of foreach after counters.clear();

  foreach (var counter in counters)
        {
            product.Counters.Add(new ProductCounter(){Name=counter.Name,Value=counter.Value});
        }

this gives this error in response :

""innererror":{ "message":"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.

"internalexception":{ "message":"Store update, insert, or delete statement affected an unexpected number of rows (0). Entities may have been modified or deleted since entities were loaded. Refresh ObjectStateManager entries.","type":"System.Data.Entity.Core.OptimisticConcurrencyException","stacktrace":" at System.Data.Entity.Core.Objects.ObjectContext.SaveChanges(SaveOptions options)\\r\\n at System.Data.Entity.Internal.InternalContext.SaveChanges()"

Here is the POST method, for more information. Counters are actually added anew to a product and other complex properties like Documents and ProjectTasks have relationship.

 // POST api/Product
    public HttpResponseMessage PostProduct(Product product)
    {
        if (ModelState.IsValid)
        {
            db.Products.Add(product);
            foreach (var projectTask in product.ProjectTasks)
            {
                db.ProjectTasks.Attach(projectTask);
            }
            foreach (var bookmark in product.Bookmarks)
            {
                db.Bookmarks.Attach(bookmark);
            }
            foreach (var document in product.ProductDocuments)
            {
                db.Documents.Attach(document);
            }
            //No attachment for counters, they are added as a list. if I remove all attachments for other entities, these entities get duplicated in their own tables.


            db.SaveChanges();

            HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.Created, product);
            //response.Headers.Location = new Uri(Url.Link("DefaultApi", new { key = product.ID }));
            return response;
        }
        else
        {
            return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState);
        }
    }

Question : I want to keep only coming counters and no more existing. Whether it is done through first clearing all and then adding new comers? or any other method?

在你的savechanges()之后你应该使用。这个对我有用

db.Entry(projectTask).State = EntityState.Unchanged;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM