Issues with ApplicationContext.ContextManager when calling DataPortal memebers in a Thread created from ASP.Net web app

Issues with ApplicationContext.ContextManager when calling DataPortal memebers in a Thread created from ASP.Net web app

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


uma posted on Tuesday, July 20, 2010

 We are experimenting CSLA 4.0 RC0. In our web application (ASP.Net 4.0),on click of a button we create a Thread to do some lengthy calculations in the background .

  In the thread we found that all DataPortal operations are failing because GetPrincipal() is trying to retreive HttpContext.Current.User instead of Thread.CurrentPrincipal. On further debugging we found that ApplicationContext.ContextManger gets an instance of Csla.Web.ApplicationContextManager instead of Csla.ApplicationContextManager as the thread is created from ASPNet.

 As a work-around , we set ApplicationContext.ContextManger=null while creating every business object to force CSLA to reload ApplicationContext.ContextManger. Not sure whether this is a good idea.Any suggestions?


 

 


Code Skeleton

 protected void button_Click(object sender, ImageClickEventArgs e)
    {
       
                     Thread thread = new Thread(new ParameterizedThreadStart(StartTask));
                    thread.Start();
 
    }
 private static void StartTask(object param)
  {
     // When you check at this point ApplicationContext.ContextManger it is

   // Csla.Web.ApplicationContextManager instead of csla.ApplicationContextManager
  }

RockfordLhotka replied on Tuesday, July 20, 2010

That's interesting. The reason there's a web-specific ApplicationContext is because ASP.NET doesn't reliably set Thread.CurrentPrincipal. The only safe way to get the principal in ASP.NET is via HttpContext. Similarly, storing context on TLS isn't safe, again HttpContext is the only safe location.

Now if you are creating your own threads, then you manage their lifetimes, so the rules are different - but the ASP.NET managed threads aren't safe.

The scenario where failure occurs is an edge case, having to do with early parts of the page lifecycle processing. And when I dug into this it was 4-5 years ago, so I don't remember the specifics. But it is a very real scenario, and at the time I made the change it was in response to someone hitting the problem (of course).

Back to your threads though - if you are explicitly creating them, then you control their lifetime, so using TLS should be fine - which means the non-web ApplicationContext should be fine.

uma replied on Wednesday, July 21, 2010

We can safely use the Csla.ApplicationContextManager . But switching between Csla.Web.ApplicationContextManager  and Csla.ApplicationManager is where it gets tricky.

Now we have to do something like the following in our application to force the contextManager property to load everytime before DP operations.

  public  class MyBusinessBase<T> : Csla.BusinessBase<T> where T: MyBusinessBase<T>
{
    protected MyBusinessBase()
        {
            ApplicationContext.ContextManager = null;
           
        }
}


IMHO, it would have been better if CSLA did the following


 public static IContextManager ContextManager
    {
      get
      {
          if (_contextManager == null || !_contextManager.IsValid)  // No point in using a invalid contextManager
        {
          var webManagerType = Type.GetType("Csla.Web.ApplicationContextManager, Csla.Web");
          if (webManagerType != null)
          {
            _contextManager = (IContextManager)Activator.CreateInstance(webManagerType);
            if (!_contextManager.IsValid) _contextManager = null;
          }
          if (_contextManager == null)
            _contextManager = new ApplicationContextManager();
        }
        return _contextManager;
      }
      set { _contextManager = value; }
    }


And in Csla.ApplicationContextManager change the IsVsalid property to check whether it is ASPNET worker thread or non-ASPNET worker thread.

RockfordLhotka replied on Wednesday, July 21, 2010

Well it is actually a little more complex than that if you get right down to it.

I suspect what most people would actually expect to happen, is that some things (most notably GlobalContext and User) would automatically flow into the background thread.

If all CSLA does is make sure there's a valid (but empty) ApplicationContext on the thread, that still won't really solve the problem, and we'd be having a discussion about why CSLA doesn't solve the actual problem Smile

The reason is simple: most experts recommend against spinning up threads on the web server. It generally harms scalability and isn't reliable for long-running tasks (since things get real messy if the AppDomain is recycled, and that happens quite a lot on most web sites).

So enabling something most experts recommend against has never been high on my priority list.

