Interrupting long running method pattern - c#

I am currently using this somewhat tedious pattern to generate error message for user running some long operation:
string _problem;
void SomeLongRunningMethod()
{
try
{
_problem = "Method1 had problem";
Method1();
_problem = "Unexpected error during doing something in Method2";
if(Method2())
{
_problem = "Method3 fails";
Method3();
}
_problem = "Not possible to obtain data";
var somedata = Method4();
}
catch(Exception)
{
MessageBox.Show("Problem with some long running method: " + _problem);
}
}
Either of methods may throw and I want to tell the user at which step failure occurs. This is done by setting _problem before running any of them.
In some cases I can use different Exception types to catch, but that doesn't works always, e.g. both Method1 and Method2 can throw InvalidOperationException().
This repeated code looks like a pattern. Though I can't recognize it. Any ideas? How to improve readability?

You could use when in the catch to differentiate between the same exception types and to check which method threw this exception:
void SomeLongRunningMethod()
{
try
{
Method1();
if (Method2())
{
Method3();
}
var somedata = Method4();
}
catch (InvalidOperationException invEx) when (invEx.TargetSite?.Name == nameof(Method1))
{
// ...
}
catch (InvalidOperationException invEx) when (invEx.TargetSite?.Name == nameof(Method2))
{
// ...
}
catch (Exception ex)
{
// ...
}
}

You could get the method that caused the exception using error.TargetSite. The only thing you need to change is your catch line: catch (Exception error)

I'd make a sequence of the things you want to do and run through them:
var methodList = new[]{
new{action = (Action)Method1, identifier = "Method1"},
new{action = (Action)Method2, identifier = "Method2"},
new{action = (Action)Method3, identifier = "Method3"},
};
string problem = null;
foreach(var info in methodList)
{
try
{
info.action();
}
catch(InvalidOperationException)
{
problem = string.Format("{0} failed", info.identifier);
break;
}
}
if(problem != null)
{
//notify
}

Related

C# Are errors in DataGridView.Datasource assignments handled internally

I have an application that assigns a list of values to a DataGridView.
This is done inside a Try catch block.
Occasionally there is an issue with the with the procedure that produces this list (also in a try catch block) and this causes an error when the assignment is done.
It appears the exception is never caught by the block doing the assignment and the application freezes.
Is the exception handled inside the assignment call so that it is not propagated out to the catch block?
The exception produced as a pop up states
"The following exception occurred in the DataGridView:
System.IndexOutOfrangeException: Index 1 does not have a value at
System.Windows.Forms.dataGridViewDataConnection.GetError(Int32 rowIndex)
To replace this default dialog please handle the DataError event"
The code is basically
public void SetupRunners(EventDetails _eventDetails)
{
try
{
RunnerAssociates.Clear();
RunnerAssociates = null;
RunnerAssociates = createRunnerAssociateList(selectedEventDetails);
runnerAssociatesDataGridView.DataSource = RunnerAssociates;
runnerAssociatesDataGridView.DataMember = string.Empty;
}
catch (Exception ex)
{
NZRB_Global.Utils.LogException(methodName, ex);
}
}
private List<Runner_Associate> createRunnerAssociateList(EventDetails _meetAndRace) //NZRB_Global.MeetAndRace _meetAndRace)
{
const string methodName = "createRunnerAssociateList";
List<Runner_Associate> lra = new List<Runner_Associate>();
try
{
if (null != _meetAndRace)
{
_meetAndRace.UpdateAllProfiles();
foreach (RunnerDetail rd in _meetAndRace.runners.runnerList)
{
Runner_Associate ra = new Runner_Associate();
ra.Number = rd.number;
ra.RunnerName = rd.name;
RunnerProfile profile = rd.GetRunnerProfile();
if ((rbJockeyDriver.Checked) || (rbCustom.Checked))
{
ra.AssociateName = rd.person;
}
else if (profile != null)
{
if (rbTrainers.Checked)
{
ra.AssociateName = profile.trainer;
}
else if (rbOwners.Checked)
{
ra.AssociateName = profile.owners;
}
}
lra.Add(ra);
}
}
}
catch (Exception ex)
{
NZRB_Global.Utils.LogException(methodName, ex);
}
return lra;
}

C# Determinig if exception initialized with a message

