.NET Forum / Windows Forms / Design Time / April 2006
BackgroundWorker with a DataAccess Layer
|
|
Thread rating:  |
Techno_Dex - 05 Apr 2006 22:41 GMT I'm stumped with a design problem and hoping someone has an elegant solution. I have a DataAccess Layer which contains all my database access functionality. I would like to run all the processing requests that come into the DataAccess Layer on a thread that is NOT the UI thread. My DataAccess object is sitting behind a Controller class so the DataAccess object only gets instantiated once on startup and remains throughout the lifetime of the application. All of my forms get at the the various methods in my DataAccess Layer by calling Controller.DataAccess.<MethodName> (i.e. Controller.DataAccess.GetMyCustomerData(iCustomerID)). Everything I've looked at so far for using Threads or BackgroundWorkers is throwing me into a tailspin. If I use BackgroundWorkers, I can get the e.Result from the BackgroundWorker_RunWorkerCompleted Event but then I can't figure out a way to pass the results back to the calling form. I thought about creating a custom event and delegate for each Method in my DataAccess Layer and raising a new Event from within the BackgroundWorker_RunWorkerCompleted EventHandler and passing the e.Result into the delegate method but that causes issues because I could have multiple calls to the method before the first one is finished and I'm not guaranteed that the results will get back to the right calling class or delegate in the calling class.
Does anyone have any ideas on passing in a calling object and a delegate and someway of keeping them all straight for each Background thread that is called? Am I missing something or is there a better approach to handling this issue? TIA
Kevin Spencer - 06 Apr 2006 00:50 GMT The BackgroundWorker's RunWorkerCompleted event handler is on the main thread, so you can do whatever you need from there.
 Signature HTH,
Kevin Spencer Microsoft MVP Professional Numbskull
Show me your certification without works, and I'll show my certification *by* my works.
> I'm stumped with a design problem and hoping someone has an elegant > solution. I have a DataAccess Layer which contains all my database access [quoted text clipped - 22 lines] > is called? Am I missing something or is there a better approach to > handling this issue? TIA msnews.microsoft.com - 06 Apr 2006 01:36 GMT I might not have explained myself well enough. Here is some sample code. My BackgroundWorker is not in the Form
//Controller.cs public class Controller { private DataAccess _DataAccess; public static DataAccess DataAccess { get { if (_DataAccess == null) { _DataAccess = new DataAccess(); } return (_DataAccess); } } }
//DataAccess.cs public class DataAccess { private DataSet _DataSet;
public DataSet GetMyCustomerData(int ClientID) { //This is where the database calls occur //This is where I was thinking about calling the BackgroundWorker thread which also removes //the need for the UI to know anything about how the data is retrieved even if it's on it's own Thread //I've thought about trying to pass in the Form1 instance and/or a delegate on the Form1 class to this method //also in order to call back to the Form1 class or any Form1 class for that matter after the process has completed }
//Tried to figure out a way to possibly to make the database calls in here private void BackgroundWorker1_DoWork(object sender, DoWorkEventArgs e) {
}
//And tried to figure out a way to possibly pass back the DataSet from the Database call in here private void BackgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { _DataSet = (DataSet)e.Results; } }
//Form1.cs public class Form1 { private button1_Click(object sender, EventArgs e) { DataSet dsDataSet; dsDataSet = Controller.DataAccess.GetMyCustomerData(12345); }
//This was my thought as a possible delegate to get the results from the Controller.DataAccess method call public void GetMyCustomerDataCallback(DataSet dsDataSet) {
} }
> The BackgroundWorker's RunWorkerCompleted event handler is on the main > thread, so you can do whatever you need from there. [quoted text clipped - 25 lines] >> is called? Am I missing something or is there a better approach to >> handling this issue? TIA Kevin Spencer - 06 Apr 2006 12:42 GMT Well, your BackgroundWorker should be a member of the form. It can execute the method in the Controller Class. The DoWork Event Handler receives an instance of DoWorkEventArgs, which can have any object passed to it as an argument. It also has a member called "result," to which the return value of a function can be passed. The "result" member is then accessed in the RunWorkerCompleted event handler, which operates in the main thread of the Form, and can then do anything with it.
Here's an example from MSDN2:
http://msdn2.microsoft.com/en-us/library/hybbz6ke(VS.80).aspx
In your case, you would do something like the following:
public class Form1 { private System.ComponentModel.BackgroundWorker backgroundWorker1; private DataSet dsDataSet;
public Form1() { this.backgroundWorker1 = new System.ComponentModel.BackgroundWorker(); this.backgroundWorker1.DoWork += new System.ComponentModel.DoWorkEventHandler(this.backgroundWorker1_DoWork); this.backgroundWorker1.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(this.backgroundWorker1_RunWorkerCompleted); }
private button1_Click(object sender, EventArgs e) { backgroundWorker1.RunWorkerAsync(); }
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) { e.Result = Controller.DataAccess.GetMyCustomerData(12345); }
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { dsDataSet = (DataSet)e.Result; }
}
 Signature HTH,
