Server-side LocalContext: problem calling objects from different threads...

Server-side LocalContext: problem calling objects from different threads...

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


Izhido posted on Monday, January 22, 2007

Hi everybody!

I got this problem while working on 2.1.1.
Recently, my business objects were changed to take advantage of the Csla.ApplicationContext.LocalContext() data store, mainly on the server side; there, I put many ADO.NET-related objects that give the business layer access to the underlying database, among other things. So far, so good.

Recently, one developer on my company created an application that uses these business objects. At one point, he wanted to invoke a "process" (a Csla.CommandBase-based object I built into the business layer), but did it in a separate thread from the UI main thread. He told me that his app was actually crashing at the invocation of the process. A NullReferenceException was being thrown from inside the DataPortal_Execute() class. When I checked it, I saw that the LocalContext() data store was actually >>empty<<, thus, basically, there's no way my business object could get any database-related info, and it should fail (understandably).

Now, that wouldn't surprise me if the server was mounted as an IIS process (as far as I know, unless you're very careful configuring your web app, you may lose your server cache frequently)... the thing is, there's no IIS involved, the server is actually >>local<< to the client!

And, of course, I can guarantee the process being invoked works correctly (tested a million times :D ); seems the only difference with my tests & the new application is that the process was being invoked from a different thread than the main UI thread (where the Login() method was invoked).

Any idea what can I do here? It appears that being able to call the process on a separate thread is fundamental for the developer of this app. Suggestions will be highly appreciated!

- Izhido

ajj3085 replied on Monday, January 22, 2007

Well, the problem sounds like the LocalContext is using a ThreadContext for its variable storage, whcih is unique to each thread.  One of the Csla contexts takes this into consideration and doesn't use the thread local storage.. but I don't recall at the moment which one that is..

RockfordLhotka replied on Monday, January 22, 2007

No, all of the Csla.ApplicationContext context collections are per-thread.

This is a complex issue. You can use static/Shared fields to store things per-AppDomain, which in any web setting makes the values global to all users of that web site. Or you can use per-thread techniques, which makes the values available to one specific user, but also just to that one thread.

Making values available to the various threads that might be created and/or used by a specific user would require that CSLA .NET provide its own thread allocation mechanism. And of course that would not co-exist well with the .NET threadpool, ASP.NET's threading mechanism, etc.

You'll notice that even Microsoft doesn't solve this issue in any realistic sense. System.Thread.Threading.CurrentPrincipal is per-thread too.

Izhido replied on Monday, January 22, 2007

Thanks, ajj, Rocky!

Hm.. I think you got me a little confused here. Right now I'm debugging the app, and it stepped just into the process invocation (in the separate thread):
pru = RemoverAsignacionRuta.RunRemoverAsignacionRuta(ssn, False, DirectCast(RouteInfoCell.RouteLinkLabel.Tag, Long))
(Spanglish, sorry :D )

If I ask the IDE at this point the current value of System.Threading.Thread.CurrentPrincipal, it returns:

Threading.Thread.CurrentPrincipal: {SuiGeneris.eXigo.Negocios.eXigoPrincipal}
|
--- Identity: {SuiGeneris.eXigo.Negocios.eXigoIdentity}

These two are my principal & identity business objects.

So, it seems, CurrentPrincipal (and CurrentPrincipal.Identity) are actually crossing the thread barrier.

Am I missing something here?

RockfordLhotka replied on Monday, January 22, 2007

There are cases where the currentprincipal moves across threads. You can set that as a policy, and ASP.NET does some magic to make it happen too (remember that they own the threading within their environment).
 
Rocky
 


From: Izhido [mailto:cslanet@lhotka.net]
Sent: Monday, January 22, 2007 4:49 PM
To: rocky@lhotka.net
Subject: Re: [CSLA .NET] Server-side LocalContext: problem calling objects from different threads...

Thanks, ajj, Rocky!

Hm.. I think you got me a little confused here. Right now I'm debugging the app, and it stepped just into the process invocation (in the separate thread):
pru = RemoverAsignacionRuta.RunRemoverAsignacionRuta(ssn, False, DirectCast(RouteInfoCell.RouteLinkLabel.Tag, Long))
(Spanglish, sorry :D )

If I ask the IDE at this point the current value of System.Threading.Thread.CurrentPrincipal, it returns:

Threading.Thread.CurrentPrincipal: {SuiGeneris.eXigo.Negocios.eXigoPrincipal}
|
--- Identity: {SuiGeneris.eXigo.Negocios.eXigoIdentity}

These two are my principal & identity business objects.

So, it seems, CurrentPrincipal (and CurrentPrincipal.Identity) are actually crossing the thread barrier.

Am I missing something here?


ajj3085 replied on Tuesday, January 23, 2007

RockfordLhotka:
No, all of the Csla.ApplicationContext context collections are per-thread.


