2

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));
    }  
}
4

1 回答 1

1

尝试从 DefaultModelBinder 而不是 IModelBinder 继承,然后您可以调用 base.BindModel 来填充 mvc 1.0 的 bindingContext.Model 或 mvc 2.0 的 bindingContext.ModelMetadata.Model

要触发 bindingContext.Model 填充,请在控制器上调用 UpdateModel。

您需要将书中的语句添加回

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

但将其更改为填充模型并保存在会话中。

if(bindingContext.Model != null)
{
    base.BindModel(controllerContext, bindingContext);
    //save bindingContext.Model to session, overwriting current.
    return bindingContext.Model
}
于 2010-03-03T19:06:49.610 回答