在请求期间访问/使用相同的对象 - asp.net

时间:2009-07-27 02:05:32

标签: asp.net request store

我有一个HttpModule,它在每个请求上创建一个CommunityPrincipal(实现IPrincipal接口)对象。我想以某种方式为每个请求存储对象,以便我可以在需要时随时获取它,而无需进行演员或再次创建。

基本上我想模仿FormsAuthenticationModule的工作方式。 它在每次请求时为HttpContext.User属性分配一个实现IPrincipal接口的对象。

我想以某种方式能够调用HttpContext.MySpecialUser(或MySpecialContext.MySpecialUser - 可以创建静态类),它将返回我的对象​​(特定类型)。

我可以使用扩展方法,但我不知道如何存储对象,以便在请求期间可以访问它。

如何实现这一目标?

请注意我想将其存储为特定类型(CommunityPrincipal - 不仅仅是作为对象)。 它当然应仅适用于正在处理的当前请求,而不能与所有其他线程/请求共享。

现在我将我的CommunityPrincipal对象分配给HttpModule中的HttpContext.User,但是每当我需要使用未在IPrincipal接口中定义的CommunityPrincipal对象的属性时,它需要我进行转换。

3 个答案:

答案 0 :(得分:1)

我建议您远离将数据耦合到线程本身。您无法控制asp.net现在或将来如何使用线程。

数据与请求上下文密切相关,因此应该与上下文一起定义,生存和死亡。这是放置它的正确位置,并且在HttpModule中实例化对象也是合适的。

演员阵容确实不应该是一个问题,但是如果你想摆脱它,我强烈建议为此使用HttpContext的扩展方法......这正是扩展方法的一种情况旨在处理。

以下是我如何实施它:

创建一个静态类来放置扩展方法:

public static class ContextExtensions
{
    public static CommunityPrinciple GetCommunityPrinciple(this HttpContext context)
    {
        if(HttpContext.Current.Items["CommunityPrinciple"] != null)
        {
            return HttpContext.Current.Items["CommunityPrinciple"] as CommunityPrinciple;
        }
    }
}

在你的HttpModule中,只需将主体放入上下文项集合中,如:

HttpContext.Current.Items.Add("CommunityPrincipal", MyCommunityPrincipal); 

这使常规上下文的用户属性保持在自然状态,这样第三方代码,框架代码和您编写的任何其他内容都不会受到您篡改正常IPrincipal的风险。该实例仅在用户请求有效期间存在。最重要的是,该方法可用于编码,就像它只是任何常规的HttpContext成员....并且不需要强制转换。

答案 1 :(得分:0)

由于您已经将对象存储在HttpContext.User属性中,所以您真正需要实现的目标是实现目标的静态方法: -

  public static class MySpecialContext
  {
    public static CommunityPrinciple Community
    {
        get
        {
           return (CommunityPrinciple)HttpContext.Current.User;
        }
    }
  }

现在您可以将CommunityPrinciple视为: -

  var x = MySpecialContext.Community;

然而,要避免似乎需要付出很多努力: -

  var x = (CommunityPrinciple)Context.User;

替代方案是HttpContext上的Extension方法: -

  public static class HttpContextExtensions
  {
    public static CommunityPrinciple GetCommunity(this HttpContext o)
    {
      return (CommunityPrinciple)o.User;
    }
  }

使用它: -

  var x = Context.GetCommunity();

这很整洁,但需要你记得在每个需要的文件的使用列表中包含扩展类定义的命名空间。

修改

让我们假设你有一些非常好的理由为什么即使在上面的被调用代码中执行的演员仍然是不可接受的(顺便说一句,我真的很想知道什么情况会导致你得出这个结论)。

另一个替代方案是ThreadStatic字段: -

  public class MyModule : IHttpModule
  {
    [ThreadStatic]
    private static CommunityPrinciple _threadCommunity;

    public static CommunityPrinciple Community
    {
        get
        {
           return _threadCommunity;
        }
    }
    // Place here your original module code but instead of (or as well as) assigning
    // the Context.User store in _threadCommunity.
    // Also at the appropriate point in the request lifecyle null the _threadCommunity

  }

用[ThreadStatic]修饰的字段每个线程将有一个存储实例。因此,多个线程可以修改和读取_threadCommunity,但每个线程都将在其特定的字段实例上运行。

答案 2 :(得分:0)

将自定义主体分配给Context.User是正确的。希望你在Application_AuthenticateRequest中做到这一点。

提出您的问题,您是否只从ASPX页面访问用户对象?如果是这样,您可以为您实现包含演员表的自定义基页。

public class CommunityBasePage : Page
{
    new CommunityPrincipal User
    {
        get { return base.User as CommunityPrincipal; }
    }
}

然后让您的网页继承自CommunityBasePage,您就可以从this.User访问所有属性。