Sanderson custom mvc model extension for object saved in session

In his excellent MVC book, Stephen Sanderson provides an example of a custom binder that sets and retrieves a session variable while hiding a data storage member from the controller.

I'm trying to expand on this to suit a fairly common scenario: I store a User object in the session and make it available to every action method as a parameter. The Sanderson class worked fine when the user's data did not change, but now I need to let the user edit their data and save the changed object back to the session.

My problem is that I cannot figure out how to distinguish GET from POST other than checking the number of keys in bindContext.ValueProvider.Keys and this seems so wrong that I am sure I am misunderstanding something.

Can anyone point me in the right direction? Basically all actions need access to the current user, and the UpdateMyDetails action needs to update the same object, all backed up by the session. Here's my code ...

public class CurrentUserModelBinder : IModelBinder
{
    private const string userSessionKey = "_currentuser";
    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) {
        var user = controllerContext.HttpContext.Session[userSessionKey];
        if (user == null)
            throw new NullReferenceException("The CurrentUser was requested from the CurrentUserModelBinder but no IUser was present in the Session.");

        var currentUser = (CCL.IUser)user;
        if (bindingContext.ValueProvider.Keys.Count > 3)
        {

            var firstName = GetValue<string>(bindingContext, "FirstName");
            if (string.IsNullOrEmpty(firstName))
                bindingContext.ModelState.AddModelError("FirstName", "Please tell us your first name.");
            else
                currentUser.FirstName = firstName;

            var lastName = GetValue<string>(bindingContext, "LastName");
            if (string.IsNullOrEmpty(lastName))
                bindingContext.ModelState.AddModelError("LastName", "Please tell us your last name.");
            else
                currentUser.LastName = lastName;

            if (bindingContext.ModelState.IsValid)
                controllerContext.HttpContext.Session[userSessionKey] = currentUser;

        }
        return currentUser;
    }
    private T GetValue<T>(ModelBindingContext bindingContext, string key)
    {
        ValueProviderResult valueResult;
        bindingContext.ValueProvider.TryGetValue(key, out valueResult);
        bindingContext.ModelState.SetModelValue(key, valueResult);
        return (T)valueResult.ConvertTo(typeof(T));
    }  
}

      

+2


source to share


1 answer


Try to inherit from DefaultModelBinder instead of IModelBinder then you can call base.BindModel to populate bindingContext.Model for mvc 1.0 or bindingContext.ModelMetadata.Model for mvc 2.0

To call bindContext.Model to populate, call UpdateModel on the controller.

You need to add the expression from the book back in



if(bindingContext.Model != null)
throw new InvalidOperationException("Cannot update instances");

      

but change it to fill the model and keep it in session.

if(bindingContext.Model != null)
{
    base.BindModel(controllerContext, bindingContext);
    //save bindingContext.Model to session, overwriting current.
    return bindingContext.Model
}

      

+1


source







All Articles