A fast and efficient way to get the needed Inner Exception - c#

In an application using Entity Framework 6, I track the changes made on tables in the database in a specially designed entity. They track the changes on all tables, including their own table.
So if an exception occurs while saving changes in the database, I delete all of the pending tracking entities in order to avoid creating new and new trackers recursively, log the exception and exit the saving method.
However, if the exception is caused due to connection timeout, I try 3 times to resave the changes, while changing the EntityState of the tracking entities to avoid creating unnecessary trackers. In order to accomplish that, I need to catch a DbUpdateException, get the SqlException down the hierarchy of exceptions, and check its number. However, I'm not sure how deep is the SqlException in the hierarchy. To accomplish successfully getting the Sql Exception, I wrote this:
catch (DbUpdateException duEx)
{
var inner = new Exception();
inner = duEx;
do
{
inner = inner.InnerException;
}
while (!(inner is SqlException) && inner != null);
var innerEx = inner as SqlException;
if (innerEx != null && innerEx.Number == -2)
{
//do job here
}
I tested it and it seems to work, however it looks a bit clumsy. So my question is: Is there any way of getting the SqlException, if any, directly?

What I was wondering is whether there is already some 3rd party
extension method which I could use
No, but you can create it yourself:
public static class Helper
{
public static TException GetInnerException<TException>(this Exception ex) where TException : Exception
{
return ex.InnerException != null
? ex.InnerException as TException ?? GetInnerException<TException>(ex.InnerException)
: null;
}
}
And use it :
catch (DbUpdateException duEx)
{
if (duEx.GetInnerException<SqlException>()?.Number == -2)
{
//do job here
}
}

Related

C# Retry Policy: How to check what exception you receive as a variable?

I have a retry policy created for SQL Exceptions but it seems to not be retrying properly.
I am currently trying to debug and I want to create a temporary code line to test if the exception is a SQL Exception:
if (exception == SQLException) then bool correct = true;
But how would I create an exception variable?
I am currently causing the exception by using RAISERROR('test', 16, 1); in the stored procedures in the database and also creating a SQL timeout.
Just want to check if the exception I'm receiving is a SQL Exception or if it's not even registering.
Thank you
Not sure enough about the context, but if you have the exception object, then try the is operator
if (ExceptionObject is SqlException )
{
//run the retry logic for SqlException
}
The details are here:
https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/type-testing-and-cast
Just catch the SqlException and check for the Class and State properties:
if (exception is SqlException sqlException)
{
if(sqlException.Class == 16 && sqlException.State == 1)
{
}
}

My custom DbExecutionStrategy is not being called

My original problem was that I was experiencing deadlocks often when updating my SQL database. Through a little bit of research, I found that I'm able to define a custom DbConfiguration and with it a DbExecutionStrategy which instructs Entity Framework to automatically retry after getting certain errors after x milliseconds and y number of times. Great!
So, following the guide at https://msdn.microsoft.com/en-us/data/jj680699, I build my custom DbConfiguration, which is being used, but the associated DbExecutionStrategy seems to be ignored.
Originally, my entire DbConfiguration was being ignored, but I found that was because I was referencing it in my app.config as well as decorating my entity constructor with the DbConfigurationType attribute [DbConfigurationType(typeof(MyConfiguration))]. Now that I'm only using the app.config, at least my custom configuration is being called.
In its simplest form, my custom config looks like this:
public class MyConfiguration : DbConfiguration
{
public MyConfiguration()
{
System.Windows.MessageBox.Show("Hey! Here I am!"); //I threw this in just to check that I was calling the constructor. Simple breakpoints don't seem to work here.
SetExecutionStrategy("System.Data.SqlClient", () => new MyExecutionStrategy(3, TimeSpan.FromMilliseconds(500)));
}
}
My custom DbConfiguration is referenced in my app.config like so:
<entityFramework codeConfigurationType="MyDataLayer.MyConfiguration, MyDataLayer">
...
</entityFramework>
My custom DbExecutionStrategy is built like so:
private class MyExecutionStrategy : DbExecutionStrategy
{
public MyExecutionStrategy() : this(3, TimeSpan.FromSeconds(2))
{
System.Windows.MessageBox.Show($"MyExecutionStrategy instantiated through default constructor.");
}
public MyExecutionStrategy(int maxRetryCount, TimeSpan maxDelay) : base(maxRetryCount, maxDelay)
{
System.Windows.MessageBox.Show($"MyExecutionStrategy instantiated through parametered constructor.");
}
protected override bool ShouldRetryOn(Exception ex)
{
System.Windows.MessageBox.Show($"Overriding ShouldRetryOn.");
bool retry = false;
SqlException sqlException = GetSqlException(ex);
if (sqlException != null)
{
int[] errorsToRetry =
{
1205, //Deadlock
-2 //Timeout
};
if (sqlException.Errors.Cast<SqlError>().Any(x => errorsToRetry.Contains(x.Number)))
{
retry = true;
}
}
if (ex is TimeoutException)
{
retry = true;
}
return retry;
}
}
I'm not hitting anything at all in this particular piece of the code.
One thing that may be of note, is that every example I've seen so far (for example http://blog.analystcircle.com/2015/08/01/connection-resiliency-in-entity-framework-6-0-and-above/) has casted the exception in ShouldRetryOn directly to a SqlException using
SqlException sqlException = ex as SqlException;
I found that using this method always resulted in a null SqlException because my program is throwing an EntityException which can't be cast into a SqlException. My underlying SqlException is actually the inner exception of the inner exception of the EntityException. So, I put together a short recursive call to dig in and find it.
private SqlException GetSqlException(Exception ex)
{
SqlException result = ex as SqlException;
if (result == null && ex.InnerException != null)
result = GetSqlException(ex.InnerException);
return result;
}
This works properly, but the fact that I need to do it when the examples I've found don't is probably a clue as to what's going wrong. Do EntityExceptions not trigger a DbExecutionStrategy? If not, why is this listed as a solution to be used with EF 6? Any insight would be much appreciated.
EDIT:
Doing some more digging into the source for DbExecutionStrategy (https://github.com/aspnet/EntityFramework6/blob/master/src/EntityFramework/Infrastructure/DbExecutionStrategy.cs), I've found that my recursive function to find my SqlException from the EntityException is unnecessary. DbExecutionStrategy has a function UnwrapAndHandleException which does just that and passes the SqlException on to ShouldRetryOn. So, it seems I'm right back at square one.
EDIT 2:
Not really a solution, because it doesn't explain why my DbExecutionStrategy isn't being called as it should, but I have found that if I explicitly call the execution strategy, it works.
The code to use the execution strategy explicitly is:
var executionStrategy = new MyConfiguration.MyExecutionStrategy();
executionStrategy.Execute(
() =>
{
//build your context and execute db functions here
using (var context = new Entities())
{
...do stuff
}
});
Probably way too old by now but in case anyone is having the same issues:
exception.GetBaseException() gets you the root cause of any exception. No need for recursion
I'm able to get this to work using EF 6.4.0

How to rethrow exceptions in a program with multiple layers?

I have an MVC EF5 setup, with classes:
Program - this is the controller
UserInterface - this is the view, responsible for displaying and prompting for data.
DataAccess - Model, this Creates, Reads, Updates, and Deletes data in my EF model classes
When the DataAccess class tries to do a CRUD operation on my database, if it catches an error, it needs to be handled, my UserInterface class needs to print messages to the user, reporting any errors if neccessary. So, when an error happens, it needs to go through the program class first, then to the UserInterface class, because data layer shouldn't directly communicate to the presentation layer.
It was suggested to me that I don't pass or return the exception to a calling function, but that I should "throw a new simpler exception to the layers above". All this talk about exceptions is confusing to me because My experience with exceptions is limited to this format:
try
{
// stuff
}
catch (exception ex)
{
console.writeline(ex.ToString());
}
I've done some of my own research to try and find the answer to this problem, and I've learned a few things but don't know how to put it all together:
I learned:
throw; rethrows an exception and preserves the stack trace
throw ex throws an existing exception, such as one caught in a catch block. and resets the stack trace.
There is a property called Exception.StackTrace. I understand that each time an exception is thrown, the frames in the call stack are recorded to the Exception.StackTrace property.
However, I don't know where to place my try/catch blocks to utilize rethrowing
Is it something like the following code? Or am I missing the point on how this works?
EDITED: (added a little more to make sense of this guesswork to others)
void MethodA()
{
try
{
MethodB();
}
catch (MyExceptionType ex)
{
// Do stuff appropriate for MyExceptionType
throw;
}
}
void MethodB()
{
try
{
MethodC();
}
catch (AnotherExceptionType ex)
{
// Do stuff appropriate for AnotherExceptionType
throw;
}
}
void MethodC()
{
try
{
// Do Stuff
}
catch (YetAnotherExceptionType ex)
{
// Do stuff appropriate for YetAnotherExceptionType
throw;
}
}
There is more than how you use different type of exception handling. Functionally you should define what layers has to do what with a exception.
Like data layer => dont throw anything other than DataException or SQLException. Log them and throw back a generic database exception back to UI.
Business layer => log and rethrow simple bussiness exception
UI layer => catch only business exception and alert it in a message inside business exception
Once all this is defined, you can use what you have learned and summarized in question to build this.
What (I think) was suggested you do by throw a new simpler exception is that you translate the exceptions from the lower layers into new, higher level exceptions for consuming in the outer layers. The lower level exceptions are not suitable for consumption at the upper levels of the program.
For example, in LINQ to Entities, the method Single() will throw an InvalidOperationException when the sequence has no elements. However, this exception type is very common, so catching it in the user interface levels is hard to do: how would you differentiate between different possibilities of this exception being thrown (for example, modifying a read-only collection)? The solution is to translate the exception into another (new, user-defined) type that the application can easily handle.
Here is a simple example of the idea:
public class MyUserService {
public User GetById(int id) {
try {
using(var ctx = new ModelContainer()) {
return ctx.Where(u => u.Id == id).Single();
}
}
catch(InvalidOperationException) {
// OOPs, there is no user with the given id!
throw new UserNotFoundException(id);
}
}
}
Then the Program layer can catch the UserNotFoundException and know instantly what happened, and thus find the best way to explain the error to the user.
The details will depend on the exact structure of your program, but something like this would work in an ASP.NET MVC app:
public class MyUserController : Controller {
private MyUserService Service = new MyUserService();
public ActionResult Details(int id) {
User user;
try {
user = Service.GetById(id);
}
catch(UserNotFoundException) {
// Oops, there is no such user. Return a 404 error
// Note that we do not care about the InvalidOperationException
// that was thrown inside GetById
return HttpNotFound("The user does not exist!");
}
// If we reach here we have a valid user
return View(user);
}
}

Can I combine try catch blocks?

I have the following code:
public IDictionary<string, string> GetNextBase36(string partitionKey, string rowKey, ref string seq)
{
Sequence sequence;
try {
sequence = _sequenceRepository.Get(u => u.PartitionKey == partitionKey & u.RowKey == rowKey);
} catch {
_errors.Add("", "Database error: Get sequence failed");
}
try {
sequence.Value = Base36.Encode(Base36.Decode(sequence.Value) + 1);
_sequenceRepository.AddOrUpdate(sequence);
seq = sequence.Value;
} catch {
_errors.Add("", "Database error: Updating sequence failed");
}
return _errors;
}
It works but it seems overkill to have each database access surrounded by a try catch block. Is there some way I could simplify this? Is there any beter approach?
Updated code based on help / advice:
public IDictionary<string, string> GetNextBase36(string partitionKey, string rowKey, ref string seq)
{
Sequence sequence;
string catchMsg = string.Empty;
try {
catchMsg = "Database error: Get sequence failed"
sequence = _sequenceRepository.Get(u => u.PartitionKey == partitionKey & u.RowKey == rowKey);
sequence.Value = Base36.Encode(Base36.Decode(sequence.Value) + 1);
catchMsg = "Database error: Updating sequence failed"
_sequenceRepository.AddOrUpdate(sequence);
seq = sequence.Value;
} catch {
_errors.Add("", catchMsg);
}
return _errors;
}
You are sacrificing exact exception stack traces on the altar of error message readability (not to mention code readability).
Consider your audience: a developer. A developer should be able to read an exception stack trace, which is much more valuable debugging information than a short error message.
Hence my suggestion is to only put in one exception handler for logging somewhere higher up the hierarchy. If you have to have one in your current method, at least capture the full exception stack trace.
Also - does it really make sense to continue after the first exception? If retrieving the sequence fails, using its value in the next try block won't fair any better. It does not make sense having multiple try/catch blocks here, unless you know exactly how to solve the problem in each catch handler so you can continue.
Update:
Now that you have a single try/catch block code readability is improved. It still looks very odd that you are basically returning a list of errors to the caller - the error case should be exceptional, so it should not bleed into the business logic of your method, instead I would just let the exception go up the stack until you can truly handle the problem (or just log it and exit the app).
Your code should ideally look something like this (I will be inventing class names here and there): i.e. you should have one try block and multiple handlers which depend on the type of exception raised.
The reason why you should have only one try block is that, as pointed out by others, the two queries are related, and you should not perform the update if the select has gone wrong.
public IDictionary<string, string> GetNextBase36(string partitionKey, string rowKey, ref string seq)
{
Sequence sequence;
try {
sequence = _sequenceRepository.Get(u => u.PartitionKey == partitionKey & u.RowKey == rowKey);
sequence.Value = Base36.Encode(Base36.Decode(sequence.Value) + 1);
_sequenceRepository.AddOrUpdate(sequence);
seq = sequence.Value;
} catch( SelectException e ) {
_errors.Add("", "Database error: Get sequence failed");
} catch( UpdateException e ) {
_errors.Add("", "Database error: Updating sequence failed");
} catch {
_errors.Add("", "Database error: Something really bad happened!");
}
return _errors;
}
If you fail to get the item from the database, does it make sense that you should attempt to update it? Wrap the whole thing in a single try { ... } catch () block if you intend to handle/translate exceptions at that point in your code.

Annoying SQL exception, probably due to some code done wrong

I started working on this "already started" project, and I'm having a really annoying error when trying to execute some interactions with SQL Server 2008:
The server failed to resume the
transaction. Desc.:
One of these errors I get in this specific method call:
The aspx.cs Call:
busProcesso openProcess = new busProcesso(pProcessoId);
try
{
if (openProcess.GetDocument() == null)
{
//Irrelevant code.
}
}
catch{ //... }
The Business class (relevant part):
public class busProcesso : IbusProcesso
{
public Processo vProcesso { get; set; }
RENDBDataContext db;
public busProcesso()
{
vProcesso = new Processo();
}
public busProcesso(decimal pProcessoId)
{
db = new RENDBDataContext();
try
{
vProcesso = db.Processos.SingleOrDefault(x => x.Id == pProcessoId);
}
catch (Exception ex)
{
throw new Exception(ex.Message, ex);
}
}
public string GetDocument()
{
try
{
string document = null;
foreach (Processo_has_Servico ps in ListaServicosProcesso())
{
if (ps.Servico.Document != null) //Get the error right at this line.
{
document = ps.Servico.Document;
}
}
return document ;
}
catch (Exception ex)
{
throw new Exception(ex.Message, ex);
}
}
public IQueryable<Processo_has_Servico> ListaServicosProcesso()
{
db = new RENDBDataContext();
try
{
return from ps in db.Processo_has_Servicos
join s in db.Servicos on ps.Servico_Id equals s.Id
where ps.Processo_Id == vProcesso.Id
select ps;
}
catch (Exception ex)
{
throw new Exception(ex.Message, ex);
}
}
}
As I said, the error occurs right at the line:
if (ps.Servico.Document != null) from the GetDocument() method.
Opening SQL Server Activity Monitor, I see there is a process for my database (.Net SqlClient Data Provider)
After some time/use (when I start to get the "server failed to resume the transaction" error), I go to the SQL Server Activity Monitor and there's around 5 or 6 more identical processes that weren't killed and (probably) should've been. When I manually kill them, the error stops for a while, until it starts again.
I'm not really good at working in OO and all, so I'm probably missing something, maybe some way to close one of these connections. Also, any help/tip about this structure will be welcome.
PS. The error doesn't happen everytime. Sometimes it runs just perfectly. Then it starts to give the error. Then it stops. Sometimes it happens just once.. pretty weird.
The code in ListaServicosProcesso is creating the context db. Then it is returning an IQueryable.
At this point no request has been sent to the database.
Then there is a for each in the code. At this point EF says "I need to get the data from the database". So it tries to get the data.
But the context db is now out of scope, so it crashes, on the first line that tries to use the data.
There are 2 ways to get around this:
return a list from ListaServicosProcesso, this will force the database call to execute
move the for each into ListaServicosProcesso
Edit
Pharabus is correct db is not out of scope. The problem is here:
db = new RENDBDataContext();
A new instance of the context is being created without the old one being disposed. Try Dispose of db at the end of ListaServicosProcesso. Even better place db in a using statement. But then the foreach must be moved inside the using statement.
Here's a couple of ideas to try.
1/ You can attach SQL server profiler to see the query that is being executed, which will allow you to copy and paste that query to see the data that is in the database. This might be help.
2/ You never check whether ps.Servico is null - you jump straight to ps.Servico.Document. If ps.Servico is null then you will get a null reference exception if you try to access any properties on that object.
I'm not sure of the exact cause of the error you're seeing (if you Google it, the references are all over the place...), but there are a few things you could improve in your code and I've found that just cleaning things up a bit often makes problems go away. Not always, but often.
I agree with the other answerers that it would help to keep better track of your DataContext(s). For example in you're creating it once in the constructor, then again in ListaServicosProcesso(). At that point vProcesso is on one DataContext and other entities will be on another, which gets messy.
I think you could simplify the whole thing a bit, for example you could combine GetDocument() and ListaServicosProcesso() like this:
public string GetDocument()
{
try
{
// Are you sure vProcesso is not null?
if (vProcesso == null)
return null;
// Only create the context if it wasn't already created,
if (db == null)
db = new RENDBDataContext();
return db.Processo_has_Servicos
.Where(ps => ps.Processo_Id == vProcesso.Id && ps.Servico.Document != null)
.Select(ps => ps.Servico.Document) // use an implicit join
.SingleOrDefault();
}
catch (Exception ex)
{
throw new Exception(ex.Message, ex);
}
}

Categories

Resources