使用静态工厂Func <T>为ASP.NET应用程序创建"Ambient Context"(UserContext)

Roo*_*ian 11 c# asp.net asp.net-mvc dependency-injection ambientcontext

我发现几乎每个类(控制器,视图,HTML帮助程序,服务等)都需要当前登录的用户数据.所以我想创建一个"Ambient Context"而不是直接注入IUserService或User.

我的方法看起来像那样.

public class Bootstrapper
{
    public void Boot()
    {
        var container = new Container();
        // the call to IUserService.GetUser is cached per Http request
        // by using a dynamic proxy caching mechanism, that also handles cases where we want to 
        // invalidate a cache within an Http request
        UserContext.ConfigureUser = container.GetInstance<IUserService>().GetUser;
    }
}

public interface IUserService
{
    User GetUser();
}

public class User
{
    string Name { get; set; }
}

public class UserContext : AbstractFactoryBase<User>
{
    public static Func<User> ConfigureUser = NotConfigured;

    public static User ActiveUser { get { return ConfigureUser(); } }
}

public class AbstractFactoryBase<T>
{
    protected static T NotConfigured()
    {
        throw new Exception(String.Format("{0} is not configured", typeof(T).Name));
    }
}
Run Code Online (Sandbox Code Playgroud)

用法示例:

public class Controller
{
     public ActionResult Index()
     {
         var activeUser = UserContext.ActiveUser;
         return View();
     }
}
Run Code Online (Sandbox Code Playgroud)

我的方法是正确的还是我错过了什么?你有更好的解决方案吗?

更新:

用户类的更多细节:

public class User
{
   string Name { get; set; }
   bool IsSuperUser { get; set;}
   IEnumerable<AzManOperation> Operations { get; set}
}
Run Code Online (Sandbox Code Playgroud)

控制器中,我们需要检查用户是否是SuperUser才能为SuperUser提供一些额外的功能.

public class BaseController : Controller
{
    private readonly IUserService _userService;

    BaseControler(IUserService userService)
    {
        _userService = userService
    }

    public User ActiveUser
    {
        get { return _userService.GetUser(); }
    }
}
Run Code Online (Sandbox Code Playgroud)

视图中,如果用户有权这样做,我们会检查操作以仅显示编辑或删除按钮.视图从不使用DependencyResolver,而是使用ViewBag或ViewModel.我的想法是实现自定义ViewBasePage并提供ActiveUser属性,以便Views可以轻松访问.

HtmlHelpers中,我们根据IsSuperUser和Operations渲染控件(传入User对象或使用DependencyResolver).

服务类中,我们也需要这些属性.例如,判断篮子是否有效(检查用户是否被允许购买不在标准列表中的文章).所以Service类依赖于IUserService并调用GetUser().

Action Filters中强制用户更改其密码(仅当它不是SuperUser并且User.ForcePasswordChange为true时).这里我们使用DependencyResolver.

我希望有一种更简单的方法来获取User对象,而不是使用DependencyResolver.Current.GetService().GetUser()或使用类似的东西ViewBag.ActiveUser = User.User对象是几乎到处检查权限等所需的对象.

Ste*_*ven 8

在视图中,如果用户有权这样做,我们会检查操作以仅显示编辑或删除按钮.

视图不应该执行此检查.Controller应该将视图模型返回到包含布尔属性的视图,该属性指出这些按钮是否应该可见.返回一个bool IsSuperUser已经移动到视图中的众所周知.视图不应该知道它应该为超级用户显示某个按钮:这取决于控制器.应该只告知视图要显示的内容.

如果几乎所有视图都有此代码,则有一些方法可以从视图中提取重复的部分,例如使用部分视图.如果您发现自己在许多视图模型上重复这些属性,也许您应该定义一个包络视图模型(包装特定模型的通用视图模型T).控制器可以创建其视图模型,同时创建服务或横切关注点,将其包装在信封中.

在服务类中,我们也需要这些属性.例如,判断篮子是否有效

在这种情况下,您谈论的是验证,这是一个跨领域的问题.您应该使用装饰器来添加此行为.