Расширение пользовательского mvc ModelBinder Сандерсона для объекта, хранящегося в сеансе - PullRequest
2 голосов
/ 26 августа 2009

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

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

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

Кто-нибудь может указать мне правильное направление? По сути, всем действиям нужен доступ к текущему пользователю, а действию 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 Ответ

1 голос
/ 03 марта 2010

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

Чтобы запустить привязку для заполнения.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
}
...