Is there a way when catching an exception to determine if it were constructed with a non-default message.
try
{
throw new Exception(message); // case 1
//throw new Exception(); // case 2
}
catch(Exception exp)
{
/* what do I put here such that if the case 2 exception were
caught it would output exp.ToString() instead of exp.Message? */
textBox1.Text = exp.Message; // case 1 handeling
}
Just to clarify when Exception(message) is thrown I want it to ouptut exp.Message and when Exception() is thrown I want to output exp.ToString(). I would prefer to accomplish this without adding a custom exception. Thanks.
You need to check the message against a default exception
catch (Exception e)
{
bool isDefaultMessage = e.Message == new Exception().Message;
}
Update
Difference types of Exception
catch (Exception e)
{
bool isDefaultMessage = false;
try
{
var x = (Exception) Activator.CreateInstance(e.GetType());
isDefaultMessage = e.Message == x.Message;
}
catch (Exception) {} // cannot create default exception.
}

How can I pass on an Exception?

Basically, what I want to do is pass a specific Exception to a more general Exception within the same try block. I've tried the following and it doesn't work:
static bool example(int count = 0)
{
try
{
work();
}
catch (TimeoutException e)
{
if (count < 3)
{
Console.WriteLine("Caught TimeoutException: {0}", e.Message);
return example(count + 1);
}
else
{
throw new Exception(e.Message);
}
}
catch (Exception e)
{
Console.WriteLine("Caught Exception: {0}", e.Message + " rethrown");
return false;
}
return true;
}
static void work()
{
throw new TimeoutException("test");
}
I want the TimeoutException to be only handled a certain amount of times before going to a more generic Exception. This is because the TimeoutException has additional information about the exception on a case by case basis. I do not want to duplicate the code for Exception under the else clause of TimeoutException. The reason I want all exceptions to be handled is that there may be other unknown exceptions that are thrown. The nature of the program requires it to not crash so I must account for any other exceptions and log them. How can I implement this?
You would need to nest this as 2 tries if you want to handle this this way:
static bool example(int count = 0)
{
try
{
try
{
work();
}
catch (TimeoutException e)
{
if (count < 3)
{
Console.WriteLine("Caught TimeoutException: {0}", e.Message);
return example(count + 1);
}
else
{
// Just throw, don't make a new exception
throw; // new Exception(e.Message);
}
}
}
catch (Exception e)
{
Console.WriteLine("Caught Exception: {0}", e.Message + " rethrown");
return false;
}
return true;
}
The "inner try/catch" will only catch TimeoutException, so any other exception will always go to the outer scope. When you rethrow, it'll automatically get caught by the outer scope, as well, which eliminates the need for killing the exception information. (If you throw new Exception, you lose your stack trace data, and other very valuable debugging information.)
Here's my take:
bool example()
{
// Attempt the operation a maximum of three times.
for (int i = 0; i < 3; i++)
{
try
{
work();
return true;
}
catch (Exception e)
{
Console.WriteLine("Caught exception {0}", e.Message);
// Fail immediately if this isn't a TimeoutException.
if (!(e is TimeoutException))
return false;
}
}
return false;
}
EDIT
If you want to actually do something with the TimeoutException, you could change the catch block like so:
catch (Exception e)
{
// As Reed pointed out, you can move this into the if block if you want
// different messages for the two cases.
Console.WriteLine("Caught exception {0}", e.Message);
TimeoutException timeoutException = e as TimeoutException;
if (timeoutException != null)
{
// Do stuff with timeout info...
}
else
{
// Not a timeout error, fail immediately
return false;
}
}

Best way to throw exceptions on query attempt c#

