从 中使用有状态数据的最佳实践HttpContext
是创建您自己的应用程序特定上下文,该上下文在构造函数中接受一个HttpContext
(依赖注入)。
你的业务逻辑永远不应该依赖于一个HttpContext
新的应用程序特定的上下文(可能是使用来自的信息创建的HttpContext
)。
这不仅可以解决您的上述问题,还可以增加代码的可测试性。
例子:
public class MyApplicationContext
{
public IPrincipal ContextPrincipal { get; set; }
public MyApplicationContext(HttpContext httpContext)
{
// Store the current user principal & identity
ContextPrincipal = httpContext.User;
// Need to grab anything else from the HttpContext? Do it here!
// That could be cookies, Http request header values, query string
// parameters, session state variables, etc.
//
// Once you gather up any other stateful data, store it here in
// your application context object as the HttpRequest can't be passed
// to another thread.
}
}
public class MyHttpHandler : IHttpHandler
{
#region IHttpHandler Members
public bool IsReusable
{
// Return false in case your Managed Handler cannot be reused for another request.
// Usually this would be false in case you have some state information preserved per request.
get { return true; }
}
public void ProcessRequest(HttpContext context)
{
// Do some work on another thread using the ThreadPool
ThreadPool.QueueUserWorkItem(new WaitCallback(DoWork), new MyApplicationContext(context));
}
public void DoWork(object state)
{
// Grab our state info which should be an instance of an
// MyApplicationContext.
MyApplicationContext context = (MyApplicationContext) state;
// Assign this ThreadPool thread's current principal according
// to our passed in application context.
Thread.CurrentPrincipal = context.ContextPrincipal;
// Check if this user is authenticated.
if (context.ContextPrincipal.Identity.IsAuthenticated)
{
var userName = context.ContextPrincipal.Identity.Name;
}
// Check if this user is an administrator.
if (context.ContextPrincipal.IsInRole("Administrator"))
{
}
// Do some long-ish process that we need to do on the threadpool
// after the HttpRequest has already been responded to earlier.
//
// This would normally be some fancy calculation/math, data
// operation or file routines.
for (int i = 0; i < 30; i++)
{
Thread.Sleep(1000);
}
}
#endregion
}
和接口都IPrincipal
没有IIdentity
明确提供 dispose 方法。所以他们都应该可以保留对他们的引用。但是,我没有测试过上面的代码,我只是为了这个问题而写的。
如果通过一些糟糕的设计,它们实际上确实依赖于底层数据库连接来查询角色成员资格,那么您只需在应用程序上下文的构造函数中更早地评估它,而 HttpContext 和 asp.net 表单身份验证提供程序仍然未处理/关闭。
您总是可以拆开主体和身份并重新创建一个新实例,GenericPrincipal
甚至GenericIdentity
创建您的应用程序身份类来实现IIdentity
. 这里有很多定制/扩展的空间。