我应该在ASP.NET MVC3中附加一个自定义的用户上下文会话包装器?

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (2)
  • 关注 (0)
  • 查看 (18)

我已经阅读了MVC中会话范围数据的许多帖子,但我仍然不清楚在解决方案中包含自定义会话包装器的正确位置。

我想从IPrincipal获取当前用户的用户名,加载关于该用户的附加信息并将其存储在Session中。然后,我想从Controller和View中访问该用户数据。

以下任何一种方法都不符合我想要做的。

选项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:将会话注入到类构造器 论坛帖子中

这个选项看起来不错,但我仍然不确定如何将它附加到控制器和视图。我可以在Controller中新建一个,但不应该将它作为依赖注入吗?

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:使用Brad Wilson的StatefulStorage类

Brad Wilson 在他的演讲中展示了他的StatefulStorage课程。它是一组聪明而有用的类,包含接口和使用构造函数注入。不过,它似乎让我的选择与选项2一样。它使用接口,但我无法使用Container注入它,因为它依赖于静态工厂。即使我可以注入它,它是如何传递给视图的。每个ViewModel都必须拥有一个具有setable User属性的基类吗?

选项4:使用与Hanselman IPrincipal ModelBinder类似的东西

我可以将User作为参数添加到Action方法中,并使用ModelBinder从Session中提取水分。这看起来像是在需要的地方添加它的很多开销。另外,我仍然必须将其添加到ViewModel以使其可用于视图。

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

我觉得我正在推翻这一点,但似乎应该有一个明显的地方来做这种事情。我错过了什么?

提问于
用户回答回答于

我的会议方法:

封面与界面的会议:

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

Ninject依赖关系:

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

ViewData当想在母版页中使用并在特定视图中使用视图模型时,可以使用一些常用信息。

用户回答回答于

我强烈建议通过控制器在视图中传递任何你需要的东西。这样,关于视图应该呈现哪些数据的决定留在控制器中。为了尽可能简单,创建一个ViewModelWithUserBase具有可设置User属性的抽象类真的不是一个坏主意。一个选项是创建一个接口IViewModelWithUser,并且User每次都重新实现该属性(或者与基类结合使用,但是如果在某些特定情况下使事情变得更容易,您可以选择重新实现而不是继承基类) 。

至于填充这个属性,它可能很容易通过一个动作过滤器来完成。利用该OnActionExecuted方法,可以测试传递给视图的模型是否实现了您的基类(或接口),然后IPrincipal在适当的情况下使用正确的对象填充该属性。这样做的好处是,由于在单元测试中不执行动作过滤器,因此可以在动作过滤器中使用HttpContext.Current.Session选项1中的相关代码,并且在控制器上仍然具有可测试接口。

扫码关注云+社区