Oh, I thought this had changed a while ago because of a thread here.  I'll try to locate the thread.. I must be mistaken.

ajj3085 replied on Tuesday, January 23, 2007

I found this thread, which is what I was remembering. 

RockfordLhotka replied on Tuesday, January 23, 2007

Oh yeah, I'd forgotten about that!

So yes, on a smart client the ClientContext is available to all threads in the AppDomain.

But not in ASP.NET or any server environment, because they are all running multiple users on different threads in the same AppDomain.

ajj3085 replied on Tuesday, January 23, 2007

RockfordLhotka:
Oh yeah, I'd forgotten about that!


Whew, I thought I was totally offbase there.

RockfordLhotka:
So yes, on a smart client the ClientContext is available to all threads in the AppDomain.

But not in ASP.NET or any server environment, because they are all running multiple users on different threads in the same AppDomain.

I'm not sure I follow; if all the threads are in the same AppDomain, shouldn't ClientContext be available to all threads just as it is in a smart client?  Or am I missing something?

RockfordLhotka replied on Tuesday, January 23, 2007

The change I made only applies to code where you are NOT running in ASP.NET and where you ARE on the client workstation.

If you are running in ASP.NET, then your appdomain is shared across mutliple users, pretty much by definition, so I can't store ClientContext in the appdomain in that environment.

Also, if you are running on the server in any server hosting environment that is supported (ASP.NET, Remoting custom host or COM+) then your appdomain is shared across multiple users too. Again, I can't store ClientContext in the appdomain in those environments either.

If someone did come up with a custom host of some sort, where they created an appdomain per-client-request, then the value could be stored at the appdomain level. Such a hypothetical developer would first need to overcome the non-trivial performance implications of what I'm suggesting. Then they'd need to alter the code in Csla.ApplicationContext to be aware of their host so it could store ClientContext in the appdomain within that enviornment as well.

Today however, there are no such hosts provided by Microsoft.

ajj3085 replied on Wednesday, January 24, 2007

Ahh, ok.  I didn't realize the changes were only for local smart clients.

ward0093 replied on Tuesday, November 06, 2007

O.k.... having some CurrentPrincipal issues and this previous post is similar.

I have a WinForms application that runs completely local (with a local database) and we set the CSLA.ApplicationContext.User to the currenly logged in IPrincipal object. 

When we spin up a background thread (an second thread other than that on the current UI Thread) the user property is already set to the UI User property on the main thread?  How is this possible?  Don't I have to set the background threads CSLA.ApplicationContext.User property when it is started up?

ward0093

RockfordLhotka replied on Tuesday, November 06, 2007

If you directly create a background thread I believe it defaults to “inheriting” the principal of the creating thread. This isn’t necessarily the case when using the thread pool, which is the better way to create background threads than direct creation. I don’t know if it is true when using the BackgroundWorker component, which is also better than manual creation of a thread.

 

When I say “better”, I mean more reliable, with more services. My preference is to use BackgroundWorker, because if you don’t then you’ll need to write the functionality it provides for you, but by hand.

 

But if you are going to create background threads directly, you are typically better off using the thread pool instead of “x = new Thread()”, because the thread pool provides some nice features (thread reuse, maximum number of threads, etc).

 

Rocky

 

 

From: ward0093 [mailto:cslanet@lhotka.net]
Sent: Tuesday, November 06, 2007 2:52 PM
To: rocky@lhotka.net
Subject: Re: [CSLA .NET] Server-side LocalContext: problem calling objects from different threads...

 

O.k.... having some CurrentPrincipal issues and this previous post is similar.

I have a WinForms application that runs completely local (with a local database) and we set the CSLA.ApplicationContext.User to the currenly logged in IPrincipal object. 

When we spin up a background thread (an second thread other than that on the current UI Thread) the user property is already set to the UI User property on the main thread?  How is this possible?  Don't I have to set the background threads CSLA.ApplicationContext.User property when it is started up?

ward0093



ajj3085 replied on Wednesday, November 07, 2007

I've been using the BackgroundWorker componet quite extensively, and I haven't had to do anything to set the princpal properlly.  I had to set the princpal manually though when creating my own threads though.  Haven't used the ThreadPool.

ward0093 replied on Wednesday, November 07, 2007

O.K... I use strictly the BackgroundWorker and yes... it seems to inherit the creating thread properties....

However... I just ran a bunch of tests and something really strange is going on...

When the app is started (no one is logged in yet) we spin up the background thread... so both the UI thread and the background thread have no logged in principal.  Now when we log in on the UI thread, CSLA.ApplicationContext.User.IsAuth. = True... UNTIL the chain of calls is completed and then it resets the principal to nothing.

Is our background thread doing something that is affecting this?  It is very strange and i can only test inside my Dev. Environment.

ward0093

Copyright (c) Marimer LLC