C# Recreating an exception for testing - c#

How would I go about recreating a "The specified network name is no longer available" exception for testing.
The following code below is trying to copy a file on the same network. If the connection to the network is lost, I would like to recall the CopyFile method and try running it after a couple seconds before throwing the exception. What would be the most easiest way to test this exception?
private void CopyFile()
{
int numberOfExecution = 0;
bool copying = true;
while (copying)
{
copying = false;
try
{
File.Copy(file1, file2);
}
catch (Exception ex)
{
if (ex.Message.Contains("The specified network name is no longer available"))
{
numberOfExecution += 1;
System.Threading.Thread.Sleep(5000);
if (numberOfExecution >= 5)
{
throw ex;
}
copying = true;
}
else
{
throw ex;
}
}
finally
{
}
}
}

The idea is to create a test where File.Copy resolves to a static method you've set up for testing purposes and not to System.IO.File.Copy. In other words, you mock File.Copy.
You can tailor this method to cover all cases; succeed on first try, fail at first and succeed later on, or fail on all tries.
The fact that it doesn't really copy anything and simply returns or throws is irrelevant to the method you are testing.
My advice, use an existing tool to do this; Alexei's comment points you in the right direction.

Related

Proper way to use Try Catch for different situations in C#

I'm working with the following code, but I'm not sure how is the proper way to do it.
try
{
// Do code for Try1
Console.WriteLine("Try1 Successful");
}
try
{
// If try1 didn't work. Do code for Try2
Console.WriteLine("Try2 Successful");
}
try
{
// If try2 didn't work. Do code for Try3
Console.WriteLine("Try3 Successful");
}
catch (Exception)
{
// If try1, 2 and 3 didn't work. print this:
Console.WriteLine("The program failed");
}
What I want is to try 3 different ways of a task, and if the 3 of them fail, print "The program failed", but if one of them is successful, don't do the other ones and continue with the program
Edit:
The task that I am trying to do, is looking for a NETWORK PATH.
The Task 1 will look if a path can be opened, if so OPEN THE DIRECTORY.
If not: Task 2 will look if a second path can be opened, if so OPEN THE DIRECTORY.
If not: Task 3 will look if a third path works, if so OPEN IT.
If not "no paths can be found on this pc"
You can make it without try catch block.
For the simplicity make that Task1, Task2, Task3, have some kind of return types.
For example if they return boolean type. TRUE if Task succededd or FALSE if Task failed.
Or they can return some custom type with boolean result, and string error message. I would not go with nested try catch blocks.
executeTasks() {
Console.WriteLine("Try 1");
if (Task1()) return;
Console.WriteLine("Try 2");
if (Task2()) return;
Console.WriteLine("Try 3");
if (Task3()) return;
Console.WriteLine("The program failed");
}
Hopefully this snippet can be easily adapted to meet your needs.
namespace StackOverflow69019117TryCatch
{
using System;
using System.IO;
public class SomeClass
{
public void MainMethod()
{
var paths = new string[] {
#"C:\Users\otherUser\Documents", // exists but I don't have access to it
#"C:\temp", // exists but doesn't contain the folderToSearchFor subfolder
#"Z:\doesntexist", // doesn't exist
};
foreach (var path in paths)
{
Console.WriteLine($"Trying with path {path}");
if (this.ProcessDirectory(path, "folderToSearchFor"))
{
// We've succeeded so exit the loop
Console.WriteLine($"Succeeded using path {path}");
return;
}
else
{
// We've failed so continue round the loop and hope we succeed next time
Console.WriteLine($"Failed using path {path}");
}
}
}
private bool ProcessDirectory(string directoryPath, string folderToSearchFor)
{
// First, check whether the directory we want to search actually exists.
if (!Directory.Exists(directoryPath))
{
// Then the directory we're trying to search in doesn't exist.
// Return false, no need to incur the overhead of an exception.
Console.WriteLine($"Directory {directoryPath} doesn't exist");
return false;
}
// This doesn't appear to throw an exception if directoryPath isn't accessible to the current user.
// Instead it just returns whatever the current user has access to (which may be an empty array).
var propFolderCandidates = Directory.GetDirectories(directoryPath, $"{folderToSearchFor}*");
// But did it return anything?
// If not then what we're looking for either doesn't exist or the user doesn't have access to it.
if (propFolderCandidates.Length == 0)
{
// Then there's no folder here matching the search path.
// Return false, no need to incur the overhead of an exception.
Console.WriteLine($"Couldn't find folder matching {folderToSearchFor} in {directoryPath}");
return false;
}
var propFolder = propFolderCandidates[0];
// Consider implementng similar checks in Process.Start.
// e.g. if it's reading a file, check whether the file exists first
if (Process.Start(propFolder))
{
Console.WriteLine($"Process.Start succeeded using {directoryPath}");
return true;
}
else
{
Console.WriteLine($"Process.Start failed using {directoryPath}");
return false;
}
}
}
}
As #BionicCode has pointed out in various comments, it's less expensive to check whether an action might throw an exception before performing that action, than it is to perform the action and then handle the exception if it's thrown by the action.
I had to do a bit of digging to establish what happens when Directory.GetDirectories tries to get the subfolders of a folder that the current user doesn't have access to - I was expecting it to throw an exception, but it seems that it doesn't, it just returns an empty array representing the nothing that the current user has access to in that location, so no exception to handle in that scenario.
Throwing and catching of exceptions definitely has its place in .net software, but you should treat it as something to fall back on if something happens which you can't anticipate at design time - if there's a way at design time of detecting that a particular action isn't going to work, then you should detect it and report to the caller that the action they've requested won't work, rather than performing the action and trying to handle any exception it might throw.
There is some wise advice from Microsoft on the subject of best practice for exceptions.
Use exception handling if the event doesn't occur very often, that is, if the event is truly exceptional and indicates an error (such as an unexpected end-of-file). When you use exception handling, less code is executed in normal conditions.
Check for error conditions in code if the event happens routinely and could be considered part of normal execution. When you check for common error conditions, less code is executed because you avoid exceptions.
Hope this is useful :-)
You're gonna have to nest your try-catch blocks:
try {
Console.WriteLine("Try1 successful");
} catch {
try {
Console.WriteLine("Try2 successful");
} catch {
try {
Console.WriteLine("Try3 successful");
} catch {
Console.WriteLine("The program failed");
}
}
}
I have already found a way, to be honest I don't know if this is the best way, but totally works.
The solution was to nest a few try-catch(exception)
This is what I am doing...
try
{
try
{
//Check if PROP can be found inside initial path A
string[] PROP_FOLDER = Directory.GetDirectories(Full_PathA, $"{PROP}*");
Process.Start(PROP_FOLDER[0]);
//Open PROP in path A and RETURN
status_label.Text = " Found it!";
status_label.ForeColor = Color.LimeGreen;
}
catch (Exception) //If an error occurs on path A
{
try
{
//Check if PROP can be found inside initial path B
string[] PROP_FOLDER = Directory.GetDirectories(Full_PathB, $"{PROP}*");
Process.Start(PROP_FOLDER[0]);
//Open PROP in path B and RETURN
status_label.Text = " Found it!";
status_label.ForeColor = Color.LimeGreen;
}
catch (Exception) //If an error occurs on path B
{
//Check if PROP can be found inside initial path C
string[] PROP_FOLDER = Directory.GetDirectories(Full_PathC, $"{PROP}*");
Process.Start(PROP_FOLDER[0]);
//Open PROP in path C and RETURN
status_label.Text = " Found it!";
status_label.ForeColor = Color.LimeGreen;
}
}
}
catch (Exception) //If PROP cannot be found on any of those paths, the PROP doesn't exist
{
status_label.Text = " Not Found!";
status_label.ForeColor = Color.Red;
}

