Descendent Grandchild (Generic) and BackgroundWorker Component

Descendent Grandchild (Generic) and BackgroundWorker Component

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


tsaltd posted on Wednesday, January 17, 2007

I have long running processes ( Aggregation SQL Queries) in the following Hirearchy

                    

There are multiple long running queries that need to be run to calculate properties of all Animals in each store --  IE: - Count of All Animals.Dogs, Average Age of All Animals.Dogs, Cost of All Animals.Dogs

I've started by calling the long running process from the process that iterates through the AnimalCollection in a store

holdID = dr.GetInt32("id");
foreach (Store item in this)
     (item.AnimalCollectionID == dr.GetInt32("AID"))
          if (item.ID == holdHistID)
                  {
                      isFound = true;
                     
                         item.Cats = Cats.NewAnimal();
                         item.Cats.id = dr.GetInt32("dataset_id");
                         item.Cats.getMetrics(item.dsDatFile.id);

The above iterates through the Stores collection and:

            That method -- In the Animal<T> class -- calls

                                       this.getMetrics(this.myDataWorker, id);

               Both those methods are Instance ( not static ) methods in Animal<T>

                                 and

         myDataWorker is declared as a Public field in the Animal<T> class

the DataPortalCreate() in Animal instantiates the BackgroundWorker and its events 

protected override void DataPortal_Create()

{

myDataWorker = new BackgroundWorker();

myDataWorker.DoWork += new System.ComponentModel.DoWorkEventHandler(this.myDataWorker_DoWork);

myDataWorker.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(this.myDataWorker_RunWorkerCompleted);

}

The DoWorkEventHandler fires OK and completes and sets a return value

 ... but the RunWorkerCompleted never seems to fire

private void myDataWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)

{

this._animal_count  = (int)e.Result;

}

And the Count field in Animal never gets set

Am I putting the calls and instatiation in the right place ?

any clue why RunWorkerCompleted is not firing or why field/property is not being set ???

Is there another pattern I might follow.  I've never used a CommandObject before ... should I do it here

Any issues with not using the BackgroundWorker on a Form ... I'm putting it in the Generic EditableChild ( and the Animal.Cat is a grandchild (Child of Store) ) and calling it from the StoreList

TIA ..

Steve


                        

RockfordLhotka replied on Wednesday, January 17, 2007

CSLA .NET, and therefore your objects, are NOT threadsafe. This simply means that you can never allow more than one thread to talk to one of your objects (really an entire object graph) at a time.

The UI runs on one thread, your backgroundworker task runs on another thread. You must ensure that only one thread or the other uses your objects. If your objects are connected to the UI through data binding then you can NOT use a backgroundworker (or any other non-UI thread) to interact with your objects while they are data bound.

Also, remember that per-thread data, like the user's principal and all the context data in Csla.ApplicationContext, are really per-thread. If you need any of that information on your background thread, you must somehow get it there.

tsaltd replied on Wednesday, January 17, 2007

Thanks Rocky, but ....

I'm not sure whether I'm in that "un-thread-safe" situation ....

Object tree is populated from button click on form

If it's OK to instatiate and wire  the BackgroundWorker from the DataPortalCreate of the Generic <Animal> parent of Subclass Animal.Cat and invoke it from the StoreList class then I should be "safe", no ... it is not data-bound.

So if I can get this working ... what I understand is that I cannot kick-off > the next long running query until the first BackgroundWorker thread ends ... I was hoping to launch 3 or 4 queries Asynchronously .... but that's a No No ??

What if I do it from a Web Service ?? -- create the object -- shut down all the threads and return object to client where it will  then be data-bound.

The only time I plan to do thes Asynch calls using Background Worker is when I am "hydrating" a new instance.

Or are you saying that my code is not running correctly now because I'm violating thread safety and there is no pattern or architecture ( WebService ? ) that I can use to implement this process.

Steve 

 

RockfordLhotka replied on Wednesday, January 17, 2007

My comments were more general in nature - I'm still not sure what your exact issue is.

But ADO.NET isn't threadsafe either. You can't, for instance, share a connection or any other ADO.NET objects across threads.

I guess I read into what you were saying that you want to load several parts of your object graph on different threads. Which isn't supported, because you'd end up with multiple threads at least interacting with the collections and parent objects. Not that you maybe can't make it work - but you'll hit threading issues with standard CSLA (due to the interaction between parent-child objects within CSLA itself).

I also now think I know why your BGW isn't raising events back to the UI correctly. To do this, the BGW needs a reference to a Windows Forms object so it can find that object's thread (the UI thread). That is required so it can marshal its events back onto the UI thread.

But you are creating the BGW within your DP_Create() method, and I imagine you aren't giving it a reference to a UI control or component when it is created/initialized. Without that, it can't safely raise events back to the UI thread.

Were it up to me, I'd use the BGW from the UI and have the background task call the factory method of the object. That'd spin the loading of the object into a background thread quite nicely, and all the BGW eventing would work automatically because it would be a UI component (which is what it is really designed for).

The only thing you'd lose there is any sort of progress notification. But that's hard to do in the disconnected architecture of CSLA, because it would require callbacks from DP_Fetch(), and you can't reliably do that if your data portal server is ever remote. You can do it if you know your data portal "server" will always be called via LocalChannel, so it is in the same AppDomain. Then you could pass a back-reference through your Criteria object to allow the callbacks. Just remember that you could never move to a phyiscal n-tier deployment then!

tsaltd replied on Thursday, January 18, 2007

Thanks again R.L. ... let me see if I can clarify .. 

(I have considered running the BGW from the form, and passing those values to the BO ... but I'd prefer to encapsulate all of that behavior in the Business Object)

Background Worker Component has no hooks to the form ... and I am under the impression that BWG can exist in a class library ...

Fom my previous posts: 

If it's OK to (1) instatiate and wire  the BackgroundWorker from the DataPortalCreate of the Generic <Animal> parent of Subclass Animal.Cat and (2) invoke it from the StoreList class then I should be "safe", no ... it is not data-bound.

  getMetrics(ID) -- In the Animal<T> class -- calls

                                       this.getMetrics(this.myDataWorker, id);

               Both those methods are Instance ( not static ) methods in Animal<T>

                                 and

         myDataWorker is declared as a Public field in the Animal<T> class

 

It compiles and runs ... but the RunWorkerCompleted (in the ListBase of  Stores) never seems to fire

private void myDataWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)

{

this._animal_count  = (int)e.Result;

}

ajj3085 replied on Thursday, January 18, 2007

tsaltd:
(I have considered running the BGW from the form, and passing those values to the BO ... but I'd prefer to encapsulate all of that behavior in the Business Object)


I think that is your best bet.  Your objects should not have to worry about loading and threading, just loading.  What if you run into a situation where for some reason you don't want to run the load on another thread? 

I think your BO should just worry about the business aspects, not threading issues. 

Copyright (c) Marimer LLC