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
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.
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.
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.
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; }
}
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