Kevin Spencer Microsoft MVP Professional Numbskull
Show me your certification without works, and I'll show my certification *by* my works.
>I might not have explained myself well enough. Here is some sample code. >My BackgroundWorker is not in the Form [quoted text clipped - 96 lines] >>> is called? Am I missing something or is there a better approach to >>> handling this issue? TIA Techno_Dex - 06 Apr 2006 14:43 GMT I came to a similar conclusion, but am not very happy with the solution. I would rather see the threading be handled inside of the DataAccess class in order to hide the details and minimize the amount of code that is rewritten in every form that uses the same DataAccess methods.... Any other suggestions?
> Well, your BackgroundWorker should be a member of the form. It can execute > the method in the Controller Class. The DoWork Event Handler receives an [quoted text clipped - 144 lines] >>>> thread that is called? Am I missing something or is there a better >>>> approach to handling this issue? TIA Kevin Spencer - 06 Apr 2006 15:46 GMT The *method* is in the DataAccess class. *How* it is executed is determined by the client using it. The client may or may not want to use a separate thread to execute the method. However, if you wish, you can simply spawn another Thread in the DataAccess class that performs the operation. There is no need to use a BackgroundWorker component to do this. The class can then raise an event when the Thread has finished executing. However, it should be noted that a Windows Form is STA threaded, and you will still have to deal with the threading problem in the client Form.
 Signature HTH,
Kevin Spencer Microsoft MVP Professional Numbskull
Show me your certification without works, and I'll show my certification *by* my works.
>I came to a similar conclusion, but am not very happy with the solution. I >would rather see the threading be handled inside of the DataAccess class in [quoted text clipped - 151 lines] >>>>> thread that is called? Am I missing something or is there a better >>>>> approach to handling this issue? TIA Techno_Dex - 06 Apr 2006 17:07 GMT This brings me back full circle to my original problem. Say I do spawn a new thread in the DataAccess.GetMyCustomerData() method which queries the database and gets the results back and raises an event. There is no way for me (that I can think of, thus this posting) to determine which Form was the calling form and should get the results. The use could have two instances of Form1 open (fForm1_1 and fForm1_2). Each form is dealing with a different Client's data (fForm1_1 -> ClientID = 1000, fForm1_2 -> ClientID = 2000). If the users executes GetMyCustomerData from both Form1 instances back to back (say it take 5 minutes to query client data, the user starts one process then starts the second process). Both fForm1_1 and fForm1_2 will be listening for the GetMyCustomerDataCompleted Event. They will both get data from ClientID 1000 then both get the data for ClientID 2000.
//MDI Parent Form1 fForm1_1 = new Form1(); Form1 fForm1_2 = new Form1();
fForm1_1.Show(); fForm1_2.Show();
//fForm1_1 instance private button1_Click(object sender, EventArgs e) { Controller.DataAccess.GetCustomerDataCompleted += new EventHandler(DataAccess_GetCustomerDataCompleted) Controller.DataAccess.GetCustomerData(1000); }
//fForm1_2 instance private button1_Click(object sender, EventArgs e) { Controller.DataAccess.GetCustomerDataCompleted += new EventHandler(DataAccess_GetCustomerDataCompleted) Controller.DataAccess.GetCustomerData(2000); }
> The *method* is in the DataAccess class. *How* it is executed is > determined by the client using it. The client may or may not want to use a [quoted text clipped - 161 lines] >>>>>> thread that is called? Am I missing something or is there a better >>>>>> approach to handling this issue? TIA Kevin Spencer - 06 Apr 2006 19:06 GMT You have a couple of options. You can pass the data back with the EventArgs, or you can store the data in the class and have the form fetch it when it's ready.
 Signature HTH,
