In his wonderful MVC book Steven Sanderson gives an example of a custom model binder that sets and retrieves a session variable, hiding the data storage element from the controller.
I'm trying to extend this to cater for a pretty common scenario: I'm storing a User object in the session and making this available to every action method as a parameter. Sanderson's class worked ok when the User details weren't changing, but now i need to let the user edit their details and save the amended object back to the session.
My problem is that I can't work out how to distinguish a GET from a POST other than by checking the number of keys in bindingContext.ValueProvider.Keys, and this seems so wrong I'm sure I'm 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 that same object, all backed 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));
}
}