Repeating a function in C# until it no longer throws an exception

I've got a class that calls a SOAP interface, and gets an array of data back. However, if this request times out, it throws an exception. This is good. However, I want my program to attempt to make this call again. If it times out, I'd like it to keep making this call until it succeeds. How can I accomplish this?
For example:
try
{
salesOrdersArray = MagServ.salesOrderList(sessID, filter);
}
catch
{
?? What Goes Here to FORCE the above line of code to rerun until it succeeds.
}
You just need to loop forever:
while (true)
{
try
{
salesOrdersArray = MagServ.salesOrderList(sessID, filter);
break; // Exit the loop. Could return from the method, depending
// on what it does...
}
catch
{
// Log, I suspect...
}
}
Note that you should almost certainly not actually loop forever. You should almost certainly have a maximum number of attempts, and probably only catch specific exceptions. Catching all exceptions forever could be appalling... imagine if salesOrderList (unconventional method name, btw) throws ArgumentNullException because you've got a bug and filter is null... do you really want to tie up 100% of your CPU forever?
You must place the try/catch block inside a loop construct. If you wish not to consume 100% of your processor place a Thread.Sleep in the catch block, so everytime an exception occurs, it will wait some time, freeing the processor to do other things.
// iterate 100 times... not forever!
for (int i = 0; i < 100; i++)
{
try {
// do your work here;
break; // break the loop if everything is fine
} catch {
Thread.Sleep(1000);
}
}
You could also specify exception type, so that only the timeout exception is handled, and other kinds of exceptions pass-through.
// iterate 100 times... not forever!
for (int i = 0; i < 100; i++)
{
try {
// do your work here;
break; // break the loop if everything is fine
} catch (TimeOutException) {
Thread.Sleep(1000);
}
}
Note that, TimeOutException should be replaced by the real name of the exception... I don't know if that is the real name.
Also adjust the sleep time, given in millisecs and the amount of repeats, in the case I presented, 100 repeats of 1000ms yields a maximum wait of 1 minute and 40 seconds, plus the operation time itself.
If you can't change the timeout, the below should work. salesOrdersArray should be initialized to null.
while(salesOrdersArray == null)
{
try
{
salesOrdersArray = MagServ.salesOrderList(sessID, filter);
}
catch
{
// Log failure
}
}
It its not gernally a good idead to use exceptions as control flow, but this will do what you requested.
bool Caught = true;
while (Caught)
try
{
salesOrdersArray = MagServ.salesOrderList(sessID, filter);
Caught = false;
}
catch
{
Caught = true;
}
I will use a transactional queue (MSMQ) to store the service call. A loop will dequeue messages and call the service in a TransactionScope, if the call fails the message appear to be still in the queue. An ov erall timeout can be specified by adding a time to expire in the message. This solution is good if you really want a reliable solution since I guessed that calling that operation is critical.
Try
bool failed = false;
do {
try
{
salesOrdersArray = MagServ.salesOrderList(sessID, filter);
}
catch
{
failed = true;
}
} while(failed);
The behavior you are after might cause an endless loop if this never succeeds though...
Try something like this:
var failed = true;
while (failed)
{
try
{
salesOrdersArray = MagServ.salesOrderList(sessID, filter);
failed = false;
}
catch
{
}
}
Edit: Wow! Great minds think alike! :)
Although I would NOT recommend you to do this for an infinite number of times, you could make a separate function out of that one sentence:
void GoConnect()
{
try
{
salesOrdersArray = MagServ.salesOrderList(sessID, filter);
}
catch
{
GoConnect();
}
}
while(salesOrdersArray == null){
try
{
salesOrdersArray = MagServ.salesOrderList(sessID, filter);
}
catch(salesOrderException e)
{
log(e.message);
}
}
This will run forever, and is using exceptions as a loop which is slow. Is there a way you can modify your function that it returns null, instead of throwing an exception? If you're expecting that this call will fail regularly, don't use a try/catch block.
I follow this pattern in order to solve this problem:
public void Send(String data, Int32 attemptNumber)
{
try
{
yourCodeHere(data);
}
catch (WebException ex)
{
if (attemptNumber > 0)
Send(data, --attemptNumber);
else
throw new AttemptNumberExceededException("Attempt number exceeded!", ex);
}
catch (Exception ex)
{
//Log pourpose code goes here!
throw;
}
}
Trying forever seems not to be a good idea as you may end up having an infinite process. If you think you need many attempts to achieve your goal just set huge number here.
I personally think its wise to wait some milliseconds, or seconds after eac attempt Thread.Sleep(1000); before callig Send(data); --- you could for example, use the attempNumber variable to increse or decrease this waiting time if you think its wise for your scenario.
bool repeat = true;
while (repeat)
{
try
{
salesOrdersArray = MagServ.salesOrderList(sessID, filter);
repeat = false;
}
catch
{
}
}

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);
}
}

