I've got a logger that records exception information for our in house applications.
When we log SQL exceptions it'd be super useful if we could see the actual query that caused the exception.
Is there a way we can achieve this?
The SqlException does not hold a reference to the SqlCommand that caused the exception. In your logger there is no way to do this. What you could do is catch the SqlException in the method that executes the SqlCommand and wrap it in a more descriptive exception. Example:
using (var command = new SqlCommand(connection, "dbo.MyProc"))
{
try
{
command.Execute();
}
catch (DbException ex)
{
throw new InvalidOperationException(ex.Message + " - " + command.Text, ex);
}
}
This way you can log this more expressive exception.
You can NOT throw a sql exception. I think he meant to throw a new Exception that contains the command.CommandText.
As a simple hack you can also add the sql as part of the Data of the exception. This will preserve the original exception but also give the additional sql message.
using (var command = new SqlCommand(connection, "dbo.MyProc"))
{
try
{
command.Execute();
}
catch (SqlException ex)
{
ex.Data.Add("Sql",command.Text);
throw ex
}
}
The DRYest way to do this is to write a helper method that takes a delegate, the sql command text, and optionally a sql parameter array if you're using parameterized queries. Wrap the delegate in a try catch block and call the LogError method when there is an exception:
protected virtual TResult ExecuteAndLogError<TResult>(Func<TResult> code, string sql, SqlParameterCollection parameters = null)
{
try {
if ((System.Diagnostics.Debugger.IsAttached))
PrintSqlToDebug(sql, parameters);
return code();
} catch (Exception ex) {
LogError(sql, parameters, ex);
throw;
}
}
In my SQL code I call ExecuteAndLogError from data layer helper methods. All of the data layer methods call ExecuteAndLogError, so there is only one section of code to log SQL errors.
public virtual DataTable ExecuteDataTable(SqlCommand command, params SqlParameter[] parameters)
{
command.Parameters.AddRange(parameters);
DataTable table = new DataTable();
using (SqlDataAdapter adapter = new SqlDataAdapter(command)) {
using (command) {
ExecuteAndLogError(() => adapter.Fill(table), command.CommandText, command.Parameters);
}
}
return table;
}
You can use it like this: repo.ExecuteDataTable("SELECT * FROM Users"); If there is an exception you can implement the LogError method to perform additional logging.
Some of this code was taken from the Subtext Blog data layer classes.
Related
I am working on a MVC web page that edits a SQL DB table. In my controller, I have a DB call to increment an entity table. Then if successful, creates a new row in my target table (not the entity table).
The problem I am running into is I keep getting the following error:
The ConnectionString property has not been initialized.
However this only happens after the entity table has been incremented. Not sure where to go on this, so I am hoping that by posting some code, someone would be able to help me find my error.
so here is the obligatory code:
My SQL Connection:
private SqlConnection con;
public BaseRepository()
{
con = new SqlConnection(ConfigurationManager.ConnectionStrings["SqlServerConnection"].ToString());
}
My Entity Table Increment Call:
public int GetNextId()
{
try
{
using (con)
{
DynamicParameters dynParam= new DynamicParameters();
dynParam.Add("#entity_name", "insert_object ");
con.Open();
var value = con.Execute(SP_GET_NEW_ID, dynParam, commandType: CommandType.StoredProcedure);
con.Close();
return value;
}
}
catch (Exception ex) { throw ex; }
}
Finally, here is the Row Insert Code:
public int InsertRowCode(InsertObject ccModel, UserModel appUser)
{
var value = GetNextId();
if (value == 1)
{
try
{
using (con)
//this is where the code breaks and jumps the the exception ex in my catch
{
con.Open();
var dP = new DynamicParameters();
//(add 14 dynamic Parameters here)
var result = con.Execute(SP_SAVE_CORRECTION_CODES, dP, commandType: CommandType.StoredProcedure);
con.Close();
return result;
}
}
catch (Exception ex)
{
throw ex;
}
}
else { throw new Exception("Busted"); }
}
Any help is greatly appreciated. TIA
Don't use shared connection objects.
When you exit this block:
using (con)
{
//...
}
That connection object is now disposed and can't be used anymore. Don't worry about trying to optimize your connections, the connection pool does a very good job of that already. Create your connection objects where you need them, use them, and dispose them in a tight scope:
using (var con = new SqlConnection(connectionString))
{
//...
}
As a side note, this is superfluous:
catch (Exception ex)
{
throw ex;
}
That catch block isn't doing anything for you, and is actually deleting important information about the exception. Just remove that try/catch entirely.
If, on the other hand, you ever do want to do something with an exception before re-throwing it, just use the keyword throw by itself:
catch (Exception ex)
{
// log something, etc.
throw;
}
This would allow the exception to continue up the stack unmodified, preserving the actual error information.
I have a method that returns a List. Now I want to know how to place the try/catch blocks properly. If I place the return statement inside try I get error
Not all code paths return a value
If I place after catch(like I'm doing currently) it will return the products even after an Exception. So what should be the best way?
Here is the method:
public List<Product> GetProductDetails(int productKey)
{
List<Product> products = new List<Product>();
try
{
using (SqlConnection con = new SqlConnection(_connectionString))
{
SqlCommand cmd = new SqlCommand("usp_Get_ProductDescription", con);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#riProductID", productKey);
con.Open();
using (SqlDataReader reader = cmd.ExecuteReader())
{
while (reader.Read())
{
Product product = new Product(reader["Name"].ToString(), reader["Code"].ToString());
products.Add(product);
}
}
}
}
catch { }
return products;
}
Remove the complete Try and Catch blocks. Apparently you are unable to handle the exceptions in the GetProductDetails method so just let them be thrown.
However the calling code can make the decision:
IList<Product> products = null;
try
{
products = GetProductDetails(3);
}
catch(Exception ex)
{
// Here you can make the decision whether you accept an empty list in case of retrieval errors.
// It is the concern of this method, not of the ProductDetails method.
// TODO: Use logging
products = new List<Product>();
}
I can imagine it feels like code duplication if you have to write this in every method using the GetProductDetails method. However consider, when you have X implementations, you want to react differently to not being able to get product details. You will have to come up with workarounds. You might even end up with weird bugs which are hard to troubleshoot.
That depends on what should happen in an exceptional case. If this might happen for some reason which isn´t "bad enough" to let the app crash or if you are able to handle that exception appropriately then you might go with your current appraoch - however you should definitly leave at least a log-message within the catch-clause that contains the error which has been thrown:
catch (Exception e)
{
log.Info(e.Message);
}
This way you get all results within your list except those that caused any exceptions. You can simply continue work with all the results that you got and ignore those errorous (supposing you logged them in any way).
If it is a really unexpected behaviour (which is the intended one for exceptions, this is why they are called exceptions) you should delete all this try/catch from within your method and handle any exceptions outside the method as Maurice already mentioned.
At the moment, you don't return anything if an exception is thrown.
Use try, catch, finally. (May you want to see the MSDN page for more official information)
try
{
//try to execute this code
}
catch
{
//execute this if an exception is thrown
}
finally
{
//execute this code, after try/catch
}
So if you place your return statement into the finally section, you will return your list even if there's an exception thrown...
In c#, can I catch all errors about (non) connectivity to an Oracle database?
I don't want to catch error about badly written query but only errors like No listener, connection lost...
If queries are badly written (or table are missing) then this is my fault.
But if Oracle or the network is down then this should be held by another department.
Write your code in which you build the connection in a try catch part:
try
{
BuildConnection(connectionString);
}
catch (OracleException ex)
{
//Connectivity Error
}
Errors between ORA-12150 to ORA-12236 are related to connection errors. A few examples:
ORA-12154: TNS:could not resolve the connect identifier specified
ORA-12152: TNS:unable to send break message
ORA-12157: TNS:internal network communication error
Please refer to https://docs.oracle.com/cd/E11882_01/server.112/e17766/net12150.htm
Simple answer for this Type of problem is Use Try Catch Block like
try
{
// your code
}
catch (OracleException ex)
{
}
MSDN HELP
Sure - you can catch specific exception types, or if they're all the same exception type, you can catch it, check to see if it's a specific type, and re-throw ones you don't want to handle. Not having your syntax, here's an example...
try
{
// your Oracle code
}
catch (OracleException ex)
{
if (ex.Message == "Something you don't want caught")
{
throw;
}
else
{
// handle
}
}
errors like No listener, connection lost are still caught in System.Data.SqlClient.SqlException, however, you may inspect ErrorCode and Errors to handle different situations accordingly, say, not listener or connection lost etc.
MSDN does not seem to document all possible errors, however, you may write a few unit tests or integration tests to learn what appear in ErrorCode and Errors, then write error handlers in production codes accordingly.
OracleException contains only ErrorCode not Errors. So you may be using switch(e.ErrorCode) to handle different situations.
I observed that each time a network exception occurs, then a SocketException can be found in inner exceptions.
I also observed that when a network exception occurs, the first inner exception is of type «OracleInternal.Network.NetworkException» but unfortunately, this class is internal...
Based on this observations, I would code something like this:
public void RunQuery()
{
try
{
var con = new OracleConnection("some connection string");
con.Open();
var cmd = con.CreateCommand();
// ...
cmd.ExecuteNonQuery();
}
catch (Exception ex) when (IsNetworkException(ex))
{
// Here, a network exception occurred
}
catch (Exception ex)
{
// Here, an other exception occurred
}
}
private static bool IsNetworkException(Exception ex)
{
var exTmp = ex;
while (exTmp != null)
{
if (exTmp is SocketException)
return true;
exTmp = exTmp.InnerException;
}
return false;
}
in my function i am returning a datatable that is returned by method
MyDatatable(sqlQuery, connection ),
but it may generate some exceptions that will threw error can i do something like this
return try {cmn.MyDatatable(qry, con)} catch(Exception ex){null};
i don't want to do this way
DataTable dt =null;
try{
dt = cmn.MyDatatable(qry, con);
}catch().....
return dt==null ? null :dt;
Your syntax is quite wrong. You can do it this way:
try
{
return cmd.MyDatatable(qry, con);
}
catch(Exception ex)
{
return null;
}
Although I doubt you want to swallow all exceptions.
IMO, the best way is to do that :
{
object l_oOutDatatable = null;
try
{
oOutDatable = cmd.MyDatatable(qry, con);
}
catch(Exception e)
{
log.Message(e.ToString);
}
return l_oOutDatatable;
}
You must ALWAYS manage exception, because they must have a good reason to be thrown ;). More over it's consider as a good practice to have a single return in each method.
And in bonus, you can use the ?? keyword : This keyword is meaning to return the value or something else if it's null. as Example : c = a ?? b is similar to this : c = (a!=null) ? a : b;
You should return the exception all the way to the client which is calling this code. It is up to the client to handle the exception.
Remember you should only be catching exceptions where you expect them to occur, such as trying to connect to a db or write a file to disk etc.
You should also make your exception catches as specific as possible to catch a know exception that normally occurs when you get the dataset.
try
{
return cmd.MyDatatable(qry, con);
}
catch(Exception ex)
{
//Log exeption
Throw ex
}
If this is just a check to handle null datatables when they have no results then you should test if they are null or populated. This would be a better solution if it is part of your applications ability to return empty datatables.
var dataTableResult = cmd.MyDatatable(qry, con);
if ( dataTableResult != null)
{
return dataTableResult;
}
return null;
I am working on a generic class and am working on handling errors. I am using a try catch on one spot where I am getting an error. The question is, how do I return that error back to the calling method?
public static DataTable GetData(string connString, string sqlStatement, Action<iDB2ParameterCollection> parameters)
{
DataTable dt = new DataTable();
using (iDB2Connection conn = new iDB2Connection(connString))
{
using (iDB2Command cmd = new iDB2Command(sqlStatement, conn))
{
conn.Open();
if (parameters != null) { parameters(cmd.Parameters); }
try
{
using (iDB2DataAdapter da = new iDB2DataAdapter(cmd)) { da.Fill(dt); }
}
catch (iDB2SQLErrorException e)
{
}
conn.Close();
}
}
return dt;
}
By not catching it in a base class!
I am not a fan of capturing and swallowing exceptions at the base class level.
Let your derived classes worry about these details.
Side Note (Evidence of position):
You'll notice that in practically any API, the doucmentation will report what exceptions will be thrown with classes. If they were to catch them in a base class, they have effectively swallowed them rendering you helpless as the user of said classes.
Additional Articles:
...instead of writing our abstractions based on details, the we should
write the details based on abstractions.
This is a core tenant of Dependency Inversion Principle.
Take a look at this article for some really good things to consider in your design process, http://www.oodesign.com/design-principles.html
We do this, for the same reason you appear to be doing it. So that you can ensure the connection is closed.
We just re-throw the same error and lose the connection in the "finally" block. This lets the connection be closed and still bubble the connection back up to the caller, because the "finally" block gets executed regardless.
catch (iDB2SQLErrorException e)
{
throw e;
}
finally
{
cn.Close();
}
The above code is what we've used for years, but thanks to the comments, I think it might need tweaking. See this blog post for info on how to preserve the stack trace with exception handling: http://weblogs.asp.net/fmarguerie/archive/2008/01/02/rethrowing-exceptions-and-preserving-the-full-call-stack-trace.aspx
You could implement some logic to handle to exception internally in this method and re-throw it again. The exception will bubble up in the call stack;
Other option is to use error codes to pass the error up in the stack. It depends on the API.
Either don't catch it and let the caller handle it, or throw your own error that wraps the original one:
class DataRetrievalException : Exception {
DataRetrievalException(String message, Exception cause) : base(message, cause) {}
}
// ...
catch (iDB2SQLErrorException e) {
throw new DataRetrievalException("Error retrieving data from database", e);
}