All that said, Jonny has a prototype of a BackgroundWorker implementation that would automatically move ClientContext, GlobalContext and User to the background thread (from the thread pool of course - this is a BackgroundWorker). I don't think any of us considered it for use on a web server, so it may not fully address your issue - it is designed for smart client scenarios like WPF and Silverlight where background tasks are increasingly common.

Unfortunately this didn't make it into CSLA 4 due to time constraints, but it is something that I'm sure will find its way into a point release in the relatively near future.

I'm pretty sure Jonny is on holiday at the moment though, otherwise I'm sure he'd share his thoughts as well.

uma replied on Wednesday, July 21, 2010

Rocky, Thanks for your reply. I am hopping to see Johnny's input in this.

For now, can we safely do the following ?

 public  class MyBusinessBase<T> : Csla.BusinessBase<T> where T: MyBusinessBase<T>
{
    protected MyBusinessBase()
        {
            ApplicationContext.ContextManager = null;
           
        }
}

RockfordLhotka replied on Wednesday, July 21, 2010

I don't know if you can safely do that or not. You'll have to test and see if it works reliably. I don't have time to do any real research into this at the moment, sorry.

RockfordLhotka replied on Monday, July 26, 2010

I've been doing a little digging around on this issue, and the issue is somewhat broader than what you are seeing.

Not only does the current implementation complicate using background threads, but it causes issues in a mixed-mode app where you have both web UI elements and smart client elements (like Silverlight) using the data portal hosted in the web app.

Your suggested change is quite sufficient to resolve the data portal issue - and that causes no undesirable side-effects, since the data portal sets all the context values (they flow from the client).

I am still somewhat concerned about creating a new and empty context on a background thread. I'm thinking though, that this is not a new problem - anyone creating background threads knows they need to manually shift any UI thread data to the background thread as they create it. So perhaps there's no natural assumption that ApplicationContext would automatically flow across.

Following that train of thought, i think I'll add the !IsValid check to 4.0.1 and we'll see how that goes.

JonnyBee replied on Tuesday, July 27, 2010

Hi,

I have a reworked CslaBackroundWorker ready as per comments and dicussions I had with Rocky earlier.  It is aimed at Rich clients as Rocky mentioned but I believe it should work fine in ASP.NET too - especially if you are already using the standard BackgroundWorker. My implementation is a wrapper around a  BackgroundWorker that sends the ClientContex, GlobalContext an User to the background thread.

I will return from holiday on the 2.8 and will do a quick check on the code. You may send me an email at jonny.bekkum(a)gmail.com and I can send you the code for testing under ASP.NET.

RockfordLhotka replied on Tuesday, July 27, 2010

JonnyBee

I have a reworked CslaBackroundWorker ready as per comments and dicussions I had with Rocky earlier.  It is aimed at Rich clients as Rocky mentioned but I believe it should work fine in ASP.NET too - especially if you are already using the standard BackgroundWorker. My implementation is a wrapper around a  BackgroundWorker that sends the ClientContex, GlobalContext an User to the background thread.

The only thing you might need to do in ASP.NET is create your own synchronizationcontext object before using the component (unless ASP.NET creates one for you - which is possible).

The big thing that all the smart client technologies do to protect their UI thread is to create a synchronizationcontext for it. That is what allows the BackgroundWorker to find the UI thread again, when the background task is complete.

Environments that don't have a pre-existing synchronization context (to my knowledge everything except Windows Forms, WPF and Silverlight) need to create their own context if they want the BackgroundWorker to call back on the same thread.

But Web Forms might also have a context - I guess we'll find out Smile

uma replied on Wednesday, July 28, 2010

Thanks Rocky and Johnny.  After thinking more about spawing a Thread from ASP.Net  application, we re-wrote that piece of code with async WCF calls with no callback.

But still I feel Context.IsValid should be checked as there is no point in using an invalid context

While doing further testing , we found it is not safe to do the following as dpContext is set before  proxy.Fetch  for obvious reasons.

 public  class MyBusinessBase<T> : Csla.BusinessBase<T> where T: MyBusinessBase<T>
{
    protected MyBusinessBase()
        {
            ApplicationContext.ContextManager = null;
           
        }
}

 

Copyright (c) Marimer LLC