views:

56

answers:

2

Trying to get UpdateModel to work for my User. The User class has basic string properties like CompanyName, FirstName, LastName, etc so nothing exotic.

Here is the header for my view:

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Public.Master" Inherits="System.Web.Mvc.ViewPage<User>" %>

After they submit, in my controller, the code looks like this:

[HttpPost]
    public ActionResult Edit(string id, FormCollection collection)
    {
        try
        {
            User myUser = db.Get<IUserRepository>().Get(id);
            UpdateModel(myUser);
            db.Update(myUser);

            return RedirectToAction("Index", "Home");
        }
        catch
        {
            return View();
        }
    }

The values past into FormCollection have the values like:

[0] "FirstName" string
[1] "LastName" string
[2] "Email" string

Here is my UserModelBinder (took out some error checking code) which seems to be the source of the problem:

public class UserModelBinder : IModelBinder
{
    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        IPrincipal p = controllerContext.HttpContext.User;
        User u = db.Get(p.Identity.Name);
        return u;
    }
}

while the myUser I get from the database has all its original values, the UpdateModel for my controller never actually makes any changes. I have read problems people have with ViewModels and which prefix to use, but I am just passing in the regular database object.

The strange thing is that this User edit is for my "Public" area and I already have a User edit for the Admin area which lets the administrator change extra Properties. The "Admin" area User edit is working fine, but the "Public" area for User edit isn't even though the code is almost identical.

Update:

This turned out to be a custom ModelBinding problem and by changing my UserModelBinding to derive from DefaultModelBinder and adding into my BindModel method:

if (bindingContext.Model != null)
            return base.BindModel(controllerContext, bindingContext);

Everything seems to work.

+1  A: 

Try this instead

public ActionResult Edit(User thisUser)

Id will need to come from a hidden field maybe.

Also you will need to ensure your field names match the User property names.

You shouldn't need to do anything more as the object should have the values within it.

If this is not helpful then let me know and I'll remove this answer

edit

This is one of my update methods

    [HttpPost]
    public ActionResult EditCustomer(Customer customer)
    {
        //ensure that the model is valid and return the errors back to the view if not.
        if (!ModelState.IsValid)
            return View(customer);

        //locate the customer in the database and update the model with the views model.
        Customer thisCustomer = customerRepository.Single(x => x.CustomerID == customer.CustomerID);
        if (TryUpdateModel<Customer>(thisCustomer))
            customerRepository.SaveAll();
        else
            return View(customer);

        //return to the index page if complete
        return RedirectToAction("index");
    }

edit 2

my custom model binder

public class CustomContactUsBinder : DefaultModelBinder
    {
        protected override void OnModelUpdated(ControllerContext controllerContext, ModelBindingContext bindingContext)
        {
            ContactFormViewModel contactFormViewModel = bindingContext.Model as ContactFormViewModel;

            if (!String.IsNullOrEmpty(contactFormViewModel.Name))
                if (contactFormViewModel.Name.Length > 10)
                    bindingContext.ModelState.AddModelError("Name", "Name is too long.  Must be less than 10 characters.");

            base.OnModelUpdated(controllerContext, bindingContext);
        }
    }
griegs
Well, the string id comes from the URL in the edit:
enantiomer2000
I had previously set up a ModelBinding for User. Could that affect how User is passed in and how UpdateModel would affect it? I noticed that the User being passed in is definitely the one from the database (from the ModelBinder that I created).
enantiomer2000
griegs, your method seems to work, but only if I remove the custom UserModelBinder class from the ModelBinders list in the global.asax. I kind of liked being able to have my User instance automatically passed in, rather than an IPrincipal. Anybody have any ideas on how to make this work with my original way?
enantiomer2000
what does your custom binder do? I can post you an example of mine but doubt it would help you.
griegs
Posted the code now. basically just returns a User. My goal for the UserModelbinder was basically just to pass in User into various controller Actions instead of IPrincipal. I don't know why this is affecting the UpdateModel for my Controller.
enantiomer2000
does your custom model perform a base.OnModelUpdated(controller context, binding context)? Also how is it adding errors if it finds any?
griegs
@enantiomer2000 see my second edit re binders
griegs
it doesn't call base.OnModelUpdated and currently not much error checking in there at the moment.
enantiomer2000
Thanks griegs for the tip. I now check on whether the bindingContext.Model is null or not and if it isn't, I just call the base.BindModel. This seems to do the trick. Still not sure if this is best practices...
enantiomer2000
+1  A: 

This blog post sounds like it solves your exact problem:

UpdateModel(user, "User");

Since it seems the data you want to bind to is prefixed by the viewmodel name.

amurra
If it would be prefixed the FormCollection would contain Keys like User.FirstName and User.LastName
Henrik P. Hessel
Henrik is correct and I already tried this to no avail. I think it may have something to do with the custom ModelBinding that I created to easily pass into my controllers the User instance of the authenticated user. Investigating further...
enantiomer2000
Can you post your custom ModelBinding code?
amurra
It is done. My goal for the UserModelbinder was basically just to pass in User into various controller Actions instead of IPrincipal. I don't know why this is affecting the UpdateModel for my Controller.
enantiomer2000