3

I've been looking around and can't quite find the answer. I'm using a ViewModel in my Edit View so that I can have values for some dropdownlist. Now when I go to update my DB I'm not understanding how I can update my database record. I'm guessing I could create a new entity object, do a Find, and then update each property based on the ViewModel passed in from the Form but that sure seems like a lot of manual work.

Here I'm using the VeiwModel in the Edit View.

@model CPPCustomerCall.ViewModels.CustomerCallVM

Here is my controller's ActionResult. I changed the object type of the ActionResult to take in CustomerCallVM instead of the CustomerCall which was auto-generated. I assume since the Edit View's model is the ViewModel that's the type of object the ActionResult will receive. However, my ViewModel has more properties that aren't needed for the Entity Model to update the record. How do I go about updating my DB record in this ActionResult?

[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Edit([Bind(Include = "Id,CustomerName,Subject,PhoneNumber,CallMessage,CallDate,Status,CallNotes")] CustomerCallVM customerCall)
{
    if (ModelState.IsValid)
    {
        db.Entry(customerCall).State = EntityState.Modified;
        await db.SaveChangesAsync();
        return RedirectToAction("Index");
    }
    return View(customerCall);
}

2 Answers 2

5

First, Bind and view models are mutually exclusive. If you don't want something to be eligible to be modified, then it shouldn't be on your view model, in the first place. Barring that, view models diverge from entities in the sense that they can't be saved directly. As a result, there's always some intervention present on your part to map the posted values back onto the entity, which means you can then selectively not map over certain properties that shouldn't be, regardless of whether they were posted or not. Long and short, get rid of the Bind stuff. It's just something else to maintain and a huge source of potential bugs.

That said, the code you have is workable; you're just missing the crucial part where you map the data from your view model back onto your entity. First, you need to fetch the entity from the database so you have a base to work from:

var customerCall = db.CustomerCalls.Find(id);
if (customerCall == null)
{
    return new HttpNotFoundResult();
}

FWIW, your edit route should include the id in the route, according to REST conventions. Following REST isn't strictly required, but it's certainly recommended. While a web application adhering to REST doesn't mean it's a good application, not adhering to rest is generally a sure sign of a badly designed and coded application.

Then, you map over your properties. You can either do this manually:

customerCall.CustomerName = model.CustomerName;
// etc.

Or you can use a library like AutoMapper.

mapper.Map(model, customerCall);

AutoMapper requires a bit of initial setup to make this magic work, of course, so review the docs, if you're going that route. Manual mapping is easier, but far more tedious and repetitive.

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks. The Bind stuff was auto generated and I remember reading some tutorials from Channel9 I believe where they suggested using it to keep from over posting. Either way, I have no issue removing because it does seem like extra work that at least for this scenario is probably overkill. As for the other code, it sounds like I'll need to create a new entity object and map the properties myself from the ViewModel passed in. I figured that was one way to handle it but just seemed like a potential for extra code. I looked a AutoMapper once but have also read to stay away from it.
Be careful with what you read. There's a lot of bad information out there. AutoMapper is just fine, if you use it properly. The problem comes as with anything, when you start to over-rely on it. The Bind stuff is Microsoft's sorry attempt to cover over sorry design. The truth is an entity is not a model, and it makes no sense to use the same class to post data as you use to persist data. For years, Microsoft kept push that bad practice, with Bind quick-fix way to gloss over a huge security issue.
0
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Edit([Bind(Include = "Id,CustomerName,Subject,PhoneNumber,CallMessage,CallDate,Status,CallNotes")] CustomerCallVM customerCall)
{
    if (ModelState.IsValid)
    {
       // Find The record you need
       var dbObj = CustomerCalls.FirstOrDefault(x=> x.id = customerCall.id);
       //Check if null
       if(dbObj == null) dbObj = new CustomerCall();

       /// Map your properties

       // Add object to the stack
       if(dbObj.id == 0){
         CustomerCalls.Add(dbObj);
       }else{
         CustomerCalls.Update(dbObj);
       }

    await db.SaveChangesAsync();
    return RedirectToAction("Index");
    }
    return View(customerCall);
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.