I have implemented SqlClient connecting function with timeout parameter. It means, that connection.open() is in another thread and after thread is started, I'm checking elapsed time. If the time had reached timeout, thread is aborted and no connection is established.
The thing is, that if I have timeout bigger then default connection.open() timeout, open() throws SqlException, which isn't caught in my Catch(SqlException) block.
I'm starting whole connecting process in another thread:
public void connect()
{
Thread connectThread = new Thread(waitForTimeout);
connectThread.Start();
}
Connecting thread -> starts another thread for timeout waiting
public void waitForTimeout()
{
connection = new SqlConnection(connectString);
Thread timeoutThread = new Thread(openConnection);
timeoutThread.Start();
DateTime quitTime = DateTime.Now.AddMilliseconds(timeout);
while (DateTime.Now < quitTime)
{
if (connection.State == ConnectionState.Open)
{
transac = connection.BeginTransaction();
command = connection.CreateCommand();
command.Transaction = transac;
break;
}
}
if (connection.State != ConnectionState.Open)
timeoutThread.Interrupt();
}
Here exception isn't caught after open() default timeout:
private void openConnection()
{
try
{
connection.Open();
}
catch(SqlException ex)
{
// This isn't caught anytime
}
}
Thank you for any ideas!
Isn't it caught or isn't it thrown? Thread.start only schedules the thread for running but doesn't mean it will start immediately. Maybe code runs till threadTimeout interruption, then openConnection starts and always succeed to open the connection within default timeout.
---edit
In this case maybe could you try to:
replace SqlException by Exception and check if you catch something such as ThreadInterruptedException instead
put the content of openConnection method in your waitForTimeout method right after connection = new SqlConnection(connectString); (and comment the rest of the code) and see if exception is still not handled. If not, then put it in the connect() method and check again.
After comments I tried to implement new solution, and here it is:
Constructor:
/* ------------------------------------------------------------------------- */
public Database(string source, string database, string user, string password,
int timeout, DelegateConnectionResult connectResult)
{
this.timeout = timeout;
this.connectResult = connectResult;
connectString = "server=" + source +
";database=" + database +
";uid=" + user +
";pwd=" + password +
";connect timeout=" + Convert.ToInt16(timeout / 1000);
}
Asynchonous connect:
/* ------------------------------------------------------------------------- */
public void connectAsync()
{
connectThread = new Thread(connectSync);
connectThread.Start();
}
Synchronous connect:
/* ------------------------------------------------------------------------- */
public void connectSync()
{
connection = new SqlConnection(connectString);
try
{
connection.Open();
transac = connection.BeginTransaction();
command = connection.CreateCommand();
command.Transaction = transac;
if (connection.State == ConnectionState.Open)
connectResult(true);
else
connectResult(false);
}
catch
{
connectResult(false);
}
}
And I found the solution for original problem witch SqlException from this post: it has something to do with Exceptions settings in debugger of Visual Studio. If I unchecked "throw" choice at SqlException in Exceptions list (CTRL + D + E in Visual Studio), I'm finally able to catch exception.
Related
i have an notification application built in c# for notifying any change in database . the application runs in the background . But the problem is after the start of the application if the internet is switched off the application throws an SQLException . i have used try catch to handle the exception . but i want my application to try connecting the database and when the connection is established it returns to the main code .
try
{
using (SqlConnection connection =
new SqlConnection(GetConnectionString()))
{
//i want to return here when the connection is reestablished
using (SqlCommand command =
new SqlCommand(GetListenerSQL(), connection))
{
connection.Open();
// Make sure we don't time out before the
// notification request times out.
command.CommandTimeout = NotificationTimeout;
SqlDataReader reader = command.ExecuteReader();
while (reader.Read())
{
messageText = System.Text.ASCIIEncoding.ASCII.GetString((byte[])reader.GetValue(13)).ToString();
// Empty queue of messages.
// Application logic could parse
// the queue data and
// change its notification logic.
}
object[] args = { this, EventArgs.Empty };
EventHandler notify =
new EventHandler(OnNotificationComplete);
// Notify the UI thread that a notification
// has occurred.
this.BeginInvoke(notify, args);
}
}
}
catch(SqlException e)
{
}
is it possible to do it without goto statement . i would prefer avoiding the goto statement .
I would move the retry logic out of the query method. I've seen a good retry library somewhere, but I can't find it just now.
public void StartListener()
{
var message = GetMessage();
//process message in some way
object[] args = { this, EventArgs.Empty };
EventHandler notify = OnNotificationComplete;
this.BeginInvoke(notify, args);
}
private const int TimeoutStep = 2000;
private const int MaxTimeout = 10000;
private string GetMessage(int timeout = 0)
{
//prevent loop of endless retries
if (timeout >= MaxTimeout)
{
//optional: define your own Exception class
throw new MaxTimeoutException();
}
try
{
Thread.Sleep(timeout);
return GetMessageFromDatabase();
}
catch (SqlException ex)
{
//log ex in debug mode at least
return GetMessage(timeout + TimeoutStep);
}
}
private string GetMessageFromDatabase()
{
string message = null;
using (var connection = new SqlConnection(GetConnectionString()))
{
using (var command = new SqlCommand(GetListenerSQL(), connection))
{
connection.Open();
command.CommandTimeout = NotificationTimeout;
using (var reader = command.ExecuteReader())
{
while (reader.Read())
{
message = System.Text.ASCIIEncoding.ASCII.GetString((byte[])reader.GetValue(13));
}
}
}
}
return message;
}
from the suggestion of #Archer i got the solution . in the catch block i call the method again which uses this connection after some suitable time . Something like
public void StartListener()
{
try
{
using (SqlConnection connection =
new SqlConnection(GetConnectionString()))
{
//i want to return here when the connection is reestablished
using (SqlCommand command =
new SqlCommand(GetListenerSQL(), connection))
{
connection.Open();
// Make sure we don't time out before the
// notification request times out.
command.CommandTimeout = NotificationTimeout;
SqlDataReader reader = command.ExecuteReader();
while (reader.Read())
{
messageText = System.Text.ASCIIEncoding.ASCII.GetString((byte[])reader.GetValue(13)).ToString();
// Empty queue of messages.
// Application logic could parse
// the queue data and
// change its notification logic.
}
object[] args = { this, EventArgs.Empty };
EventHandler notify =
new EventHandler(OnNotificationComplete);
// Notify the UI thread that a notification
// has occurred.
this.BeginInvoke(notify, args);
}
}
}
catch(SqlException e)
{
Thread.Sleep(2000);
StartListener();
}
}
If its an failure you should call an timer and the timer should call the backgroundworker.
Write the Functionality to check the connection in the backgroundworkers. if it goes for true you should stop the timer. And call the usual process
I am testing both service broker external activator and polling based client on behalf of process speed performances of each.
For external activator, I have built a command line application which is being notified when any change occur some table and writes to the same db. Code inside exe looks like as follows
private static void ProcessRequest()
{
using (var connection = new SqlConnection(ServiceConstant.ConnectionString))
{
connection.Open();
do
{
using (var tran = connection.BeginTransaction())
{
//Get a message from the queue
byte[] message = QueueProcessorUtil.GetMessage(ServiceConstant.QueueName, connection, tran, ServiceConstant.WaitforTimeout);
if (message != null)
{
MessageReceiving = true;
try
{
//Write it to the db
ProcessMessage(message);
}
catch (Exception ex)
{
logger.Write("Fail: " + ex);
}
tran.Commit();
}
else
{
tran.Commit();
MessageReceiving = false;
}
}
}
while (MessageReceiving);
}
}
When I insert 20 messages to the queue, total duration of insertion of all the messages is approx 10ms
When I extract the ProcessMessage function above which writes the messages to the db to an another separate console application and then call this function 20 times as follows, this time it takes approx 50ms
class Program
{
static void Main(string[] args)
{
for (var i = 1; i <= 20; i++)
{
string message = "mm";
ProcessMessaage(message);
}
}
}
ProcessMessage function
string sql = #"INSERT INTO [Workflow].[dbo].[TestOrderLog]([OrderId],[RecordTime])
VALUES (#orderId, GETDATE()) SELECT SCOPE_IDENTITY()";
using (SqlConnection con = new SqlConnection(ConfigurationManager.AppSettings["SqlConnection"].ToString()))
using (SqlCommand com = new SqlCommand(sql, con))
{
con.Open();
com.CommandType = CommandType.Text;
com.Parameters.AddWithValue("#orderId", 1);
try
{
var result = com.ExecuteScalar();
var id = (result != null) ? Convert.ToInt32(result) : 0;
}
catch (Exception ex)
{
throw ex;
}
con.Close();
}
I don't understand and am surprised although there are costly processing blocks (query a message) inside the loop of external activator code, it takes faster to write db than pure loop in console app code.
Why would pure insertion in a loop be slower than insertion inside the external activator exe instance's code?
Side note, in EAService.config file, <Concurrency min="1" max="1" />
It was an absurd mistake of mine, first one is compiled and deployed running code
and the second one is running with debugger inside visual studio, so the intervals became normal running without debugger.
Updated question for Ping Async: Large amount of pings in async task - getting Exception "An asynchronous call is already in progress."
I've got a Windows Service that grabs a list of hosts from a MySQL Database and reports back if the host was pingable. This works great for small amounts of IP's, but I'm expecting about 200+ IP's at a time, and one or two hosts being down is causing a lot of lag. My quick and dirty solution was opening the ping's in a thread, so that the loop can continue to run if one host is down or has a slow network... It works.. but not well. I'm new at this, but I know enough to realize this can't be the best way to do it. Any help/guidance would be greatly appreciated!
try
{
MySqlConnection Connection = new MySqlConnection(ConnectionString);
Connection.Open();
MySqlCommand query = Connection.CreateCommand();
query.CommandText = "SELECT obj_id AS id,ip FROM objects LIMIT 200";
MySqlDataReader Nodes = query.ExecuteReader();
// Record in log that a NEW iteration is starting - for tracking issues with pings
Library.WriteErrorLog("######################## New Iteration ########################");
int i = 1;
while(Nodes.Read())
{
// Open a new thread for each ping. There are over 2000 host objects in the database - if we do not open in seperate threads we will never finish
// each thread will ping the hosts and results will be logged in log.txt and MySQL
string Host = (i == 5 ? "google.com" : Nodes["ip"].ToString());
Host = (i == 4 ? "lindevenv" : Host);
int HostID = int.Parse(Nodes["id"].ToString()); // Obj -> string -> int because super awesome .NET logic
Thread thread = new Thread(() => UpdateStatus(Host, HostID, ConnectionString, i));
thread.Start();
i++;
}
Connection.Close();
}
catch(Exception ee)
{
Library.WriteErrorLog("Error: " + ee.ToString());
}
and...
// Called by the thread as the worker method inside CheckStatus Method below
private void UpdateStatus(string Host, int HostID, string ConnectionString, int loopID)
{
try
{
Ping pinger = new Ping();
PingReply reply = pinger.Send(Host, 3000);
if (reply.Status == IPStatus.Success)
{
Library.WriteErrorLog("(" + loopID + ") " + Host + " is UP!");
}
else
{
Library.WriteErrorLog("(" + loopID + ") " + Host + " is DOWN!");
Library.ReportDown(HostID, ConnectionString);
}
}
catch (PingException e)
{
// Do not throw exception - a pingexception is thrown when there is a timeout/dns/other issue - report as down
Library.WriteErrorLog("(" + loopID + ") " + Host + " is DOWN!");
Library.ReportDown(HostID, ConnectionString);
}
}
In addition to being extemely memory heavy, some information gets left out/duplicated between threads which makes it very unreliable.
I want to synchronize my local and web database so i have written a stored procedure using linked server. My stored procedure executes fine and data synchronization is successful but the procedure takes around 7-10 minutes to get executed. The exact timing cannot be determined. So whenever the procedure runs on my windows application then the page seems as if it has become unresponsive though the process is still going on.
So i am having a "Data Sync" button on my page on click of which i want the progress bar to display the progress of the stored procedure. For the time being I am taking the average of last few execution timings to define the time duration for which the stored procedure runs. Now the problem is that when i click on the data sync button then the progress bar doesn't work. Kindly help me with this issue.
My code is as follows:-
namespace RMS
{
public partial class DataSync : Form
{
connection con = new connection();
SqlCommand cmd = new SqlCommand();
static int rowCount;
static int syncTime;
static int timeSlice;
public DataSync()
{
InitializeComponent();
}
private void btnDataSync_Click(object sender, EventArgs e)
{
// Start the asynchronous operation.
backgroundWorker1.RunWorkerAsync();
try
{
con.GetConnectLive();
con.GetConnect();
if (con.CnLive.State == ConnectionState.Open)
{
MessageBox.Show("Connection to Live Server Successful!!!...Data Synchronisation may take several minutes so do not cancel the operation while in execution mode");
btnDataSync.Enabled = false;
btnDataSync.Text = "Please Wait...";
string Str = "RMS_LocalToLive";
cmd = new SqlCommand(Str, con.Cn);
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandTimeout = 1200;
rowCount = cmd.ExecuteNonQuery();
if (rowCount > -1)
{
MessageBox.Show("Total no. of rows synchronised = " + rowCount);
btnDataSync.Text = "Success";
}
else
{
MessageBox.Show("Data Synchronisation couldn't be completed because of connection problem... Please try again!!!");
}
}
else
{
MessageBox.Show("Unable to connect to Live Server...Please check your internet connection and try again!!!");
}
con.GetDisConnect();
con.GetDisConnectLive();
}
catch (Exception ex)
{
MessageBox.Show("Please check your internet connection and try again!!!");
}
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
try
{
con.GetConnect();
string Str = "RMS_DataSyncTime";
cmd = new SqlCommand(Str, con.Cn);
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandTimeout = 1200;
syncTime = Convert.ToInt32(cmd.ExecuteScalar().ToString());
timeSlice = syncTime / 100;
con.GetDisConnect();
}
catch (Exception ex)
{
MessageBox.Show("Unable to retrieve last Data Synchronisation Timing");
}
for (int i = 1; i <= synctime; i=i+timeslice)
{
Thread.Sleep(timeslice);
// Report progress.
backgroundWorker1.ReportProgress(i);
}
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
// Change the value of the ProgressBar to the BackgroundWorker progress.
progressBar1.Value = e.ProgressPercentage;
// Set the text.
this.Text = e.ProgressPercentage.ToString() + "% Completed";
}
private void DataSync_Load(object sender, EventArgs e)
{
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgse)
{
}
}
}
The main issue here is that, while you are executing your progress bar updates in the BackgroundWorker's thread, the ReportProgress() updates never make it to the UI thread, because you've blocked that thread with the main SQL operation.
Instead of doing that, you should do something more like this:
private void btnDataSync_Click(object sender, EventArgs e)
{
// Start the asynchronous operation.
backgroundWorker1.RunWorkerAsync();
btnDataSync.Enabled = false;
btnDataSync.Text = "Please Wait...";
bool success = false;
try
{
// Execute the query asynchronously
success = await Task.Run(() => ExecuteLocalToLive());
}
catch (Exception ex)
{
MessageBox.Show("Please check your internet connection and try again!!!");
}
btnDataSync.Enabled = true;
btnDataSync.Text = success ? "Success" : "Failure";
}
private bool ExecuteLocalToLive()
{
bool success = false;
con.GetConnectLive();
con.GetConnect();
if (con.CnLive.State == ConnectionState.Open)
{
MessageBox.Show("Connection to Live Server Successful!!!...Data Synchronisation may take several minutes so do not cancel the operation while in execution mode");
string Str = "RMS_LocalToLive";
cmd = new SqlCommand(Str, con.Cn);
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandTimeout = 1200;
rowCount = cmd.ExecuteNonQuery();
if (rowCount > -1)
{
MessageBox.Show("Total no. of rows synchronised = " + rowCount);
success = true;
}
else
{
MessageBox.Show("Data Synchronisation couldn't be completed because of connection problem... Please try again!!!");
}
}
else
{
MessageBox.Show("Unable to connect to Live Server...Please check your internet connection and try again!!!");
}
con.GetDisConnect();
con.GetDisConnectLive();
return success;
}
I have rearranged the code that handles the button state and text, so that it's still executed in the UI thread where it belongs, even though the method itself is not. You also never appeared to set the button back to the enabled state; it's not clear to me whether that was intentional or not, so I went ahead and added a line to do that.
Finally, I will strongly recommend you figure out a better way to report status to the user than the calls to MessageBox.Show() you have now. The biggest issue is that you don't even start doing any work until after the user dismisses the initial message, which immediately puts your progress bar out of sync with the actual work. But it's also better to keep all your UI in the UI thread, and to keep UI separate from non-UI logic (i.e. the SQL operation).
All, I have successfully used ADO.NET to make use of asynchronous SQL queries similar to the example below. In the example shown the method ExecNonQuery is being invoked from the UI thread. This works well, but I wondered how I would handle the callback if I were to call ExecNonQuery from a non-UI thread?
Note. Clearly, in such a case I would amend ExecNonQuery, so that such things as this.toolStripStatusLabel1.Text were dealt with accordingly, or removed.
public bool ExecNonQuery(string strCmd, string strUserMsg = "")
{
try
{
SqlCommand cmd = new SqlCommand();
cmd.Connection = conn;
cmd.CommandText = strCmd;
cmd.CommandTimeout = 0;
bIsExecuting = true;
AsyncCallback callback = new AsyncCallback(HandleCallback);
cmd.BeginExecuteNonQuery(callback, cmd);
return true;
}
catch (Exception Ex)
{
bIsExecuting = false;
this.toolStripStatusLabel1.Text = String.Format("Ready (last error: {0})", Ex.Message);
if (conn != null)
conn.Close();
}
return false;
}
private delegate void DisplayInfoDelegate(string Text);
private void HandleCallback(IAsyncResult result)
{
try
{
// Retrieve the original command object, passed
// to this procedure in the AsyncState property
// of the IAsyncResult parameter.
SqlCommand command = (SqlCommand)result.AsyncState;
int rowCount = command.EndExecuteNonQuery(result);
string rowText = " rows affected.";
if (rowCount == 1)
rowText = " row affected.";
rowText = rowCount + rowText;
// Call the procedure from the form's thread.
DisplayInfoDelegate del = new DisplayInfoDelegate(DisplayResults);
this.Invoke(del, rowText);
}
catch (Exception ex)
{
// Because you are now running code in a separate thread,
// if you do not handle the exception here, none of your other
// code catches the exception.
// You can create the delegate instance as you
// invoke it, like this:
this.Invoke(new DisplayInfoDelegate(DisplayResults),
String.Format("Ready(last error: {0}", ex.Message));
}
finally
{
bIsExecuting = false;
if (conn != null)
conn.Close();
}
}
private void DisplayResults(string Text)
{
this.toolStripStatusLabel1.Text = Text;
this.toolStripProgressBar1.Style = ProgressBarStyle.Blocks;
this.toolStripProgressBar1.Value = 100;
}
Thanks for you time.
It makes no difference to your callback which thread runs ExecNonQuery - HandleCallback will still be run on a thread pool thread.
You have already spotted the change you need to make: don't access UI controls directly in ExecNonQuery if it is not being run on the UI thread.
Nick