Connecting to a database on a different thread - c#

I have a WPF application where the user enters database information in some textboxes. Once the user clicks "connect", a connection string is created from what the user had entered and a connection is established. I noticed that if the user enters any info that is wrong, the application will hang until the connection times out. By hang, I mean the user can't interact with the rest of the application at all.
It is my goal to keep the application responsive while the connection string is tested.
I thought that putting this workflow on a different thread is a good solution. My idea is to just disable anything that may need a database connection while the thread runs. Once the thread comes back (and has has confirmed the connection string to be valid) I would re-enable everything. Otherwise, leave everything disabled.
However, the Thread class doesn't have an event notification when the thread is done (or at least I am unaware of one).
I have also worked with the BackgroundWorker class. This works better. However, when the RunWorkerCompletedEventHandler event is fired and the connection string isn't valid, I get the following exception:
The calling thread cannot access this object because a different
thread owns it.
This is probably because the connection still hasn't timed out when the completed event handler is fired.
Does anybody have any ideas, or should I just NOT try to multithread a connection to a database?
A code outline of what I am doing:
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
dbTool = new DBTool();
// Create the connection string
e.Result = dbTool.connectToDB(); // connectToDB() returns a bool (true if connection established)
}
private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// connectToDB() returns a bool (true if connection established)
if(e.Result == true) // Trying to read e.Result here throws the exception
{
// e.Error and e.Cancel should be checked first
// However, I would like the thread to finish before
// this event is fired
}
if (e.Error != null)
{
Console.WriteLine(e.Error.Message);
}
}

Don't preserve your DbConnection object in a single global variable and share it between threads.
The .NET environment will automatically pool your connections and share them, so calling new DbConnection() is very fast.
You should keep the connection string in a global variable, but then create connections as required on each thread.
EDIT: The original poster may have actually wanted ideas on how to keep the WinForms application responsive while a connection string is being tested. In that case, you want to spawn a different thread to test the connection. From the "connection test thread," you can update the UI by following this pattern - How to update the GUI from another thread in C#?
public void TestConnectionThread(String connstr_to_test)
{
// Notify the user that we're doing our test
string message = "Testing...";
lblTestResultMessage.SetPropertyThreadSafe(() => lblTestResultMessage.Text, message);
try {
dbTool = new DBTool();
message = dbTool.connectToDB();
// If something failed, show a useful debugging message
} catch (Exception ex) {
message = ex.ToString();
}
// Use a lambda expression to communicate results to the user safely
lblTestResultMessage.SetPropertyThreadSafe(() => lblTestResultMessage.Text, message);
}