Receiving "...has already been registered..." from EventLog.CreateEventSource even though I'm checking !EventLog.SourceExists

My following code fails with "...has already been registered as a source on the local computer" even though I'm doing checks first:
lock ( eventLock )
{
string eventLog = Constants.EventLogPL;
string eventSrc = Constants.EventSrcPL;
if (!EventLog.Exists(eventLog))
{
if (!EventLog.SourceExists(eventSrc))
{
try
{
EventLog.CreateEventSource(eventSrc, eventLog);
}
catch (Exception e)
{
System.Diagnostics.Debug.WriteLine(e.Message);
}
}
}
}
I'd have thought my call to !EventLog.SourceExists would have been enough to prevent my error!
I'm on 2010 .NET 4 and Windows 7 64 compiling to any CPU.
Edit: Updated code to get Constant's to locals to check they don't change, and use locking to make sure only one thread can test and create. Code still fails with the same error.
Found the problem after digging in to Sysinternals' Process Monitor a little more:
Calling EventLog.Exists("MyLog");
Logs Name not found, as expected in:
KLM\System\CurrentControlSet\services\eventlog\MyLog
Calling EventLog.SourceExists("MySource");
Checks several places, name not found in as expected:
HKLM\System\CurrentControlSet\services\eventlog\Application\MySource
HKLM\System\CurrentControlSet\services\eventlog\HardwareEvents\MySource
HKLM\System\CurrentControlSet\services\eventlog\Internet Explorer\MySource
HKLM\System\CurrentControlSet\services\eventlog\Key Management Service\MySource
HKLM\System\CurrentControlSet\services\eventlog\Media Center\MySource
HKLM\System\CurrentControlSet\services\eventlog\ODiag\MySource
HKLM\System\CurrentControlSet\services\eventlog\OSession\MySource
HKLM\System\CurrentControlSet\services\eventlog\Security\MySource
HKLM\System\CurrentControlSet\services\eventlog\System\MySource
HKLM\System\CurrentControlSet\services\eventlog\VisualSVNServer\MySource
HKLM\System\CurrentControlSet\services\eventlog\Windows PowerShell\MySource
HKLM\System\CurrentControlSet\services\eventlog\Application\MySource
HKLM\System\CurrentControlSet\services\eventlog\HardwareEvents\MySource
HKLM\System\CurrentControlSet\services\eventlog\Internet Explorer\MySource
HKLM\System\CurrentControlSet\services\eventlog\Key Management Service\MySource
HKLM\System\CurrentControlSet\services\eventlog\Media Center\MySource
HKLM\System\CurrentControlSet\services\eventlog\ODiag\MySource
HKLM\System\CurrentControlSet\services\eventlog\OSession\MySource
HKLM\System\CurrentControlSet\services\eventlog\Security\MySource
HKLM\System\CurrentControlSet\services\eventlog\System\MySource
HKLM\System\CurrentControlSet\services\eventlog\VisualSVNServer\MySource
HKLM\System\CurrentControlSet\services\eventlog\Windows PowerShell\MySource
HKLM\System\CurrentControlSet\services\eventlog\MyLog
However, calling EventLog.CreateEventSource("MySource", "MyLog");
Finds MyLog in the following registry location and errors:
HKLM\System\CurrentControlSet\services\eventlog\Application\MyLog
Removing "HKLM\System\CurrentControlSet\services\eventlog\Application\MyLog" and re-running fixed my problem!
Looks like the .Exists don't look in all the places .CreateEvent does!
//0 for false, 1 for true.
private static int usingResource = 0;
if (!EventLog.SourceExists(Constants.EventSrcPL))
{
//0 indicates that the method is not in use.
if (0 == Interlocked.Exchange(ref usingResource, 1))
{
if (!EventLog.SourceExists(Constants.EventSrcPL))
{
try
{
EventLog.CreateEventSource(Constants.EventSrcPL, Constants.EventLogPL);
}
catch (Exception e)
{
System.Diagnostics.Debug.WriteLine(e.Message);
//Release the lock
Interlocked.Exchange(ref usingResource, 0);
}
}
}
}
else
{
usingResource = 0;
}
Does not solve the issue when the source is created by a different application in the exact time you are accessing the event log.
Edited: made modifications that account for the delayed creation of a EventSource.

