How to handle an array of SQL Error codes - c#

I have a try\catch block that handles opening a connection and inserting data into a database.
catch (SqlException ex)
{
string sqlError = null;
for (int i = 0; i < ex.Errors.Count; i++)
{
sqlError += "Error Message " + ex.Errors[i].Message + "\n" + "Stored Procedure " + ex.Errors[i].Procedure + " \n " + "Line Number " + ex.Errors[i].LineNumber;
}
LogTools.WriteLog("Sql Server error " + sqlError);
Variables.InstallStatusDetail = "Database connection failed";
if (!sqlError.Contains("connection to SQL Server"))
{
if (Variables.WriteToDatabase)
{ HostedDataBase.InsertRunStatusIntoInstallStatus('E', sqlError, null); }
}
}
I want to log sqlexceptions to the database that wont interfere with connecting and logging to the database. The problem occurs when the database cannot be found, or a login does not have the proper permissions, etc. The exception is raised, and it tries to log to the database, so when it does that, it calls the function that writes to the database and tries to access once again, but once again the same exception is raised, resulting in a loop of failed attempts to write to the database (either because the DSN cannot be found, or the user does not have proper permissions).
How can I handle sql errors that would prevent me from being able to access and write to the database, and at the same time still be able to write sql errors that would not cause this loop?

I'm slightly confused by your question but I will attempt to answer it. You have to be careful with how you handle exceptions. If you are going to attempt to reconnect to the database even though an exception was raised the first time, you may want to have some conditions checking what sort of exception was raised. This way, you know to only attempt to re-connect if it was an exception which will not be repeated over and over.
IE.
catch (SqlException ex)
{
Error = ex.ToString()
WriteToLog(Error);
CheckError();
}
Void CheckError()
{
//conditions based on error output.
}
void WriteToLog(string Error)
{
// write your error output to log
}

You should put your logging in it's own try..catch block, like this:
catch (SqlException ex)
{
string sqlError = null;
for (int i = 0; i < ex.Errors.Count; i++)
{
sqlError += "Error Message " + ex.Errors[i].Message + "\n" + "Stored Procedure " + ex.Errors[i].Procedure + " \n " + "Line Number " + ex.Errors[i].LineNumber;
}
LogTools.WriteLog("Sql Server error " + sqlError);
Variables.InstallStatusDetail = "Database connection failed";
if (!sqlError.Contains("connection to SQL Server"))
{
if (Variables.WriteToDatabase)
{
try {
//I am assuming this is where you are trying to write the error back into the database
HostedDataBase.InsertRunStatusIntoInstallStatus('E', sqlError, null);
} catch {
//ignore errors here
}
}
}
}

Unfortunately if you are writing to the same database that you don't have access to, I'm afraid you cannot do that.
I'd suggest you to use something like Log4net which can have multiple appenders (eventlog, database, file, etc). Also, log4net operates something like a fire and forget. Even if log4net has any issues logging the errors, it won't throw exceptions.

Related

What is the best approach while polling database?