Kevin Spencer Microsoft MVP Professional Numbskull
Show me your certification without works, and I'll show my certification *by* my works.
> This brings me back full circle to my original problem. Say I do spawn a > new thread in the DataAccess.GetMyCustomerData() method which queries the [quoted text clipped - 199 lines] >>>>>>> Background thread that is called? Am I missing something or is >>>>>>> there a better approach to handling this issue? TIA Techno_Dex - 06 Apr 2006 20:11 GMT How does this help? There is only one instance of the DataAccess Class and it is being used by multiple instances of 1 to N types of Forms. Each instance Form has no way to tell if it is getting it's own data or another instance Form's data.
> You have a couple of options. You can pass the data back with the > EventArgs, or you can store the data in the class and have the form fetch [quoted text clipped - 204 lines] >>>>>>>> Background thread that is called? Am I missing something or is >>>>>>>> there a better approach to handling this issue? TIA Kevin Spencer - 07 Apr 2006 14:17 GMT The Form has to pass the ClientID to the method, right? So, if the event passes the ClientID back, the client knows if it is the data it is looking for. Similarly, if the data being fetched is stored in the DataAccess class, it can be stored in a Collection, such as a Dictionary Collection, which enables a client to fetch the data using the ClientID.
 Signature HTH,
Kevin Spencer Microsoft MVP Professional Numbskull
Show me your certification without works, and I'll show my certification *by* my works.
> How does this help? There is only one instance of the DataAccess Class > and it is being used by multiple instances of 1 to N types of Forms. Each [quoted text clipped - 210 lines] >>>>>>>>> Background thread that is called? Am I missing something or is >>>>>>>>> there a better approach to handling this issue? TIA Techno_Dex - 07 Apr 2006 18:36 GMT I came to a similar conclusion this morning, but thought I would return a GUID to the calling Form on the initial call and raise an event when the data is returned which the Form would be listening for, then it would make a call back to the DataAccess class to get the results passing in it's GUID to extract the results (or getting Null if the correct Form's GUID data has not been returned yet). This would prevent any other Form from getting another Form's data (by accident or on purpose) since it now requires a GUID Key to get the results. I'm still not fond of either of these approaches as it isn't very elegant (i.e. it's not a solution you would look at and say "That's really good I should use that"). The idea behind a DataAccess Layer is that it is pluggable/swappable so I can switch between a Local SQLDAL, Remoting DAL, WebServiceDAL, etc... This just rubs me the wrong way. I was thinking there was a better way and I have missed it.
> The Form has to pass the ClientID to the method, right? So, if the event > passes the ClientID back, the client knows if it is the data it is looking [quoted text clipped - 216 lines] >>>>>>>>>> Background thread that is called? Am I missing something or is >>>>>>>>>> there a better approach to handling this issue? TIA Kevin Spencer - 07 Apr 2006 21:51 GMT I can see your point. I wish I had more time to think about the most elegant way to handle the issue, but I don't. :(
 Signature HTH,
Kevin Spencer Microsoft MVP Professional Numbskull
Show me your certification without works, and I'll show my certification *by* my works.
>I came to a similar conclusion this morning, but thought I would return a >GUID to the calling Form on the initial call and raise an event when the [quoted text clipped - 234 lines] >>>>>>>>>>> Background thread that is called? Am I missing something or is >>>>>>>>>>> there a better approach to handling this issue? TIA
Free MagazinesGet these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...
|
|
|