Additional try statement in catch statement - code smell?

Situation:
My application need to process the first step in the business rules (the initial try-catch statement). If an certain error occurs when the process calls the helper method during the step, I need to switch to a second process in the catch statement. The back up process uses the same helper method. If an same error occurs during the second process, I need to stop the entire process and throw the exception.
Implementation:
I was going to insert another try-catch statement into the catch statement of the first try-catch statement.
//run initial process
try
{
//initial information used in helper method
string s1 = "value 1";
//call helper method
HelperMethod(s1);
}
catch(Exception e1)
{
//backup information if first process generates an exception in the helper method
string s2 = "value 2";
//try catch statement for second process.
try
{
HelperMethod(s2);
}
catch(Exception e2)
{
throw e2;
}
}
What would be the correct design pattern to avoid code smells in this implementation?
I caused some confusion and left out that when the first process fails and switches to the second process, it will send different information to the helper method. I have updated the scenario to reflect the entire process.
If the HelperMethod needs a second try, there is nothing directly wrong with this, but your code in the catch tries to do way too much, and it destroys the stacktrace from e2.
You only need:
try
{
//call helper method
HelperMethod();
}
catch(Exception e1)
{
// maybe log e1, it is getting lost here
HelperMethod();
}
I wouldn't say it is bad, although I'd almost certainly refactor the second block of code into a second method, so keep it comprehensible. And probably catch something more specific than Exception. A second try is sometimes necessary, especially for things like Dispose() implementations that might themselves throw (WCF, I'm looking at you).
The general idea putting a try-catch inside the catch of a parent try-catch doesn't seem like a code-smell to me. I can think of other legitimate reasons for doing this - for instance, when cleaning up an operation that failed where you do not want to ever throw another error (such as if the clean-up operation also fails). Your implementation, however, raises two questions for me: 1) Wim's comment, and 2) do you really want to entirely disregard why the operation originally failed (the e1 Exception)? Whether the second process succeeds or fails, your code does nothing with the original exception.
Generally speaking, this isn't a problem, and it isn't a code smell that I know of.
With that said, you may want to look at handling the error within your first helper method instead of just throwing it (and, thus, handling the call to the second helper method in there). That's only if it makes sense, but it is a possible change.
Yes, a more general pattern is have the basic method include an overload that accepts an int attempt parameter, and then conditionally call itself recursively.
private void MyMethod (parameterList)
{ MyMethod(ParameterList, 0)l }
private void MyMethod(ParameterList, int attempt)
{
try { HelperMethod(); }
catch(SomeSpecificException)
{
if (attempt < MAXATTEMPTS)
MyMethod(ParameterList, ++attempt);
else throw;
}
}
It shouldn't be that bad. Just document clearly why you're doing it, and most DEFINITELY try catching a more specific Exception type.
If you need some retry mechanism, which it looks like, you may want to explore different techniques, looping with delays etc.
It would be a little clearer if you called a different function in the catch so that a reader doesn't think you're just retrying the same function, as is, over again. If there's state happening that's not being shown in your example, you should document it carefully, at a minimum.
You also shouldn't throw e2; like that: you should simply throw; if you're going to work with the exception you caught at all. If not, you shouldn't try/catch.
Where you do not reference e1, you should simply catch (Exception) or better still catch (YourSpecificException)
If you're doing this to try and recover from some sort of transient error, then you need to be careful about how you implement this.
For example, in an environment where you're using SQL Server Mirroring, it's possible that the server you're connected to may stop being the master mid-connection.
In that scenario, it may be valid for your application to try and reconnect, and re-execute any statements on the new master - rather than sending an error back to the caller immediately.
You need to be careful to ensure that the methods you're calling don't have their own automatic retry mechanism, and that your callers are aware there is an automatic retry built into your method. Failing to ensure this can result in scenarios where you cause a flood of retry attempts, overloading shared resources (such as Database servers).
You should also ensure you're catching exceptions specific to the transient error you're trying to retry. So, in the example I gave, SqlException, and then examining to see if the error was that the SQL connection failed because the host was no longer the master.
If you need to retry more than once, consider placing an 'automatic backoff' retry delay - the first failure is retried immediately, the second after a delay of (say) 1 second, then doubled up to a maximum of (say) 90 seconds. This should help prevent overloading resources.
I would also suggest restructuring your method so that you don't have an inner-try/catch.
For example:
bool helper_success = false;
bool automatic_retry = false;
//run initial process
try
{
//call helper method
HelperMethod();
helper_success = true;
}
catch(Exception e)
{
// check if e is a transient exception. If so, set automatic_retry = true
}
if (automatic_retry)
{ //try catch statement for second process.
try
{
HelperMethod();
}
catch(Exception e)
{
throw;
}
}
Here's another pattern:
// set up state for first attempt
if(!HelperMethod(false)) {
// set up state for second attempt
HelperMethod(true);
// no need to try catch since you're just throwing anyway
}
Here, HelperMethod is
bool HelperMethod(bool throwOnFailure)
and the return value indicates whether or not success occurred (i.e., false indicates failure and true indicates success). You could also do:
// could wrap in try/catch
HelperMethod(2, stateChanger);
where HelperMethod is
void HelperMethod(int numberOfTries, StateChanger[] stateChanger)
where numberOfTries indicates the number of times to try before throwing an exception and StateChanger[] is an array of delegates that will change the state for you between calls (i.e., stateChanger[0] is called before the first attempt, stateChanger[1] is called before the second attempt, etc.)
This last option indicates that you might have a smelly setup though. It looks like the class that is encapsulating this process is responsible for both keeping track of state (which employee to look up) as well as looking up the employee (HelperMethod). By SRP, these should be separate.
Of course, you need to a catch a more specific exception than you currently are (don't catch the base class Exception!) and you should just throw instead of throw e if you need to rethrow the exception after logging, cleanup, etc.
You could emulate C#'s TryParse method signatures:
class Program
{
static void Main(string[] args)
{
Exception ex;
Console.WriteLine("trying 'ex'");
if (TryHelper("ex", out ex))
{
Console.WriteLine("'ex' worked");
}
else
{
Console.WriteLine("'ex' failed: " + ex.Message);
Console.WriteLine("trying 'test'");
if (TryHelper("test", out ex))
{
Console.WriteLine("'test' worked");
}
else
{
Console.WriteLine("'test' failed: " + ex.Message);
throw ex;
}
}
}
private static bool TryHelper(string s, out Exception result)
{
try
{
HelperMethod(s);
result = null;
return true;
}
catch (Exception ex)
{
// log here to preserve stack trace
result = ex;
return false;
}
}
private static void HelperMethod(string s)
{
if (s.Equals("ex"))
{
throw new Exception("s can be anything except 'ex'");
}
}
}
Another way is to flatten the try/catch blocks, useful if you're using some exception-happy API:
public void Foo()
{
try
{
HelperMethod("value 1");
return; // finished
}
catch (Exception e)
{
// possibly log exception
}
try
{
HelperMethod("value 2");
return; // finished
}
catch (Exception e)
{
// possibly log exception
}
// ... more here if needed
}
An option for retry (that most people will probably flame) would be to use a goto. C# doesn't have filtered exceptions but this could be used in a similar manner.
const int MAX_RETRY = 3;
public static void DoWork()
{
//Do Something
}
public static void DoWorkWithRetry()
{
var #try = 0;
retry:
try
{
DoWork();
}
catch (Exception)
{
#try++;
if (#try < MAX_RETRY)
goto retry;
throw;
}
}
In this case you know this "exception" probably will happen so I would prefer a simple approach an leave exceptions for the unknown events.
//run initial process
try
{
//initial information used in helper method
string s1 = "value 1";
//call helper method
if(!HelperMethod(s1))
{
//backup information if first process generates an exception in the helper method
string s2 = "value 2";
if(!HelperMethod(s2))
{
return ErrorOfSomeKind;
}
}
return Ok;
}
catch(ApplicationException ex)
{
throw;
}
I know that I've done the above nested try catch recently to handle decoding data where two third party libraries throw exceptions on failure to decode (Try json decode, then try base64 decode), but my preference is to have functions return a value which can be checked.
I generally only use the throwing of exceptions to exit early and notify something up the chain about the error if it's fatal to the process.
If a function is unable to provide a meaningful response, that is not typically a fatal problem (Unlike bad input data).
It seems like the main risk in nested try catch is that you also end up catching all the other (maybe important) exceptions that might occur.

Categories

Resources