Currently I am doing an assignment in which I need to visit database of n number of servers to fetch some results.I have achieved this by iterating through the list of servers and raising tasks each one for each server in the collection. The task calls a function which basically makes an connection with the database,run query and disconnect from database.
My question is I am doing right by making a new connection on each polling with the database and closing it everytime or is this would be the best approach to keep a db connection open and fetch the result and then keep it open on next polling iteration.
PollingServerTimer() is being called by timer everytime.My polling timer is 3 sec.
Something like this :
private void PollingServerTimer(object sender, ElapsedEventArgs e)
{
foreach (var item in ServerOperationCollHandler)
{
if (item.RebootStatus != true)
{
PushItemIntoQueue(item);
}
}
}
public void PollingServerQueue()
{
while (isRunning)
{
this.waitHandle.WaitOne();
lock (syncRoot)
{
if (ServerQueue.Count > 0)
{
ServerOperationDataModel obj;
try
{
ServerQueue.TryDequeue(out obj);
Task GetCountFromDbTask = new Task(() => GetCountFromDb(obj));
GetCountFromDbTask.Start();
this.waitHandle.Reset();
}
catch (Exception ex)
{
MessageBox.Show("Problem encountered while finding iterim and recovery count");
isRunning = false;
break;
}
}
}
}
}
public void GetCountFromDb(ServerOperationDataModel obj)
{
ServerOperationDataModel serverObject = (ServerOperationDataModel)obj;
DataBaseHandler dbHandler = new DataBaseHandler(serverObject.DataBaseIP, serverObject.DataBasePort, serverObject.DataBaseName, serverObject.DataUserName, serverObject.DataUserPassword);
int attempts = 0;
do
{
try
{
dbHandler.connect();
}
catch (Exception ex)
{
break;
serverObject.DataBaseConnectionStatus = false;
log.Error("Connection attempt " + attempts + " failed.Retrying connection. Exception details :" + ex.ToString());
attempts++;
}
} while (attempts < _connectiontRetryAttempts && !dbHandler.isConnected());
if (dbHandler.isConnected())
{
/*Fetch Result and then get disconnect*/
dbHandler.disConnect();
}
else
{
//string msgLog = "Server : " + obj.ServerComponentIdentifier + " | " + obj.IPstring + "Connection cannot be established with the DB: " + obj.DataBaseIP + " | "+ obj.DataBasePort + " | " + obj.DataBaseName + " after a series of retries";
//LoggerUpdate.LogMessage(msgLog, LOGTYPE.POLLINGDATABASE, LoggerUpdate.ReturnLogDisplayObject(DateTime.Now, obj.ServerComponentIdentifier + "|" + obj.IPstring, Convert.ToInt16(LOGTYPE.POLLINGDATABASE), obj, msgLog));
}
}
I would not be concerned at all. Assuming you connect to the SQL Server (or a similar, enterprise DBMS), database connections are pooled at the client side which means that establishing the connection is costly only the first time the client connects to a particular db (formally: to a new, previously unseen connection string) and then each connection to the same database costs almost nothing.
If it hadn't been for pooling, applications servers would not be able to handle hundreds of concurrent browser connections that query the same data source. You would need much more than a connection every 3 seconds to cause any risk of depleting server or client resources.
You can read more on how pooling works
https://learn.microsoft.com/en-us/dotnet/framework/data/adonet/sql-server-connection-pooling
A side note: You should polish your code a little bit.
For example, you have
GetCountFromDb(ServerOperationDataModel obj)
but then
ServerOperationDataModel serverObject = (ServerOperationDataModel)obj;
Why would you need to cast the obj to another variable of the very same type?
In the catch clause, you have break and some code below it which looks unreachable.
Take a look at the .NET SqlDependency object. This allows you to register a query with a database and, using an OnChange handler, receive notification whenever the result of the query changes.

