ApplicationContext switching in ASP.NET / Background Threads

ApplicationContext switching in ASP.NET / Background Threads

Old forum URL: forums.lhotka.net/forums/t/10545.aspx


geordiepaul posted on Thursday, July 21, 2011

After upgrading to 4.1 we've stumbled upon a bit of an issue with ApplicationContext. 

Under normal ASP.NET circumstances ApplicationContext returns Csla.Web.ApplicationContextManager and everything works fine. However when we create a background worker thread, in the execution of that thread ApplicationContext.IsValid returns false and the ApplicationContext is switched Csla.ApplicationContextManager. This is never reverted back to Csla.Web.ApplicationContextManager for after that point since the value is cached in a static field, this result in all sorts of Identity issues as HTTPContext.Current.User is no long set as part of  setting Csla.ApplicationContext.User

I appreciate that background threads in ASP.NET aren't ideal however this is a trivial task of managing online users.

Am I missing something or this expected behaviour? 

Thanks

RockfordLhotka replied on Thursday, July 21, 2011

I have not put any effort into supporting the use of background threads in ASP.NET. So I suppose there is no "expected behavior" really.

However, you might try using Csla.Threading.BackgroundWorker to manage your background tasks. This type automatically copies the primary thread's identity information into the background thread.

Each thread (even outside ASP.NET) has its own identity and context - that information is always per-thread (except in WPF, where we explicitly manage the identity information differently). So it is always necessary to copy the primary thread's User property to any background thread - which is why we have our own BW component.

geordiepaul replied on Friday, July 22, 2011

Thanks for the reply.

The BackgroundWorker doesn't not work in this instance as this make a call to ApplicationContext.ContextManager on the new thread which switches the type of ContextManager used.

public static IContextManager ContextManager

 

    {
      get
      {
        if (_contextManager == null)
        {
          if (_webManagerType == null)
            _webManagerType = Type.GetType("Csla.Web.ApplicationContextManager, Csla.Web");
          if (_webManagerType != null)
            _contextManager = (IContextManager)Activator.CreateInstance(_webManagerType);
        }
        if (_contextManager == null || !_contextManager.IsValid)
          _contextManager = new ApplicationContextManager();
        return _contextManager;
      }
      set { _contextManager = value; }
    }

If the ContextManager isn't valid that the WebContextManager is swtiched for a ApplicationContextManager and is never switched back. In ASP.NET this caused all sorts of problem when trying to reset the Identity in global.asax. What would be nice is if the HTTPContext became valid it reverted back to the correct ContextManager. This was never a problem in 3.8 as both contexts were set if HTTPContext.Current wasn't null.

This method does seem a little illogical, as it caters for switching from Csla.Web.ApplicationContextManger to the normal ApplicationContextManger but not the other way round.

 

geordiepaul replied on Friday, July 22, 2011

I should extend on my post above to say that BackgroundWorker does work and does what it should. However when execution returns to the main thread ApplicationContext is of the wrong type as the BackgroundWorker in it's call to Csla.ApplicationContext switched it. Since the field _contextManager maintains a cache that variable is returned on the main thread.

 

geordiepaul replied on Friday, July 22, 2011

I've had a quick go at rewriting the ContextManager property to cater for situations like this. This does fix our problem however I'm not sure of other implications.

private static IContextManager _contextManager;
private static IContextManager _webContextManager;
private static Type _webManagerType;

/// <summary>
/// Gets or sets the context manager responsible
/// for storing user and context information for
/// the application.
/// </summary>
public static IContextManager ContextManager

    {

      get

      {

        if (_webContextManager == null)

        {

          if (_webManagerType == null)

            _webManagerType = Type.GetType("Csla.Web.ApplicationContextManager, Csla.Web");

          if (_webManagerType != null)

            _webContextManager = (IContextManager)Activator.CreateInstance(_webManagerType);

        }

        if (_webContextManager == null || !_webContextManager.IsValid)

        {

          if (_contextManager == null)

            _contextManager = new ApplicationContextManager();

          return _contextManager;

        }

        return _webContextManager;

      }

      set { _contextManager = value; }

    }

JonnyBee replied on Friday, July 22, 2011

Hi,

I see your problem now. This should be a better way then:

  public static class ApplicationContext
  {
    #region Context Manager
 
    private static IContextManager _webContextManager;
    private static IContextManager _contextManager;
    private static Type _webManagerType;
    private static Type _xamlManagerType;
 
 
    static ApplicationContext()
    {
      _webManagerType = Type.GetType("Csla.Web.ApplicationContextManager, Csla.Web");
      _xamlManagerType = Type.GetType("Csla.Xaml.ApplicationContextManager, Csla.Xaml");
 
      if (_webManagerType != null && _webContextManager == null)
        _webContextManager = (IContextManager)Activator.CreateInstance(_webManagerType);
      if (_xamlManagerType != null && _contextManager == null)
        _contextManager = (IContextManager)Activator.CreateInstance(_xamlManagerType);
      if (_contextManager == null)
        _contextManager = new ApplicationContextManager();
    }
 
 
    /// <summary>
    /// Gets the context manager responsible
    /// for storing user and context information for
    /// the application.
    /// </summary>
    public static IContextManager ContextManager
    {
      get
      {
        if (_webContextManager != null && _webContextManager.IsValid)
            return _webContextManager;
        return  _contextManager;
      }
    }
 
    #endregion

Copyright (c) Marimer LLC