C# 创建一个;“环境语境”;(UserContext)用于使用静态工厂函数的ASP.NET应用程序<;T>;
我发现几乎每个类(控制器、视图、HTML助手、服务等)都需要当前登录的用户数据。因此,我考虑创建一个“环境上下文”,而不是直接注入IUserService或用户 我的方法是这样的C# 创建一个;“环境语境”;(UserContext)用于使用静态工厂函数的ASP.NET应用程序<;T>;,c#,asp.net,asp.net-mvc,dependency-injection,ambientcontext,C#,Asp.net,Asp.net Mvc,Dependency Injection,Ambientcontext,我发现几乎每个类(控制器、视图、HTML助手、服务等)都需要当前登录的用户数据。因此,我考虑创建一个“环境上下文”,而不是直接注入IUserService或用户 我的方法是这样的 public class Bootstrapper { public void Boot() { var container = new Container(); // the call to IUserService.GetUser is cached per Http
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));
}
}
我的方法是正确的还是遗漏了什么?你有更好的解决方案吗
更新:
用户类的更多详细信息:
public class User
{
string Name { get; set; }
bool IsSuperUser { get; set;}
IEnumerable<AzManOperation> Operations { get; set}
}
在视图中我们检查操作,仅当用户有权这样做时才显示编辑或删除按钮。视图从不使用DependencyResolver,而是使用ViewBag或ViewModel。我的想法是实现一个定制的ViewBasePage并提供一个ActiveUser属性,这样视图就可以轻松访问
在HtmlHelpers中,我们根据发布者和操作(传入用户对象或使用DependencyResolver)呈现控件
在服务类中我们也需要这些属性。例如,决定篮子是否有效(检查用户是否允许购买标准列表中不包含的物品)。因此,服务类依赖于IUserService
和调用GetUser()
在操作过滤器中强制用户更改其密码(仅当它不是超级用户且user.ForcePasswordChange为true时)。这里我们使用DependencyResolver
我希望有一种更容易获取用户对象的方法,而不是使用DependencyResolver.Current.GetService().GetUser()或使用类似于ViewBag.ActiveUser=User
的东西。
用户对象几乎是检查权限等所需的所有位置的对象
在视图中,我们检查操作以仅显示编辑或删除按钮(如果用户有权这样做)
视图不应执行此检查。控制器应将视图模型返回到包含布尔属性的视图,该布尔属性说明这些按钮是否应可见。返回带有issupurer
的bool已经移动到视图中的更多knownledge。视图不应该知道它应该为超级用户显示某个按钮:这取决于控制器。只应告知视图要显示的内容
如果几乎所有视图都有此代码,那么有一些方法可以从视图中提取重复的部分,例如局部视图。如果您发现自己在许多视图模型上重复这些属性,也许您应该定义一个封套视图模型(一个将特定模型包装为T
的通用视图模型)。控制器可以创建其视图模型,而您可以创建将其封装在信封中的服务或横切关注点
在服务类中,我们也需要这些属性。例如,决定篮子是否有效
在本例中,您讨论的是验证,这是一个贯穿各领域的问题。您应该使用decorators来添加此行为。我认为最简单和可维护的解决方案是创建一个静态类CurrentUserProvider,它只有一个方法Get(HttpContextBase)返回当前用户,在后台您可以使用DependencyResolver来获取实际返回用户的服务。然后,在需要CurrentUser的地方,可以调用CurrentUserProvider.Get(上下文)并执行任何需要执行的自定义逻辑 您正在尝试的另一个解决方案是在基本控制器构造函数中注入服务,如果您有少量控制器,这是可以的。如果您有相当多的控制器,并且不是所有的控制器都需要该服务,这将成为一个问题。为这些控制器编写测试将是一件非常麻烦的事情,因为您必须为所有控制器测试为该服务创建存根/模拟。也许您可以使用属性注入而不是构造函数来解决它 您也可以对过滤器使用相同的属性注入 现在,剩下的两个是视图和辅助对象。对于视图,您可以创建从WebViewPage/ViewPage继承的特殊基类,并使用IVieActivator注入服务,这同样适用于帮助程序,创建从系统帮助程序继承的帮助程序,并在基本控制器和视图中使用这些帮助程序 我认为第二种方法有点麻烦,而且做所有这些定制的事情并没有增加多少价值 所以我的建议是第一个。这是MVC,对吗 你在重新发明轮子 将此方法添加到Global.asax.cs:
protected void Application_AuthenticateRequest(Object sender, EventArgs e)
{
var authCookie = Request.Cookies[FormsAuthentication.FormsCookieName];
if (authCookie != null)
{
var ticket = FormsAuthentication.Decrypt(authCookie.Value);
var user = ticket.Name;
var identity = new GenericIdentity(user, "Forms");
var principal = new GenericPrincipal(identity, null);
Context.User = principal;
}
}
此示例显示了表单身份验证,如果您使用另一种机制,则可以删除这些表单身份验证。关键是这三条线:
var identity = new GenericIdentity(user, "Forms");
var principal = new GenericPrincipal(identity, null);
Context.User = principal;
GenericEntity和GenericPrincipal可以替换为您想要的任何东西,只要它们实现(琐碎的)IIdentity和IPrincipal接口。您可以使用所需的任何额外属性创建这些类的自己实现
然后,您可以通过HttpContext.Current.user(静态)从列出的所有内容(控制器、视图等)访问经过身份验证的用户
如果您创建了自己的IPrincipal实现,则可以将该引用强制转换为自定义类型
您会注意到IPrincipal有一个名为IsInRole的方法,因此您会说:
if (HttpContext.Current.User.IsInRole("SuperUser"))
TL;DR-你在过度设计ASP.NET已经解决的问题,如果我在生产应用程序中看到你建议的类型,我会有动脉瘤。在使用环境上下文之前,我会查看所有的替代方案。看这本好书。这本书我已经拥有了。正如我所提到的,我几乎在应用程序中的任何地方都需要用户,因此我认为可以开始使用环境上下文,而不是使用构造函数注入或服务位置
var identity = new GenericIdentity(user, "Forms");
var principal = new GenericPrincipal(identity, null);
Context.User = principal;
if (HttpContext.Current.User.IsInRole("SuperUser"))