How can I determine the precise exception when a connection to SQL Server fails (C#)?

I have a (Windows Forms) app that will be installed on various users' desktops; it will allow them to generate reports based on custom code that connects to a SQL Server Database and reads records from certain tables.
The Connection String is:
Data Source=PLATYPUS;Initial Catalog=Duckbills;Persist Security Info=True;User ID=lilabner;Password=d0GpAtCh42;Connect Timeout=120
I understand this to mean that if all the following is true:
The user's machine has the SQL Server client software installed
The SQL Server client has been configured to access the PLATYPUS database
The table "Duckbills" exists in that database
The username and password are what is expected
...then the connection will be successful.
In the event any of the above equate to false, I want to show the user a "user-friendly" message informing them, in plain English, what the problem is and what to do about it. How can I test for these various problems so that the most appropriate message is shown the user in the event of connection failure.
Here is the pertinent existing code:
DataSet dsUsage = new DataSet();
SqlConnection conn =
new SqlConnection("SERVER=PLATYPUS;DATABASE=Duckbills;UID=lilabner;PWD=d0GpAtCh42;Connection Timeout=0");
SqlDataAdapter da = new SqlDataAdapter();
SqlCommand cmd = conn.CreateCommand();
cmd.CommandText = "Exec sp_ViewPlatypi";
da.SelectCommand = cmd;
conn.Open();
da.Fill(dsUsage);
conn.Close();
DataTable dtUsage = dsUsage.Tables[0];
if (dtUsage.Rows.Count > 0)
{
foreach (DataRow productUsageByMonthDataRow in dtUsage.Rows)
{
. . .
catch (Exception ex)
{
String exDetail = String.Format(PlatypusConstsAndUtils.ExceptionFormatString, ex.Message, Environment.NewLine, ex.Source, ex.StackTrace);
MessageBox.Show(exDetail);
}
As you can see, I have a "catch all" (no pun intended) Catch block. I want something like:
catch (SQLServerException sex)
{
MessageBox.Show("SQL Server not available - go tell the DBA");
}
catch (NoTableException ntex)
{
MessageBox.Show("Go tell the DBA there's no such table");
}
catch (BadPwdException sex)
{
MessageBox.Show("Your username and/or password are bad - go tell it to the Marines");
}
catch (Exception ex)
{
String exDetail = String.Format(PlatypusConstsAndUtils.ExceptionFormatString, ex.Message, Environment.NewLine, ex.Source, ex.StackTrace);
MessageBox.Show(exDetail);
}
...but I don't know, first if all, if it's even possible to get that granular with connection exception messages, and secondly - if it is - just what the corresponding Exception types are.
Strip your code back to handle Exception (catch (Exception ex)). Then, put a break point in your catch block. Attach the debugger to your code and when it hits the catch block, drag the ex variable in to your watch window. There, you will see all the details of the exception and you can determine what you need to be able to better handle the various exceptions that come up.
Based on MethodMan's suggestion and TheShaman's link, I adapted that code to this:
catch (SqlException sex)
{
for (int i = 0; i < sex.Errors.Count; i++)
{
String sexDetail = String.Format("SQL Exception #{0}{1}Source: {2}{1}Number: {3}{1}State: {4}{1}Class: {5}{1}Server: {6}{1}Message: {7}{1}Procedure: {8}{1}LineNumber: {9}",
i+1, // Users would get the fantods if they saw #0
Environment.NewLine,
sex.Errors[i].Source,
sex.Errors[i].Number,
sex.Errors[i].State,
sex.Errors[i].Class,
sex.Errors[i].Server,
sex.Errors[i].Message,
sex.Errors[i].Procedure,
sex.Errors[i].LineNumber);
MessageBox.Show(sexDetail);
}
}
catch (Exception ex)
{
String exDetail = String.Format(UsageRptConstsAndUtils.ExceptionFormatString, ex.Message, Environment.NewLine, ex.Source, ex.StackTrace);
MessageBox.Show(exDetail);
}
And for an example of what this produces:

Simulate error "The underlying connection was closed: The connection was closed unexpectedly."

I'm running a long process which updates some opportunities 1 by 1 in Microsoft Dynamics CRM 4. Occasionally this will stop midway through with the error: "The underlying connection was closed: The connection was closed unexpectedly." As it's intermittent it's something I think happens due to network blips but not certain. In all circumstances if I restart the process (which kicks off where it left off) it starts again straight away.
I've written the following code to try and cope with this scenario as I want this to be an overnight process (1 off):
int Retries = 0;
bool Ready = false;
while (!Ready && Retries < 5)
{
try
{
using (CrmService service = GetCrmServiceInstance())
{
service.Update(opp);
Ready = true; //break out of the while loop as connection is working.
}
}
catch (SoapException se)
{
Retries++;
if (Retries > 4)
{
throw new Exception("Error occurred updating opportunity " + opp.opportunityid.Value + ". Error: " + se.Detail.InnerXml);
}
}
catch (Exception ex)
{
Retries++;
if (Retries > 4)
{
throw new Exception("Error occurred updating opportunity " + opp.opportunityid.Value + ". Error: " + ex.Message);
}
}
}
My theory is that if it initially gets this error that it will retry to do the update and hopefully work again. If the number of retries exceeds 4 then throw an error.
I now want to test this code to see if it adequately works for my purposes. I've tried running the long process (6 hours) to see if I get the error, with break points on the SoapException and Exception blocks to be able to step through if it breaks on doing an update but it didn't. So, is there a way to simulate this error so I can test my new code?
One way to simulate a connection failure would be to restart the CRM application pool, which is CrmAppPool by default.

Getting error message in try catch (C#, Window Service)

Is it possible to get error message in try catch on C# (or window service that was written with C#) like this:
string input = Console.ReadLine();
while (input!="q")
{
try
{
double result = 10 / int.Parse(input);
Console.WriteLine("Divinced by " + input + " And result is " + result);
}
catch (Exception)
{
Console.WriteLine("Error, Please try again");
}
finally
{
input = Console.ReadLine();
}
}
It was built to ".exe" program.
If I input character ("A","B","C") or zero, then the program will show message "Error, Please try again" so I need to know error message.
I don't want to edit code. I need a tools to detect all error in program.
Thank you for your helping.
You want the exception's Message property to be printed. Replace your catch block with this:
catch (Exception e)
{
Console.WriteLine("An exception occurred : " + e.Message);
}

CreateUserWizard step changing issue after account creation

I want to change the createuserwizard.step= start if the mail sending fails and not to go to successful creation step.
catch (SmtpException ex)
{
Membership.DeleteUser(textboxemail.Text.Trim());
Literal errorMessage=(Literal) CreateUserWizard1.CreateUserStep.ContentTemplateContainer.FindControl("ErrorMessage");
errorMessage.Text = "Account creation failed due to email notification."+ ex.Message + " errorcode" +ex.StatusCode + "; inner exception; " + ex.InnerException;
CreateUserWizard1.CreateUserStep.StepType = WizardStepType.Start;
}
but the exception says the steptype can't be changed. So how to do this. I mean to stop from going to success step.
You are getting that exception because you need to use the Wizard.MoveTo method.
Like this:
CreateUserWizard1.MoveTo(WizardStep1);
Where "WizardStep1" is the ID of the asp:WizardStep that you want to go back to (the "start" step).

Categories