Куда я должен прикрепить пользовательскую оболочку Session-контекста в ASP.NET MVC3? - PullRequest
16 голосов
/ 21 февраля 2011

Я прочитал много постов о данных в формате Session в MVC, но мне все еще неясно, где находится правильное место для включения пользовательской оболочки Session в решение.

Я хочу получить имя пользователя текущего пользователя из IPrincipal, загрузить дополнительную информацию об этом пользователе и сохранить ее в сеансе.Затем я хочу получить доступ к этим пользовательским данным из контроллера и представления .

Ни один из следующих подходов не соответствует тому, что я хочу сделать.

Вариант 1: прямой доступ к коллекции сеансов

Кажется, все согласны это плохая идея , но, честно говоря, это кажется самой простой вещью, которая работает.Однако это не делает пользователя доступным для представления.

public class ControllerBase : Controller {
   public ControllerBase() : this(new UserRepository()) {}
   public ControllerBase(IUserRepository userRepository) {
      _userRepository = userRepository;
   }
   protected IUserRepository _userRepository = null;
   protected const string _userSessionKey = "ControllerBase_UserSessionKey";
   protected User {
      get { 
         var user = HttpContext.Current.Session[_userSessionKey] as User;
         if (user == null) {
            var principal = this.HttpContext.User;
            if (principal != null) {
               user = _userRepository.LoadByName(principal.Identity.Name);
               HttpContext.Current.Session[_userSessionKey] = user;
            }
         }
         return user;
      }
   }
}

Вариант 2: внедрение сеанса в конструктор класса сообщение на форуме

Эта опция кажется довольно хорошей, но я все еще не уверен, как прикрепить ее к контроллеру и представлению.Я мог бы добавить его в контроллер, но разве его не следует вводить как зависимость?

public class UserContext {
   public UserContext() 
       : this(new HttpSessionStateWrapper(HttpContext.Current.Session), 
              new UserRepository()) { } 

   public UserContext(HttpSessionStateBase sessionWrapper, IUserRepository userRepository) { 
      Session = sessionWrapper;
      UserRepository = userRepository; 
   } 

   private HttpSessionStateBase Session { get; set; }
   private IUserRepository UserRepository{ get; set; }

   public User Current { 
      get {
         //see same code as option one
      }
   }
}

Вариант 3: Использовать класс StatefulStorage Брэда Уилсона

В своей презентации Брэд Уилсон представляет свой класс StatefulStorage.Это умный и полезный набор классов, который включает в себя интерфейсы и использует внедрение конструктора.Тем не менее, кажется, что он ведет меня по тому же пути, что и вариант 2. Он использует интерфейсы, но я не смог использовать контейнер для его внедрения, потому что он опирается на статическую фабрику.Даже если бы я мог внедрить это, как это передано Представлению.Должен ли каждый ViewModel иметь базовый класс с настраиваемым свойством User?

Вариант 4: использовать что-то похожее на Hanselman IPrincipal ModelBinder

Я мог бы добавить пользователя в качестве параметра к методу Action и использовать ModelBinder для его гидратации из сеанса.Кажется, что это требует много времени, чтобы добавить его везде, где это необходимо.Кроме того, мне все равно пришлось бы добавить его в ViewModel, чтобы сделать его доступным для View.

public ActionResult Edit(int id, 
   [ModelBinder(typeof(IPrincipalModelBinder))] IPrincipal user)
{ ... }

Мне кажется, что я обдумываю это, но, похоже, для этого должно быть очевидное место.Такие вещи.Чего мне не хватает?

Ответы [ 2 ]

13 голосов
/ 21 февраля 2011

Мой подход к сеансу:

Сессия обложки с интерфейсом:

public interface ISessionWrapper
{
    int SomeInteger { get; set; }
}

Реализация интерфейса с использованием HttpContext.Current.Session:

public class HttpContextSessionWrapper : ISessionWrapper
{
    private T GetFromSession<T>(string key)
    {
        return (T) HttpContext.Current.Session[key];
    }

    private void SetInSession(string key, object value)
    {
        HttpContext.Current.Session[key] = value;
    }

    public int SomeInteger
    {
        get { return GetFromSession<int>("SomeInteger"); }
        set { SetInSession("SomeInteger", value); }
    }
}

Ввод в контроллер:

public class BaseController : Controller
{
    public ISessionWrapper SessionWrapper { get; set; }

    public BaseController(ISessionWrapper sessionWrapper)
    {
        SessionWrapper = sessionWrapper;
    }
}

Зависимая зависимость:

Bind<ISessionWrapper>().To<HttpContextSessionWrapper>()

Вы можете передавать некоторую часто используемую информацию, используя ViewData, когда хотите использовать ее на главной странице и используя модель представления в определенных видах.

3 голосов
/ 21 февраля 2011

Я настоятельно рекомендую передавать через контроллер все, что вам нужно, в виде вниз.Таким образом, решение о том, какие именно данные * должны отображать в представлении, остается за контроллером.Чтобы сделать это как можно проще, создание абстрактного класса ViewModelWithUserBase, который имеет настраиваемое свойство User, на самом деле не является плохой идеей.Один из вариантов - создать интерфейс IViewModelWithUser и каждый раз заново реализовывать свойство User (или объединять с базовым классом, но у вас будет возможность повторно реализовать, а не наследовать базовый класс, если это что-то меняет).проще в некоторых случаях).

Что касается заполнения этого свойства, то, вероятно, это можно легко сделать с помощью фильтра действий.Используя метод OnActionExecuted, вы можете проверить, реализует ли модель, переданная в представление, ваш базовый класс (или интерфейс), а затем заполнить свойство правильным объектом IPrincipal, если это необходимо.Это дает преимущество в том, что поскольку фильтры действий не выполняются в модульных тестах, вы можете использовать зависимый код HttpContext.Current.Session из вашего варианта 1 в вашем фильтре действий и по-прежнему иметь тестируемый интерфейс на контроллере.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...