Расширение пользовательского mvc ModelBinder Сандерсона для объекта, сохраненного в сеансе

В своей замечательной книге MVC Стивен Сандерсон приводит пример связывателя пользовательской модели, который устанавливает и извлекает переменную сеанса, скрывая элемент хранения данных от контроллера.

Я пытаюсь расширить это, чтобы удовлетворить довольно распространенный сценарий: я сохраняю объект User в сеансе и делаю его доступным для каждого метода действия в качестве параметра. Класс Сандерсона работал нормально, когда данные о пользователе не менялись, но теперь мне нужно позволить пользователю редактировать свои данные и сохранить измененный объект обратно в сеанс.

Моя проблема в том, что я не могу понять, как отличить GET от POST, кроме проверки количества ключей в bindingContext.ValueProvider.Keys, и это кажется настолько неправильным, я уверен, что что-то неправильно понимаю.

Может кто-то указать мне верное направление? В основном все действия должны иметь доступ к текущему пользователю, а действие UpdateMyDetails должно обновить тот же объект, все при поддержке Session. Вот мой код...

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

1 ответ

Попробуйте наследовать от DefaultModelBinder вместо IModelBinder, затем вы можете вызвать base.BindModel, чтобы заполнить связывание Context.Model для mvc 1.0 или bindingContext.ModelMetadata.Model для mvc 2.0

Чтобы запустить привязку ConContext.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
}
Другие вопросы по тегам