Hi I'm developing a winform application using C# and the entity framework (linq to entities).
Suppose the following escenario:
In a method of some class, I set and object of values with form values
private void agrega_cliente_Click(object sender, EventArgs e)
{
cliente = new _Cliente();
try
{
cliente.nombres = nom_cliente.Text;
cliente.apellidoP = apellidoP_cliente.Text;
cliente.apellidoM = apellidoM_cliente.Text;
cliente.fechaNacimiento = fechaNacimientoPicker.Value.Date;
if (operaciones.AgregaCliente(cliente, referencias))
{
MessageBox.Show("Cliente Agregado");
this.Close();
}
}
catch(Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
Note that the assigment and calling to method "AgregaCliente" is between a try and catch, so if an exception is triggered a MessageBox will show it.
Then in other class, I have AgregaCliente method which insert values in a database.
public bool AgregaCliente(_Cliente cliente, ArrayList refes)
{
try
{
Cliente cli = new Cliente()
{
Nombres = cliente.nombres,
ApellidoP = cliente.apellidoP,
ApellidoM = cliente.apellidoM,
FechaNac = cliente.fechaNacimiento
};
if (NombreExiste(cli))
context.clientes.AddObject(cli);
else
throw new System.ArgumentException("El usuario ya existe");
if (refes.Count != 0)
{
foreach (_Referencia elem in refes)
context.referencias_personales.AddObject(AgregaReferencia(elem));
}
context.SaveChanges();
}
catch (Exception ex)
{
return false;
}
return true;
}
Inside this method there is a call to "NombreExiste()" which checks that the user isn't already inserted, if the user exists an exception is thrown.
So the problem here is that if an exception is thrown in "AgregaCliente" method, I want this exception to be catched by "agrega_cliente_Click()" method, so the user knowns what originated the problem. I hope you understand what I'm trying to do.
Thanks
Simply get rid of your try/catch inside the AgregaCliente() method and the exception will automatically bubble-up.
public bool AgregaCliente(_Cliente cliente, ArrayList refes)
{
Cliente cli = new Cliente()
{
Nombres = cliente.nombres,
ApellidoP = cliente.apellidoP,
ApellidoM = cliente.apellidoM,
FechaNac = cliente.fechaNacimiento
};
if (NombreExiste(cli))
context.clientes.AddObject(cli);
else
throw new System.ArgumentException("El usuario ya existe");
if (refes.Count != 0)
{
foreach (_Referencia elem in refes)
context.referencias_personales.AddObject(AgregaReferencia(elem));
}
context.SaveChanges();
return true;
}
The problem is that your AgregaCliente() method is catching all exceptions and simply swallowing them. Instead of catching all exceptions via:
catch (Exception ex)
{
return false;
}
You should only catch specific Exceptions you can handle and let the others pass up the call chain. However, you should know that throwing exceptions is very "expensive" for a program. C# does a lot of work behind the scenes when an exception is thrown. A better solution may be to use a return code to indicate to callers of the AgregaCliente() method the status. For example:
public enum AgregaClienteStatus
{
Success = 0;
ClientAlreadyExists = 1;
Other = ??; // Any other status numbers you want
}
public AgregaClienteStatus AgregaCliente(_Cliente cliente, ArrayList refes)
{
Cliente cli = new Cliente()
{
Nombres = cliente.nombres,
ApellidoP = cliente.apellidoP,
ApellidoM = cliente.apellidoM,
FechaNac = cliente.fechaNacimiento
};
if (NombreExiste(cli))
context.clientes.AddObject(cli);
else
return AgregaClienteStatus.ClientAlreadyExists
if (refes.Count != 0)
{
foreach (_Referencia elem in refes)
context.referencias_personales.AddObject(AgregaReferencia(elem));
}
context.SaveChanges();
return AgregaClientStatus.Success;
}
Of course, this functionality could also be achieved using constant integers if you don't like enums.
You can then use that return status to indicate information to the user without the expense of an exception:
var result = AgregaClient(cliente, refes);
switch (result)
{
case AgregaClientStatus.Success:
// Perform success logic
break;
case AgregaClientStatus.ClientAlreadyExists:
MessageBox.Show("Client already exists");
break;
// OTHER SPECIAL CASES
default:
break;
}
}

C# try-catch-else

One thing that has bugged me with exception handling coming from Python to C# is that in C# there doesn't appear to be any way of specifying an else clause. For example, in Python I could write something like this (Note, this is just an example. I'm not asking what is the best way to read a file):
try
{
reader = new StreamReader(path);
}
catch (Exception)
{
// Uh oh something went wrong with opening the file for reading
}
else
{
string line = reader.ReadLine();
char character = line[30];
}
From what I have seen in most C# code people would just write the following:
try
{
reader = new StreamReader(path);
string line = reader.ReadLine();
char character = line[30];
}
catch (Exception)
{
// Uh oh something went wrong, but where?
}
The trouble with this is that I don't want to catch out of range exception coming from the fact that the first line in the file may not contain more than 30 characters. I only want to catch exceptions relating to the reading of the file stream. Is there any similar construct I can use in C# to achieve the same thing?
Catch a specific class of exceptions
try
{
reader = new StreamReader(path);
string line = reader.ReadLine();
char character = line[30];
}
catch (IOException ex)
{
// Uh oh something went wrong with I/O
}
catch (Exception ex)
{
// Uh oh something else went wrong
throw; // unless you're very sure what you're doing here.
}
The second catch is optional, of course. And since you don't know what happened, swallowing this most general exception is very dangerous.
You could write it like:
bool success = false;
try {
reader = new StreamReader(path);
success = true;
}
catch(Exception) {
// Uh oh something went wrong with opening the file for reading
}
finally {
if(success) {
string line = reader.ReadLine();
char character = line[30];
}
}
You can do this:
try
{
reader = new StreamReader(path);
}
catch (Exception)
{
// Uh oh something went wrong with opening the file for reading
}
string line = reader.ReadLine();
char character = line[30];
But of course, you will have to set reader into a correct state or return out of the method.
Catch more specific exceptions.
try {
reader = new StreamReader(path);
string line = reader.ReadLine();
char character = line[30];
}
catch(FileNotFoundException e) {
// thrown by StreamReader constructor
}
catch(DirectoryNotFoundException e) {
// thrown by StreamReader constructor
}
catch(IOException e) {
// some other fatal IO error occured
}
Further, in general, handle the most specific exception possible and avoid handling the base System.Exception.
You can nest your try statements, too
Exceptions are used differently in .NET; they are for exceptional conditions only.
In fact, you should not catch an exception unless you know what it means, and can actually do something about it.
You can have multiple catch clauses, each specific to the type of exception you wish to catch. So, if you only want to catch IOExceptions, then you could change your catch clause to this:
try
{
reader = new StreamReader(path);
string line = reader.ReadLine();
char character = line[30];
}
catch (IOException)
{
}
Anything other than an IOException would then propagate up the call stack. If you want to also handle other exceptions, then you can add multiple exception clauses, but you must ensure they are added in most specific to most generic order. For example:
try
{
reader = new StreamReader(path);
string line = reader.ReadLine();
char character = line[30];
}
catch (IOException)
{
}
catch (Exception)
{
}
More idiomatically, you would employ the using statement to separate the file-open operation from the work done on the data it contains (and include automatic clean-up on exit)
try {
using (reader = new StreamReader(path))
{
DoSomethingWith(reader);
}
}
catch(IOException ex)
{
// Log ex here
}
It is also best to avoid catching every possible exception -- like the ones telling you that the runtime is about to expire.
Is there any similar construct I can use in C#
to acheive the same thing?
No.
Wrap your index accessor with an "if" statement which is the best solution in your case in case of performance and readability.
if (line.length > 30) {
char character = line [30];
}
After seeing the other suggested solutions, here is my approach:
try {
reader = new StreamReader(path);
}
catch(Exception ex) {
// Uh oh something went wrong with opening the file stream
MyOpeningFileStreamException newEx = new MyOpeningFileStreamException();
newEx.InnerException = ex;
throw(newEx);
}
string line = reader.ReadLine();
char character = line[30];
Of course, doing this makes sense only if you are interested in any exceptions thrown by opening the file stream (as an example here) apart from all other exceptions in the application. At some higher level of the application, you then get to handle your MyOpeningFileStreamException as you see fit.
Because of unchecked exceptions, you can never be 100% certain that catching only IOException out of the entire code block will be enough -- the StreamReader can decide to throw some other type of exception too, now or in the future.
You can do something similar like this:
bool passed = true;
try
{
reader = new StreamReader(path);
}
catch (Exception)
{
passed = false;
}
if (passed)
{
// code that executes if the try catch block didnt catch any exception
}
I have taken the liberty to transform your code a bit to demonstrate a few important points.
The using construct is used to open the file. If an exception is thrown you will have to remember to close the file even if you don't catch the exception. This can be done using a try { } catch () { } finally { } construct, but the using directive is much better for this. It guarantees that when the scope of the using block ends the variable created inside will be disposed. For a file it means it will be closed.
By studying the documentation for the StreamReader constructor and ReadLine method you can see which exceptions you may expect to be thrown. You can then catch those you finde appropriate. Note that the documented list of exceptions not always is complete.
// May throw FileNotFoundException, DirectoryNotFoundException,
// IOException and more.
try {
using (StreamReader streamReader = new StreamReader(path)) {
try {
String line;
// May throw IOException.
while ((line = streamReader.ReadLine()) != null) {
// May throw IndexOutOfRangeException.
Char c = line[30];
Console.WriteLine(c);
}
}
catch (IOException ex) {
Console.WriteLine("Error reading file: " + ex.Message);
}
}
}
catch (FileNotFoundException ex) {
Console.WriteLine("File does not exists: " + ex.Message);
}
catch (DirectoryNotFoundException ex) {
Console.WriteLine("Invalid path: " + ex.Message);
}
catch (IOException ex) {
Console.WriteLine("Error reading file: " + ex.Message);
}
Sounds like you want to do the second thing only if the first thing succeeded. And maybe catching different classes of exception is not appropriate, for example if both statements could throw the same class of exception.
try
{
reader1 = new StreamReader(path1);
// if we got this far, path 1 succeded, so try path2
try
{
reader2 = new StreamReader(path2);
}
catch (OIException ex)
{
// Uh oh something went wrong with opening the file2 for reading
// Nevertheless, have a look at file1. Its fine!
}
}
catch (OIException ex)
{
// Uh oh something went wrong with opening the file1 for reading.
// So I didn't even try to open file2
}
There might not be any native support for try { ... } catch { ... } else { ... } in C#, but if you are willing to shoulder the overhead of using a workaround, then the example shown below might be appealing:
using System;
public class Test
{
public static void Main()
{
Example("ksEE5A.exe");
}
public static char Example(string path) {
var reader = default(System.IO.StreamReader);
var line = default(string);
var character = default(char);
TryElse(
delegate {
Console.WriteLine("Trying to open StreamReader ...");
reader = new System.IO.StreamReader(path);
},
delegate {
Console.WriteLine("Success!");
line = reader.ReadLine();
character = line[30];
},
null,
new Case(typeof(NullReferenceException), error => {
Console.WriteLine("Something was null and should not have been.");
Console.WriteLine("The line variable could not cause this error.");
}),
new Case(typeof(System.IO.FileNotFoundException), error => {
Console.WriteLine("File could not be found:");
Console.WriteLine(path);
}),
new Case(typeof(Exception), error => {
Console.WriteLine("There was an error:");
Console.WriteLine(error);
}));
return character;
}
public static void TryElse(Action pyTry, Action pyElse, Action pyFinally, params Case[] pyExcept) {
if (pyElse != null && pyExcept.Length < 1) {
throw new ArgumentException(#"there must be exception handlers if else is specified", nameof(pyExcept));
}
var doElse = false;
var savedError = default(Exception);
try {
try {
pyTry();
doElse = true;
} catch (Exception error) {
savedError = error;
foreach (var handler in pyExcept) {
if (handler.IsMatch(error)) {
handler.Process(error);
savedError = null;
break;
}
}
}
if (doElse) {
pyElse();
}
} catch (Exception error) {
savedError = error;
}
pyFinally?.Invoke();
if (savedError != null) {
throw savedError;
}
}
}
public class Case {
private Type ExceptionType { get; }
public Action<Exception> Process { get; }
private Func<Exception, bool> When { get; }
public Case(Type exceptionType, Action<Exception> handler, Func<Exception, bool> when = null) {
if (!typeof(Exception).IsAssignableFrom(exceptionType)) {
throw new ArgumentException(#"exceptionType must be a type of exception", nameof(exceptionType));
}
this.ExceptionType = exceptionType;
this.Process = handler;
this.When = when;
}
public bool IsMatch(Exception error) {
return this.ExceptionType.IsInstanceOfType(error) && (this.When?.Invoke(error) ?? true);
}
}
If you happen to be in a loop, then you can put a continue statement in the catch blocks. This will cause the remaining code of that block to be skipped.
If you are not in a loop, then there is no need to catch the exception at this level. Let it propagate up the call stack to a catch block that knows what to do with it. You do this by eliminating the entire try/catch framework at the current level.
I like try/except/else in Python too, and maybe they will get added to C# some day (just like multiple return values were). But if you think about exceptions a little differently, else blocks are not strictly necessary.

Categories