SqlTransaction causing application crash on error? - c#

Here's some background: Periodically our site will crash to the point of having to restart IIS; this almost always happens within an hour of patching a DLL (we use a Web Site project, not a Web Application project, so each ASPX page is a separate DLL).
In doing some research I have found that our homebrew DAL can, while debugging, cause the built-in webserver with Visual Studio to actually stop working and be shut down if it encounters a SQL error in a stored procedure (I mean it will not only throw an exception that is displayed in the browser, it will actually say that the web server has experienced an error and needs to close!)
In digging further the error seems to be related to the use of transactions for everything (including Select statements) in the DAL. What seems to happen is this:
Tries to execute stored procedure, stored procedure fails due to missing/invalid column or other error.
Application code catches the error and rethrows it (bad, yes, but I didn't write this).
Transaction tries to commit despite the exception, gets a NullReferenceException on the transaction.Commit() line (seems to be on the Connection property because there is a transaction object). Also this NullRef seems like it cannot be caught (I tried a demo that force crashed with an invalid Sproc and the NullRef was never caught even though outputting the error gave its type as System.NullReferenceException)
Transaction throws error that say something like "The transaction has completed and is no longer usable".
??? but the VS web server crashes. Debugging this part seems to hang on the above exception, never leaving the method.
Now, I don't know if this is what causes IIS to crash, but it seems quite suspicious and it's a glaring error in any event.
Having not dealt with transactions before and having only the basic idea of them, my first question is why the transaction is still trying to commit after an exception is being thrown? My second question is how to fix the failing commit and presumably infinite looping of exceptions until the server dies. Wouldn't it make sense to add something like this (the method takes a SqlTransaction parameter named transaction):
catch (SqlException se)
{
if (transaction != null)
{
transaction.Rollback();
}
throw;
}
Would that small change fix the constant exception loop that I think is crashing IIS? The DAL itself is extremely brittle and is used concretely in hundreds of files so I can't rewrite it from scratch correctly.
EDIT The entire code block is this (again, legacy code - uses the old microsoft data access block helper):
public static DataSet ExecuteDatasetStoredProc(SqlConnection conn, String storedProcName, SqlTransaction transaction, params SqlParameter[] storedProcParms)
{
try
{
// Execute the stored proc
if (transaction != null)
{
return SqlHelper.ExecuteDataset(transaction, CommandType.StoredProcedure, storedProcName, storedProcParms);
}
else
{
return SqlHelper.ExecuteDataset(conn, CommandType.StoredProcedure, storedProcName, storedProcParms);
}
}
catch (SqlException se)
{
throw new ApplicationException("Error calling " + storedProcName + ". " + se.Message, se);
}
}
However, if the catch block executes the transaction still tries to commit and this seems to be causing the hangups.

also Change your if you wrap your Transactional code in a try catch
Try
{
// your code that you assign and execute the SQl
}
catch (SQLException sqlex)
{
try
{
//try to do the rollback here.. don't always assume the commit or rollback will work
}
catch (Your SQL Exception ex)
{
}
}

Related

How to know if transaction scope was successful or not

I need to know if my transaction scope was successful or not. As in if the records were able to be saved in the Database or not.
Note: I am having this scope in the Service layer, and I do not wish to include a Try-Catch block.
bool txExecuted;
using (var tx = new TransactionScope())
{
//code
// 1 SAVING RECORDS IN DB
// 2 SAVING RECORDS IN DB
tx.Complete();
txExecuted = true;
}
if (txExecuted ) {
// SAVED SUCCESSFULLY
} else {
// NOT SAVED. FAILED
}
The commented code will be doing updates, and will probably be implemented using ExecuteNonQuery() - this returns an int of the number of rows affected. Keep track of all the return values to know how many rows were affected.
The transaction as a whole will either succeed or experience an exception when it completes. If no exception is encountered, the transaction was successful. If an exception occurs, some part of the transaction failed; none of it took place
By considering these two facts (records affected count, transaction exception or no) you can know if the save worked and how many rows were affected
I didn't quite understand the purpose of txExecuted- if an exception occurs it will never be set and the if will never be considered. The only code that will thus run is the stuff inside if(true). I don't see how you can decide to not use a try/catch and hope to do anything useful with a system that is geared to throw an exception if something goes wrong; you saying you don't want to catch exceptions isn't going to stop them happening and affecting the control flow of your program
To be clear, calling the Complete() method is only an indication that all operations within the scope are completed successfully.
However, you should also note that calling this method does not
guarantee a commit of the transaction. It is merely a way of informing
the transaction manager of your status. After calling this method, you
can no longer access the ambient transaction via the Current property,
and trying to do so results in an exception being thrown.
The actual work of commit between the resources manager happens at the
End Using statement if the TransactionScope object created the
transaction.
Since you are using ADO.NET, ExecuteNonQuery will return the number of rows affected. You can do a database lookup after the commit and outside of the using block.
In my opinion, its a mistake not to have a try/catch. You want to catch the TransactionAbortedException log the exception.
try
{
using (var scope = new TransactionScope())
{
using (var conn = new SqlConnection("connection string"))
{
}
// The Complete method commits the transaction. If an exception has been thrown,
// Complete is not called and the transaction is rolled back.
scope.Complete();
}
}
catch (TransactionAbortedException ex)
{
// log
}

Does a transaction Rollback() have any chance to throw an exception?

Question related to managing trasactions with EF Core:
Does an IDbContextTransaction.Rollback() have any chance to return an exception, so that it could ever make sense to place it inside a try block?
i.e.
try
{
//...
if(condition)
transaction.Commit();
else
throw methodReturn.Exception;
}
catch (Exception e)
{
try // Does this try make any sense?
{
transaction.Rollback();
}
catch
{
// Log
}
throw e;
}
PD: In this specific situation it is not possible to embed the transaction into a using statement, so a manual .Rollback() call is required in the real code.
Most of the times it is not until:
Deadlock Detection
No Internet Connectivity
Logically and Physically consistent connection
Shutdown, Power failure, Unexpected termination
To tackle these situations, you can:
Use the Xact_Abort setting. When SET XACT_ABORT is ON, if a Transact-SQL statement raises a run-time error, the entire transaction is terminated and rolled back.
When SET XACT_ABORT is OFF, in some cases only the Transact-SQL statement that raised the error is rolled back and the transaction continues processing. Depending upon the severity of the error, the entire transaction may be rolled back even when SET XACT_ABORT is OFF. OFF is the default setting.
Catch and handle the error, and specify a rollback within the error handling

Should we put try catch with function if we just want to log the exception?

In asp.net application, all the exception that occurs and are not inside try catch can be handled by application_error.
If we just need to log the exception along with its stack trace, and we need not make any other decision/logic inside catch, why should we put try catch at application/bl or dal layer functions? Is there any reason to put try/catch with every database call function?
For example we have hundreds of function in DAL layer that executes following code:
try
{
//open db connection, execute stored procedure
}
catch
{
//log error
}
In case we get any exception from stored procedure OR in opening database connection, we get an exception but we are not doing anything except for logging these errors. We don't have very critical data-storage/retrieval requirement. We are logging error just to be alerted and fix it later. Is this correct to put catch in every such function?
Using try and catch is not for logging purposes only, especially when dealing with database connections.
An exception means that something wasn't completed. If something wasn't completed, your business process failed. If your business process failed, you need to know about it and handle it within the scope of that code, not application_error. Each error should be handled within the scope it was generated from. application_error should be your last fallback, and theoretically should never be reached.
Sure, you can use it for logging, but also for closing your DB connection (which was probably opened before the exception occurred and be left forever open), informing your users that an exception occured, and for data recovery, alternating your process to deal with the exception or preparing it for a retry.
So, taking your posted template, good code handling should look like this:
try
{
//open db connection, execute stored procedure
}
catch
{
// Inform the user
// Alternate your process or preparing for retry
// log error
}
finally
{
// Close the DB connection
}
One should use try/catch blocks only in places where you can meaningfully handle an exception. However, "meaningful handling " includes providing good error messages.
If your catch block simply logs the exception with no additional context, then such block could be replaced with a top-level handler (like application_error) that does the same thing.
If, however, you log additional information available only at the point of invocation, then having a catch block is entirely justified: it enhances the experience by providing better diagnostics, which is a perfectly legitimate goal.

Exception Handling error when saving to database

Iam a newbie who needs help. When saving to a database, i get the following error:
A first chance exception of type 'System.Data.SqlServerCe.SqlCeException' occurred in (name of my application)
Please help me with steps to solve this one. Thank you in advance.
First chances exceptions will be shown if you have your Visual Studio settings set so that every CLR exceptions gets reported. This will include exceptions you actually handle.
In Visual Studio's menu, go to Debug > Exceptions and uncheck the Thrown option for Common Language Runtime Exceptions.
Of course that won't make the actual exception go away but you'll be allowed to handle it as you want:
try
{
// do your query
// commit current transaction if possible/necessary
}
catch (SqlCeException ex)
{
// handle exception here and rollback current transaction if possible/necessary
}
finally
{
// ensure proper disposal of any commands and connections you have
}
It goes without saying that you must ensure your query is properly written and that the server objects it tries to work with exists. You generally won't want to handle cases where a comma is missing or a field is not found, for instance. Exception handling must be for exceptional situations your code cannot control, like a faulted connection, a server malfunction, etc.
First of all you might want to check whehter your SQL query is syntactically correct.
Secondly you might want to read about how to handle exceptions in MSDN http://msdn.microsoft.com/en-us/library/0yd65esw.aspx with try-catch statement.
A small example looks like this.
try
{
// ... code which throws exception
}
catch (SqlCeException e)
{
// ... exception handling, e.g. logging, rolling back data,
// telling the user something went wrong, you name it
}
Last but not least you might want to debug your application step by step to see what is causing the error and what the actual SQL Query is throwing the exception.

SQL Insert Query not executing without throwing any exception in c#

I wrote an insert function for a discussion forum website to insert new discussion threads in database. The function is as follows:
public int createDiscuss(Discussion dis)
{
query = "insert into [MyForums].[dbo].[Discussions](desid, text, date, time, replyto, uid, isPrivate) values(#id, #text, #date, #time, #replyto, #uid, #pvp)";
string did=getDesID();
if (did == null)
return -1;
try
{
com = new SqlCommand(query, con);
com.Parameters.AddWithValue("#id", did);
com.Parameters.AddWithValue("#text", dis.gettext());
com.Parameters.AddWithValue("#date", SqlDateTime.Parse(DateTime.Now.ToString()));
com.Parameters.AddWithValue("#time", SqlDateTime.Parse(DateTime.Now.ToString()));
com.Parameters.AddWithValue("#replyto", dis.getreplyto());
com.Parameters.AddWithValue("#uid", dis.getuid());
if (dis.IsPrivate() == false)
{ com.Parameters.AddWithValue("#pvp", 1); }
else
{ com.Parameters.AddWithValue("#pvp", 2); }
con.Open();
int r=com.ExecuteNonQuery();
if (r <= 0)
{
return -1;
}
con.Close();
}
catch (Exception)
{
return -1;
}
return 0;
}
This function if encounters an error or exception than it return -1 and which is checked by the calling function and then the calling function shows error. The problem is that this insert function is not executing query. When I checked that if the values are properly passing or not to the AddWithValue function as an argument, I found that every value is passed as it was given on the asp.net page. But when control comes to com.ExecuteNonQuery() function. It is returning -1 that is error. Can anyone tell me whats wrong with my code? or how can I check why com.ExecuteNonQuery() not returning any effected row??
This exception showing
A first chance exception of type 'System.Threading.ThreadAbortException' occurred in mscorlib.dll
An exception of type 'System.Threading.ThreadAbortException' occurred in mscorlib.dll but was not handled in user code
You should close your connection in a finally block. Any exception you encounter before con.Close(); will result in your connection being left open. That could be the error here (depending on the scope of con)
You should not swallow the exception, but log it. Then you can examine the errors properly. If you don't have logging, get logging. But in the meantime, look at the exception you have caught using the debugger.
You should also catch more specific exceptions, so that you can tell the difference between a connectivity error and a chronic bug in the application. Check which exceptions are likely to be thrown by the methods you are calling. For example, con.Open() can throw these:
SqlException
InvalidOperationException
So catch those first, and catch Exception last.
Depending on the type of exception you can return a different error code and/or log at a different level of severity.
Also, figure out a way (using profiling or just copy+paste) to run that query against the database and see what happens. For example, are you sure this part is right:
com.Parameters.AddWithValue("#date",
SqlDateTime.Parse(DateTime.Now.ToString()));
com.Parameters.AddWithValue("#time",
SqlDateTime.Parse(DateTime.Now.ToString()));
#date and #time are identical. Are the database values identical too and are the columns both expecting strings?
That being said, SQL Profiler is your friend here. Grab the generated sql from profiler and run it in SQL mgmt studio. I am 99% confident the answer will be obvious. If not, then please update your answer with the generated sql from profiler, and we'll take it from there.
If you need some guidance on SQL Profiler, a simple Google search brings up many options: SQL Profiler Search
You need to do some basic debugging. Setup your code like:
catch(Exception ex)
{
return -1;
}
Set a break point on return, and see what the Exception is.
you are probably not reaching the int r=com.ExecuteNonQuery(); code. Could be an error on the con.Open(); method.
Could you throw a new exception inside your catch and show us the actual error?
A challenge with your current code is that "-1" is returned in several different scenarios:
Exception occurs
result of getDesID is null
INSERT statement fails to insert a row
One simple way to check this would be to return a different number (say -2 or -3) from each point of failure. That way you can determine where the error is being raised without changing your code much or attaching a debugger.
It appears to me that the issue is not likely your SQL INSERT statement - if the statement itself was invalid or violated a database constraint, SQL Server would raise an exception back to you which would be caught by your Exception handler.

Categories

Resources