MVC Check model values in form using TryUpdateModel in Controller

1.5k Views Asked by At

I am currently following a tutorial on MVC 5, and I am stuck on how to perform validation checks on fields entered by the User without binding my fields in the Edit Method.

I have a Edit page already in place, but this binds all fields at the start - the tutorial which I'm following makes it clear that I should not do this.

My Original code is the following. Since I bind all of my fields at the start, I can perform checks by using vans.fieldname as shown below with 'if (vans.AssetID == 20)'

    [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult Edit([Bind(Include = "AssetID,Batch_Number,Customer_Account_Holder,Dealer_ID, ")] Vans__ vans)
    {
        if (vans.AssetID == 20) //Example - check if data entered for AssetID is 20
        {
          //do something
        }
        if (ModelState.IsValid)
        {
            db.Entry(vans__).State = EntityState.Modified;
            db.SaveChanges();
            return RedirectToAction("Index");
        }
        return View(vans__);
    }

In the tutorial that I'm following, I'm instructed not to bind everything at the start, but instead use code like the following:

    public ActionResult EditPost(int? id)
    {
        if (id == null)
        {
            return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
        }

        //In here vans is the original values and not from the form.
        Vans__ vans = db.Vans__.Find(id);

       //Here how can I check if AssetID is a certain number returned from the form?

        if (TryUpdateModel(vans, "", new string[] { "AssetID" }))
        {
            try
            {
                db.SaveChanges();
                return RedirectToAction("Index");
            }
        }
    }

I understand the white-listing with the TryUpdateModel will be much easier and safer, I am just struggling to get to grips with how I get access to the data returned to the controller from the form.

How can I access the model in order for me to add my own validation Checks?

All help is appreciated, thanks.

1

There are 1 best solutions below

2
Andy Donegan On

This is not an answer.

The Microsoft tutorial is here (Disclaimer I work with James and asked him to learn by posting the question).

And write up from it below relating to why we should not be using the original BIND method which James has linked.

[HttpPost, ActionName("Edit")]
[ValidateAntiForgeryToken]
public ActionResult EditPost(int? id)
{
  if (id == null)
  {
    return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
  }
  var studentToUpdate = db.Students.Find(id);
  if (TryUpdateModel(studentToUpdate, "",
   new string[] { "LastName", "FirstMidName", "EnrollmentDate" }))
  {
    try
    {
        db.SaveChanges();

        return RedirectToAction("Index");
    }
    catch (DataException /* dex */)
    {
        //Log the error (uncomment dex variable name and add a line here to write a log.
        ModelState.AddModelError("", "Unable to save changes. Try again, and      if the problem persists, see your system administrator.");
    }
  }
  return View(studentToUpdate);
}

These changes implement a security best practice to prevent overposting, The scaffolder generated a Bind attribute and added the entity created by the model binder to the entity set with a Modified flag. That code is no longer recommended because the Bind attribute clears out any pre-existing data in fields not listed in the Include parameter. In the future, the MVC controller scaffolder will be updated so that it doesn't generate Bind attributes for Edit methods.

The new code reads the existing entity and calls TryUpdateModel to update fields from user input in the posted form data. The Entity Framework's automatic change tracking sets the Modified flag on the entity. When the SaveChanges method is called, the Modified flag causes the Entity Framework to create SQL statements to update the database row. Concurrency conflicts are ignored, and all columns of the database row are updated, including those that the user didn't change. (A later tutorial shows how to handle concurrency conflicts, and if you only want individual fields to be updated in the database, you can set the entity to Unchanged and set individual fields to Modified.)

As a best practice to prevent overposting, the fields that you want to be updateable by the Edit page are whitelisted in the TryUpdateModel parameters. Currently there are no extra fields that you're protecting, but listing the fields that you want the model binder to bind ensures that if you add fields to the data model in the future, they're automatically protected until you explicitly add them here.

As a result of these changes, the method signature of the HttpPost Edit method is the same as the HttpGet edit method; therefore you've renamed the method EditPost.