0

There are dozens of solutions how to map a model to a viewmodel in mvc asp.net, like automapper or valueinjecter. However, I just can't figure out how to map back. A simple example

I have a complex model like this one here

public class Address
{
    public int AddressID { get; set; }

    public string OwnerID { get; set; }
    public virtual ApplicationUser Owner { get; set; }

    public String Name { get; set; }

    public String Street { get; set; }

    public int ZipCode { get; set; }

    public String City { get; set; }

    public string Email { get; set; }

    // fancy business logic stuff like
    public Nullable<Guid> requestCode { get; set; }

    // or even this
    public string BestFriendID { get; set; }
    public virtual ApplicationUser BestFriend{ get; set; }

    public virtual ICollection<OtherModel> Mycollection { get; set; }

    // And some enums
    public MyEnum MyEnum { get; set; }
}

But then in one scenario, I just want to edit the name and the Email of this address, so I go ahead and create a viewmodel for it

public class SimpleEmailEditVM
{
      [MyDataAnnotations]
      public String Name { get; set; }

      [MyDataAnnotations] 
      public String Email { get; set; }
}

This allows to create very simple view's like this one

@model project.ViewModels.SimpleEmailEditVM
@Html.EditorFor(model => model, new { htmlAttributes = new { @class = "form-control" } })

and I can add in the viewmodel e.g. Street as a field and don't need to update the View. The mapping from the model to the viewmodel can be done easily with e.g. valueinjecter, but now my question: how do I map back?

In the viewmodel there is no Id information, so how do I find the correct model (I can imagine, multiple entries having the same name and email but totally different id's)? And how does the controller look like?

    [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult Edit([Bind] SimpleEmailEditVM vm)
    {
        if (ModelState.IsValid)
        {
            /* find the correct address from db and update the
               fields specified in the viewmodel? */
            db.Entry(address).State = EntityState.Modified;
            db.SaveChanges();
            return RedirectToAction("Index");
        }
        return View(address);
    }

One solution I can think of is adding the Id and making it hidden (like this here). Is this the correct and only way? The issue here is, that my id is passed and I can imagine this is not very good practice to pass internal id's to the users.

rst
  • 2,510
  • 4
  • 21
  • 47

2 Answers2

1

I can imagine this is not very good practice to pass internal id's to the users.

Exposing an id isn't a problem in itself unless that id is sensitive data like a SSN. The id for this question is exposed in the url. Your user id is exposed in your profile.

The trick is to make sure that when the id comes back you check its validity and check that the identity performing the action is allowed to perform that action. This could be a roles or permissions check or may need to be granular at the record level.

Here's some pseudocode demonstrating this.

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit([Bind] SimpleEmailEditVM vm)
{
    var itemToEdit = db.Find(vm.Id);

    //Make sure Id is valid
    if(itemToEdit == null) return BadRequest();

    //Make sure user can edit this object
    if(!User.IsInRole("CanEditItems")) return Forbidden();  
    if(itemToEdit.AllowedUser != User.Identity.Name) return Forbidden(); 

    if (ModelState.IsValid)
    {
        /* find the correct address from db and update the
           fields specified in the viewmodel? */
        db.Entry(address).State = EntityState.Modified;
        db.SaveChanges();
        return RedirectToAction("Index");
    }
    return View(address);
}
jamesSampica
  • 12,230
  • 3
  • 63
  • 85
0

You can use temproray session (TempData) for sending value to action from another action.

Please check enter link description here

pisi1001
  • 89
  • 1
  • 5