Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
HomeAnnouncementsFree MagazinesWhite PapersSubmit Content
Discussion GroupsASP.NETWindows FormsLanguages.NET FrameworkVisual Studio.NET
Articles.NET FrameworkASP.NETToolsWindows Forms
.NET DirectoryOpen Source ProjectsUser GroupsWeb Resources
Related Topics
Visual Basic 6SQL ServerMS AccessOther DB ProductsMS Server ProductsMore Topics ...

.NET Forum / Windows Forms / Design Time / April 2006

Tip: Looking for answers? Try searching our database.

BackgroundWorker with a DataAccess Layer

Thread view: 
Enable EMail Alerts  Start New Thread
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 Magazines

Get 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 ...

Oracle MagazineNetwork ComputingComputer WorldBio-IT WorldeWeekInformation WeekInfosecurity
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2008 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.