From DBConnection's documentation:
Any public static (Shared in Visual Basic) members of this type are thread safe. Any instance members are not guaranteed to be thread safe.
In other words, different threads should never share a database connection, because the instance cannot safely be shared. As Ted Spence suggests, you should instead create connections only as you need them (and .Dispose() them when you're done with them). .NET has a built in connection pooling mechanism that does a very good job of making sure connections are reused when possible, but holding onto connections any longer than is absolutely necessary can interfere with its ability to do that.

try
dbtool tool = e.result as dbtool;
If you have a variable in dbTool that that gets set to true or false when the query completes then you should be able to call
tool.variable = true/false

Thank you everyone for your input.
I was able to come up with a solution. After coming across Working With The WPF Dispatcher. I determined that you can obtain the UI thread's Dispatcher object:
//...
dbTool = new DBTool();
// Initialize the connection string
// Disable some UI
Thread thread = new Thread(new ThreadStart(
delegate()
{
dbTool.connectToDB();
UIControl.Dispatcher.BeginInvoke(
new Action(
update
));
}
));
thread.Start();
//.....
void update()
{
if (dbTool.validString) // If the connection string was valid
{
// Re-enable controls
}
else // Invalid connection string
{
// Keep controls disabled if no connection could be created
}
}
This indeed will test the connection string on a different thread, leaving the rest of the application responsive.

Related

C# Trouble with event handlers on dieing threads

First of all my Main is STAThread and i am not able to change this without facing problems with the rest of my code.
So, I am currently using Rapi2 To pull and push files between my Pda and Computer. Now since there is quite a bit of number crunching i would like to do this on a separate thread. First wat i do is create an RemoteDeviceManager and then make an Event Handler for when a device connects.
public void Initialize()
{
_deviceManager = new RemoteDeviceManager();
_deviceManager.DeviceConnected += DeviceConnected;
}
As you can see when my device connects it triggers DeviceConnected.
This is the class that i end up pulling and pushing a database and do some number work.
private void DeviceConnected(object sender, RemoteDeviceConnectEventArgs e)
{
if (e.Device == null) return;
... (unimportant code)
}
Now the problem here is that i would want to run the code inside DeviceConnected in a new thread but i am unable to access e inside the new thread since it was initialized outside that thread
So now wat i tried was make a new thread before calling Initialize.
public Watcher()
{
_dataThread = new Thread(Initialize);
_dataThread.IsBackground = true;
_dataThread.Name = "Data Thread";
_dataThread.SetApartmentState(ApartmentState.MTA);
_dataThread.Start();
}
But the thread dies and thus never fires my event handler.
I tried many different ways to make it work or keep my thread alive but without any success. I hope someone here is able to give me some hints.

Method called twice in same moment

I'm working on a windows forms application and fighting with a very harsh error. The application is supposed to run on a local machine and handle requests form a server applicaton. The client application looks like this:
public Reader mr_obj;
public Form1()
{
mr_obj = new MyReader.Reader(7137);
mr_obj.UserEvent += new ReaderEvent(UserEvent);
}
private void UserEvent(UserEvent e, long threadID)
{
Thread.Sleep(1000);
SafeSomethingToDB();
}
The Reader() object is connecting the client application to the server application. So after this, the server application is able to trigger the UserEvent() method in the client application. Ther problem is now, that the client application, which handles the UserEvents, crashes if the UserEvent() method gets triggered twice within one second.
(Its actually not crashing just hanging untill you kill the task, a try catch wont return an error)
What I've tried so far is to delegate the Thread.Sleep() and SafeSomethingToDB() to another thread. This doesnt work because the server application does not wait until the tread is finished. So the server application does not find the data in the DB because its not waiting 1 second...
The same problem happens when I did that with background workers.
Is there a possibility to handle these two triggers, which come from the same server application, in sort of a parallell way at the same time?
Any suggestions very apreciated
EDIT: I think locking the method does not cause the application to process both triggers in the same time. To make this visible I'v tried this:
private void UserEventHandler(UserEvent e, long threadID)
{
lock (_lockObject)
{
MessageBox.Show("Messagebox 1");
MessageBox.Show("Messagebox 2");
}
}
When the first request triggers UserEvent() "MessageBox1" appeares. If you press OK, "MessageBox2" appeares. But if the UserEvent gets triggered a second time while "Messagebox2" is still opened, "MessageBox1" does not appear. Instead of that the application start hanging. Shouldn "MessageBox1" appear again triggered by the second trigger of UserEvent() when the two triggers really ar bbeing processed at the same time? So the two triggers are not beeing preformed parallel or am I mistaking here?
Without knowing why you do the Sleep or what exactly SafeSomethingToDB does and what causes your problems, try to synchronize the calls:
private readonly object _lockObject = new object();
private void UserEvent(UserEvent e, long threadID)
{
lock(_lockObject)
{
Thread.Sleep(1000);
SafeSomethingToDB();
}
}
I think a simple lock for synchronization will work for you, try this
public Reader mr_obj;
private static readonly object sync = new object();
public Form1()
{
mr_obj = new MyReader.Reader(7137);
mr_obj.UserEvent += new ReaderEvent(UserEvent);
}
private void UserEvent(UserEvent e, long threadID)
{
lock(sync)
{
SafeSomethingToDB();
}
}
As you write in the comments, if SafeSomethingToDB() is called a second time before the first call has finished, then it crashes. So in other words: SafeSomethingToDB() is not re-entrant.
What you can do is use a Mutex (which stands for mutual exclusion), which defines a "critical section" in your code, meaning a code that can have only one thread executing it at any one time.
For instance:
private static Mutex mutex = new Mutex();
public void SafeSomethingToDB()
{
mutex.WaitOne(); // wait until it is safe to enter the critical section
// Critical section begins here
DoWorkAndStuff();
mutex.ReleaseMutex(); // indicate the end of the critical section
}
For more about System.Threading.Mutex, see http://msdn.microsoft.com/en-us/library/system.threading.mutex(v=vs.110).aspx.

Cross-thread operation not valid: Control accessed from a thread other than the thread it was created on

I know this question has been asked several times, but I can't quite seem to find why it does that in my situation.
First of, I'll explain my program a bit. It connects to a hardware device though a FTDI chip, so it generates us a COM over USB. My programs starts, it's an MDI interface. Clicking Connect brings a connect box similar to the Add Device box in Windows. It scans all COMs on the computer and tries to connect to it, to report what kind of device it is. Afterwards, the user click on a device, connects to it, and a child form opens up to control that device.
So, my problem is, I have a lot of multi-threading going on in there. The first time I connect to my device, it works fine. The second time, it returns a cross-thread operation error.
This is a short example of my code:
private void ConnectToolStripButton_Click(object sender, EventArgs e)
{
Dialogs.Connect Connect = new Dialogs.Connect();
if (Connect.ShowDialog() == DialogResult.OK)
{
this.Connect(Connect.Connection);
}
}
private void Connect(CommunicationInterfaces.Base Connection)
{
// Set the connection to the one the connect dialog gave us.
Child NewConnection = new Child(Connection);
// Set the parent of the new child and show it.
NewConnection.MdiParent = this;
NewConnection.Show(); // CRASH HERE!
}
So it crashes on the .show() with the following error, but only the second time I connect to it : Cross-thread operation not valid: Control 'Child' accessed from a thread other than the thread it was created on.
Thing is the Child (name of my child form) object is created on the UI thread, if I'm not mistaken. Why does it gives me a cross-thread operation error then? Is it a problem in my child form?
Update: Keep alive timer
So I've been able to pin point the problem a bit more. The problem lies with my Keep Alive thread that I have in my Child's form. To explain the situation: I have a connection which needs to be kept alive, so I have a thread running each 500ms to send a special header to my device. This is my keep alive thread code:
private void Child_Shown(object sender, EventArgs e)
{
this.Connection.DataReceived += DisplayData;
...
}
private void DisplayData(object Sender, byte[] Data)
{
...
CreateFaultBox((FaultBoxes.Base.BoxTypes)Data[1]);
...
}
private void CreateFaultBox(FaultBoxes.Base.BoxTypes BoxType)
{
KeepAliveTimer = new System.Threading.Thread(new System.Threading.ThreadStart(this.KeepAlive));
KeepAliveSwitch = true;
KeepAliveTimer.Start();
...
}
private void KeepAlive()
{
while (Connection != null && KeepAliveSwitch)
{
Console.WriteLine("KEEP ALIVE");
// Keep the connection alive.
Connection.KeepAlive();
// Wait 500ms for the next keep alive.
System.Threading.Thread.Sleep(500);
}
}
If I remove the first 3 lines, so if I don't start the thread, it works without any hiccups. Of couse, KeepAliveSwitch is set to false when I close the form, so the keep alive thread get's terminated after the next 500ms sleep period.
Solution
I changed my keep alive thread to a background worker. Works fine. But I don't get the difference between a thread and a background worker, shouldn't both work the same in this scenario?
Is any threading going on in the Child form? If so, this is my theory:
What you are likely seeing is a race condition wherein the first time you show a Client, the client form is busy connecting to some device on a background thread, while in the meantime your MDI parent UI thread Show()s the child form (and therefore owns the window handle, and all is good). The second time you show the client, you get a cached connection, and so the background thread in the child very quickly connects and then calls some UI operation, probably checking like a good developer using InvokeRequired(). Since your Client form doesn't yet have a handle, the background thread is getting a false for InvokeRequired, then Invoking and creating the handle itself.
All of this is documented in Ivan Krivyakov's great post on the matter.
So if all of the above sounds right, simply don't start the background work in the Child form until the handle is created. You might want to hang that on the Form Shown event rather than the constructor.

access object from a different thread

I have a Server class which it basically waits for connections from a client. Inside that class I create an NetworkStream object in order to be able to receive bytes from a client. Because the NetworkStream.Read() method is not asynchronous (meaning that it will wait until it reads bytes from a client in order to proceed executing code similar to the messagebox method), I have to read for bytes in a separate thread so that the user using the program can still interact with the program if the program happens to be waiting to read for data.
anyways a lot of objects are owned by that thread. One example is that I have a List called log in that class. I use that list to know the status of the server. Maybe it is listening for a connection or perhaps it's status is "connected" or "disconnected".
So if I do something like:
Server myServer = new Server("192.168.0.120","1300"...\\ I pass the appropite parameters in order to instantiate it
//...
.. then I am able to latter look at the log as
string foo = myServer.Log[0] for example.
because I want to know when the log is updated, on the server class I have created an event as:
public delegate void onUpdateHandler(string newStatus);
public event onUpdateHandler onUpdate = delegate { };
I then fire events on the Server class as:
onUpdate("waitingForConnection");
and I receive those events with the method:
but if I try to do something with newStatus I get the error stating:
System.InvalidOperationException: The calling thread cannot access this object because a different thread owns it.
so how can I pass an object with an event?
Edit
so I also notice that if I do:
I also get an error!
but when I do the same thing calling that from a button as:
// SERVER IS RUNNING BEFORE CALLING THIS METHOD
private void button3_Click(object sender, RoutedEventArgs e)
{
listView1.Items.Add("my own string");
}
I do NOT get an error!
why is it that I get an error with the event and I do not get an error when calling it with a regular button.
The problem is that the thread tries to access the ListView which is a DependencyObject which has thread affinity, use the Dispatcher to execute methods like this on the UI-thread, e.g.:
Application.Current.Dispatcher.Invoke((Action)(() =>
{
listView1.Items.Add(newStatus);
}));
Also see the threading model reference for additional info.
The problem is not that you try to do something with the value that you sent to the method, the problem is what you are trying to do with it.
The event handler is still running in your background thread, and from there you can't use any UI controls as they belong to the main thread.
The usual way of handling that is to use the CheckAccess method to check if you need to switch treads, and the Invoke method to hand off the work to the main thread:
void server_onUpdate(string newStatus) {
if (!listView1.Dispatcher.CheckAccess()) {
listView1.Dispatcher.Invoke(server_onUpdate, newStatus)
} else {
listView1.Items.Add(newStatus);
}
}

RasConnectionNotification after computer resumes from sleep

I've got a project called DotRas on CodePlex that exposes a component called RasConnectionWatcher which uses the RasConnectionNotification Win32 API to receive notifications when connections on a machine change. One of my users recently brought to my attention that if the machine comes out of sleep mode, and attempts to redial the connection, the connection goes into a loop indicating the connection is already being dialed even though it isn't. This loop will not end until the application is restarted, even if done through a synchronous call which all values on the structs are unique for that specific call, and none of it is retained once the call completes.
I've done as much as I can to fix the problem, but I fear the problem is something I've done with the RasConnectionNotification API and using ThreadPool.RegisterWaitForSingleObject which might be blocking something else in Windows.
The below method is used to register 1 of the 4 change types the API supports, and the handle to associate with it to monitor. During runtime, the below method would be called 4 times during initialization to register all 4 change types.
private void Register(NativeMethods.RASCN changeType, RasHandle handle)
{
AutoResetEvent waitObject = new AutoResetEvent(false);
int ret = SafeNativeMethods.Instance.RegisterConnectionNotification(handle, waitObject.SafeWaitHandle, changeType);
if (ret == NativeMethods.SUCCESS)
{
RasConnectionWatcherStateObject stateObject = new RasConnectionWatcherStateObject(changeType);
stateObject.WaitObject = waitObject;
stateObject.WaitHandle = ThreadPool.RegisterWaitForSingleObject(waitObject, new WaitOrTimerCallback(this.ConnectionStateChanged), stateObject, Timeout.Infinite, false);
this._stateObjects.Add(stateObject);
}
}
The event passed into the API gets signaled when Windows detects a change in the connections on the machine. The callback used just takes the change type registered from the state object and then processes it to determine exactly what changed.
private void ConnectionStateChanged(object obj, bool timedOut)
{
lock (this.lockObject)
{
if (this.EnableRaisingEvents)
{
try
{
// Retrieve the active connections to compare against the last state that was checked.
ReadOnlyCollection<RasConnection> connections = RasConnection.GetActiveConnections();
RasConnection connection = null;
switch (((RasConnectionWatcherStateObject)obj).ChangeType)
{
case NativeMethods.RASCN.Disconnection:
connection = FindEntry(this._lastState, connections);
if (connection != null)
{
this.OnDisconnected(new RasConnectionEventArgs(connection));
}
if (this.Handle != null)
{
// The handle that was being monitored has been disconnected.
this.Handle = null;
}
this._lastState = connections;
break;
}
}
catch (Exception ex)
{
this.OnError(new System.IO.ErrorEventArgs(ex));
}
}
}
}
}
Everything works perfectly, other than when the machine comes out of sleep. Now the strange thing is when this happens, if a MessageBox is displayed (even for 1 ms and closed by using SendMessage) it will work. I can only imagine something I've done is blocking something else in Windows so that it can't continue processing while the event is being processed by the component.
I've stripped down a lot of the code here, the full source can be found at:
http://dotras.codeplex.com/SourceControl/changeset/view/68525#1344960
I've come for help from people much smarter than myself, I'm outside of my comfort zone trying to fix this problem, any assistance would be greatly appreciated!
Thanks! - Jeff
After a lot of effort, I tracked down the problem. Thankfully it wasn't a blocking issue in Windows.
For those curious, basically once the machine came out of sleep the developer was attempting to immediately dial a connection (via the Disconnected event). Since the network interfaces hadn't finished initializing, an error was returned and the connection handle was not being closed. Any attempts to close the connection would throw an error indicating the connection was already closed, even though it wasn't. Since the handle was left open, any subsequent attempts to dial the connection would cause an actual error.
I just had to make an adjustment in the HangUp code to hide the error thrown when a connection is closed that has already been closed.

Categories