How to make exception handler using events - c#

I have dll Scripting. In that dll I have class named Scripter. In Scripter class i call some methods which loads data from MySQL database (LoadTables()). In that functions which loads data from MySQL database exception could happen. I want to be able, later in my app where I use Scripting.dll, to do something like this:
Scrpter sc = new Scripter();
sc.OnError += ErrorOccured;
And want to have function ErrorOcured in my app which will be like:
private void ErrorOccured(Exception exception)
{...}
What I need to have in Scripter class, and how should I pass exception in catch block in LoadTables so I could later use ErrorOcured() to see what happend wrong?

As Picoh and Zoltan commented on your question, you can easily wrap method calls to Scripter methods in try/catch block. But, if you want to use event (with custom args), you can do something like this:
//your class
public class Scripter
{
public Scripter()
{
}
//public event with custom event args
public EventHandler<ScripterErrorEventArgs> OnError;
//just for test
public void RaiseError()
{
//error which is caught here
Exception ex = new Exception("something happened");
OnError?.Invoke(this, new ScripterErrorEventArgs(ex));
}
}
//class for custom event args. add your own other properties as needed
public class ScripterErrorEventArgs : EventArgs
{
public ScripterErrorEventArgs()
{
}
public ScripterErrorEventArgs(Exception ex)
{
this.Exception = ex;
}
public Exception Exception { get; set; }
}
//usage
public void someMethod()
{
Scripter s = new Scripter();
s.OnError += new EventHandler<ScripterErrorEventArgs>(LogError)
s.RaiseError();
}
private void LogError(object sender, ScripterErrorEventArgs e)
{
//your code here
}

You could try to modify your Scripter class like this
class Scripter
{
public event EventHandler<Exception> ErrorOcurred;
protected virtual void OnErrorOcurred(Exception e)=>ErrorOcurred?.Invoke(this, e);
public void ThrowsException()
{
try
{
throw new Exception("Throws exception");
}
catch (Exception ex)
{
OnErrorOcurred(ex);
}
}
}
This way you can subscribe to ErrorOcurred and receive notifications about exceptions. You must call OnErrorOcurred in every place you catch an exception
Hope this helps

Related

Handle exceptions in events

In this example I have two subscribers to my event. One of the subscribers raises an Exception but I would like to prevent all subscribers to fail when only one of them incurs in an Exception. The try-catch statement is not enough to capture the exception of the Dog class, it makes the Cat class fails too.
using System;
namespace EventsExample
{
class BreadWinnerEventArgs : EventArgs
{
public string Name { get; set; }
}
class BreadWinner // publisher
{
public event EventHandler<BreadWinnerEventArgs> ArrivedHome; // 2.
public void Action(BreadWinnerEventArgs args)
{
Console.WriteLine("Papa says: I'm at home!");
OnArriveHome(args);
}
protected virtual void OnArriveHome(BreadWinnerEventArgs args)
{
if (ArrivedHome != null)
{
foreach (EventHandler<BreadWinnerEventArgs> handler in ArrivedHome.GetInvocationList())
{
try
{
var t = ArrivedHome; // publisher uses sames signature as the delegate
if (t != null)
t(this, args);
}
catch (Exception e)
{
Console.WriteLine("Error in the handler {0}: {1}", handler.Method.Name, e.Message);
}
}
}
}
}
class Dog
{
public void OnArrivedHome(object source, BreadWinnerEventArgs e)
{
throw new Exception();
Console.WriteLine(String.Format("Dog says: Whoof {0}!", e.Name));
}
}
class Cat
{
public void OnArrivedHome(object source, BreadWinnerEventArgs e)
{ Console.WriteLine(String.Format("Cat hides from {0}", e.Name)); }
}
class Program
{
static void Main(string[] args)
{
BreadWinner papa = new BreadWinner(); // publisher
Dog dog = new Dog(); // subscriber
Cat cat = new Cat();
papa.ArrivedHome += dog.OnArrivedHome; // subscription
papa.ArrivedHome += cat.OnArrivedHome;
papa.Action(new BreadWinnerEventArgs() { Name = "Papa" });
Console.Read();
}
}
}
You almost had it, you just where using t where you should have been using handler, you also where using ArrivedHome where you should have been using t. I also modified the code to wrap up all the exceptions and the delegate who called them in to a custom exception then wrap those in an aggragate exception and have the code raise that.
protected virtual void OnArriveHome(BreadWinnerEventArgs args)
{
var t = ArrivedHome; // publisher uses sames signature as the delegate
if (t != null)
{
var exceptions = new List<Exception>();
foreach (EventHandler<BreadWinnerEventArgs> handler in t.GetInvocationList())
{
try
{
try
{
handler(this, args);
}
catch (Exception e)
{
Console.WriteLine("Error in the handler {0}: {1}", handler.Method.Name, e.Message);
throw new DelegateException(handler, e, this, args); //Throw the exception to capture the stack trace.
}
}
catch (DelegateException e)
{
exceptions.Add(e);
}
}
if (exceptions.Count > 0)
{
throw new AggregateException(exceptions);
}
}
}
///Elsewhere
sealed class DelegateException : Exception
{
public Delegate Handler { get; }
public object[] Args { get; }
public DelegateException(Delegate handler, Exception innerException, params object[] args) : base("A delegate raised an error when called.", innerException)
{
Handler = handler;
Args = args;
}
}
However I don't think you really should be doing this, this deviates from the "expected behavior" and may catch other programmers off guard if they have to consume your classes that do this.
I'm not saying that you should do this, but this is one way to handle it:
protected virtual void OnArriveHome(BreadWinnerEventArgs args)
{
var handler = ArrivedHome;
if (handler == null)
return;
foreach (var subscriber in handler.GetInvocationList())
{
try
{
subscriber(this, args);
}
catch (Exception ex)
{
//You can, and probably should, remove the handler from the list here
}
}
}
This allows you to invoke each one of the subscribers individually instead of as a group, and catch an exception when one of them throws. The problem I have with doing this is that you really can't know what broke, or do anything to fix it. All you can do is log and optionally remove that event handler so that the next time you don't throw on that one.
Removing the handler may also be bad practice since it can be difficult to trace why a previously assigned handler is now unassigned.

How to properly notify caller on failure of the asynchronous operation?

Class FeatureManager manages some feature and looks something like this:
public class FeatureManager
{
public event EventHandler FeatureEnabledChangedEvent;
private void OnFeatureEnabledChanged()
{
if (FeatureEnabledChangedEvent != null)
{
FeatureEnabledChangedEvent(this, EventArgs.Empty);
}
}
public event EventHandler FeatureEnableBusyChangedEvent;
private void OnFeatureEnableBusyChanged()
{
if (FeatureEnableBusyChangedEvent != null)
{
FeatureEnableBusyChangedEvent(this, EventArgs.Empty);
}
}
public event EventHandler FeatureEnableFailedEvent;
private void OnFeatureEnableFailed(FeatureEnableFailedEventArgs args)
{
if (FeatureEnableFailedEvent!= null)
{
FeatureEnableFailedEvent(this, args);
}
}
private bool _isFeatureEnabled
public bool IsFeatureEnabled
{
get
{
return _isFeatureEnabled;
}
private set
{
if (_isFeatureEnabled != value)
{
_isFeatureEnabled = value;
OnFeatureEnabledChanged();
}
}
}
private bool _isFeatureEnableBusy;
public bool IsFeatureEnableBusy
{
get
{
return _isFeatureEnableBusy;
}
private set
{
if (_isFeatureEnableBusy != value)
{
_isFeatureEnableBusy = value;
OnFeatureEnableBusyChanged();
}
}
}
public async Task EnableFeature()
{
IsFeatureEnableBusy = true;
try
{
// By its nature, process of enabling this feature is asynchronous.
await EnableFeatureImpl(); // can throw exception
IsFeatureEnabled = true;
}
catch(Exception exc)
{
OnFeatureEnableFailed(new FeatureEnableFailedEventArgs(exc.Message));
}
finally
{
IsFeatureEnableBusy = false;
}
}
}
UI class FeatureView has to be notified when:
IsFeatureEnableBusy changes (or, in other words when EnableFeature is being executed - in order to disable some controls)
IsFeatureEnabled changes
EnableFeature fails (when it throws exception in which case FeatureView displays error message
to the user)
EnableFeature can be called from some Engine class E (automatically, during the initialization on the application's launch) and also from FeatureView (when user presses 'Enable' button).
In order to satisfy requirement where FeatureView has to be notified when EnableFeature fails after it's been called by E, I added an event FeatureEnableFailedEvent.
When E calls EnableFeature and EnableFeature throws an exception, FeatureView receives FeatureEnableFailedEvent and displays error message. But when FeatureView itself calls EnableFeature and EnableFeature fails, FeatureView catches thrown exception but also gets notified on this failure from FeatureEnableFailedEvent so error handler is called twice. How to avoid this?
One solution is to declare EnableFeature as an old-style async method (and use BackgroundWorker) as in the following snippet:
public class FeatureManager
{
public void EnableFeatureAsync()
{
var bgw = new BackgroundWorker();
bgw.DoWork += (sender, e) =>
{
IsFeatureEnableBusy = true;
EnableFeatureImpl(); // can throw exception
};
bgw.RunWorkerCompleted += (sender, args) =>
{
IsFeatureEnableBusy = false;
if (args.Error == null)
{
IsFeatureEnabled = true;
}
else
{
OnFeatureEnableFailed(new FeatureEnableFailedEventArgs(args.Error.Message));
}
};
bgw.RunWorkerAsync();
}
}
In this case, caller of EnableFeatureAsync can assume that this method runs asynchronously (suffix Async in method's name should be a hint) and that it has to subscribe to FeatureEnableFailedEvent if want to be notified on the method failure. This way FeatureView gets notified on EnableFeatureAsync failure only once so error handler is called once as it should be.
Is this a good approach? Could this be achieved by still using async/await in some way? Is it good assuming that suffix Async in method's name is a good enough hint to callers so they know that this method runs as asynchronous one and that they have to look for some events to subscribe to?
As commented by #svick, I also don't see why your FeatureView catches the exception and also gets the event, when the exception is not rethrown in the handler of FeatureManager. But here is an different approach, which I'd prefer over yours based on events:
Use TaskCompletionSource to let the view know when the enablement of a feature did throw an exception even when FeatureView is not the caller of EnableFeature() (btw, by convention the method should also be named EnableFeatureAsync in the first example).
public class FeatureManager
{
public TaskCompletionSource<bool> FeatureCompleted { get; private set; }
// if you still need this property
public bool IsFeatureEnabled
{
get { return FeatureCompleted.Task.IsCompleted; }
}
public FeatureManager() {}
public async Task EnableFeature()
{
IsFeatureEnableBusy = true;
try
{
// By its nature, process of enabling this feature is asynchronous.
await EnableFeatureImpl(); // can throw exception
this.FeatureCompleted.TrySetResult(true);
}
catch(Exception exc)
{
this.FeatureCompleted.TrySetException(exc);
}
finally
{
IsFeatureEnableBusy = false;
}
}
}
Your FeatureView instance now needs to await the Task of the TaskCompletionSource. The code could look like this:
public class FeatureView
{
// if you still need this property
public async void HandleFeatureCompleted(FeatureManager fm)
{
try
{
await fm.FeatureCompleted.Task;
}
catch(Exception e)
{
// handle exception
}
}
}
You have to provide the correct FeatureManager instance to your view. I'm not sure if this approach is appropriate if you have hundredths or even thousands of FeatureManager instances messages. I'd be happy if more somebody of the commenters could provide feedback about this.

C# transmit exception from class to Form

How can I pass an error in the form of the class. When creating exemplar class.
Class:
class ThreadSafeLog
{
public ThreadSafeLog()
{
try
{
if (!File.Exists(Path_))
{
using (File.Create(Path_)) { }
}
}
catch(Exception e)
{
MessageBox.Show(e.Message);//error transmit exception to Form
}
}
}
Form:
ThreadSafeLog log = new ThreadSafeLog(
#"R:\project\ThreadSafeLog\ThreadSafeLogTest\ThreadSafeLogTest\bin\");
Why would ThreadSafeLog be concerned with what the form that uses it is doing? How is it even supposed to know it's being used by a form? Catch the exception in the form instead.
class ThreadSafeLog
{
public ThreadSafeLog()
{
if (!File.Exists(Path_))
{
using (File.Create(Path_)) { }
}
}
}
Form:
try
{
ThreadSafeLog log = new ThreadSafeLog(
#"R:\project\ThreadSafeLog\ThreadSafeLogTest\ThreadSafeLogTest\bin\");
}
catch (Exception ex)
{
MessageBox.Show(e.Message);
}
Though, as Adriano points out, you may also want to question why you're displaying internal error details directly to users.
I would not notify log's users about an error inside the logger itself. Log is, usually, a subsystem that it's good to consider error-safe and thread-safe or you'll make your code a messy. Imagine:
try
{
// Do something that may fail
}
catch (SomeException e)
{
try
{
logger.Log(e);
}
catch (IOException)
{
// Ooops, log doesn't work. What should I do?
// Should I display a MessageBox?
}
}
Your code will become quickly a messy (imagine to repeat the same check again and again for each trace log).
What I would do is to ignore (if possible) errors inside log function and to take another action instead. How? Just add an event to your log class (code is an example not for production!):
class Logger
{
public event EventHandler<LogErrorEventArgs> Error;
public void Log(Exception e)
{
try
{
// Try to write log somewhere...
}
catch (IOException internalException)
{
EventHandler<LogErrorEventArgs> error = Error;
if (error != null)
error(this, new LogErrorEventArgs(e, internalException);
}
}
}
In this way unhandled errors inside logger will go unnoticed (if this is applicable) or they may be handled (in the way it's appropriate, if there is a way). For example:
Logger logger = new Logger();
logger.Error += delegate(object sender, LogErrorEventArgs e)
{
if (SystemInformation.UserInteractive)
MessageBox.Show(e.ExceptionToLog.Message);
};
You may attach multiple handlers too (for example to log to Windows Log and notify user is session is interactive). When you use log you won't be aware it may fail (so you won't have to fill your code with try/catch everywhere) but if needed you may still be notified and errors inside log will be handled in a consistent manner.
Now let's rewrite the code in our first example like this:
try
{
// Do something that may fail
}
catch (SomeException e)
{
logger.Log(e);
}
Straight, right? I assumed logger will always write an exception (somehow) but it's easy to change code to handle a raw String instead. I omitted code for LogErrorEventArgs because pretty obvious anyway it may look like this:
class LogErrorEventArgs : EventArgs
{
public LogErrorEventArgs(
Exception exceptionToLog,
Exception internalLoggerException)
{
ExceptionToLog = exceptionToLog;
InternalLoggerException = internalLoggerException;
}
public Exception ExceptionToLog
{
get;
set;
}
public Exception InternalLoggerException
{
get;
set;
}
}

A global error handler for a class library in C#

Is there a way to catch and handle an exception for all exceptions thrown within any of the methods of a class library?
I can use a try catch construct within each method as in sample code below, but I was looking for a global error handler for a class library. The library could be used by ASP.Net or Winforms apps or another class library.
The benefit would be easier development, and no need to repeatedly do the same thing within each method.
public void RegisterEmployee(int employeeId)
{
try
{
....
}
catch(Exception ex)
{
ABC.Logger.Log(ex);
throw;
}
}
You can subscribe to global event handler like AppDomain.UnhandledException and check the method that throws exception:
AppDomain.CurrentDomain.UnhandledException += CurrentDomainOnUnhandledException;
private static void CurrentDomainOnUnhandledException(object sender, UnhandledExceptionEventArgs unhandledExceptionEventArgs)
{
var exceptionObject = unhandledExceptionEventArgs.ExceptionObject as Exception;
if (exceptionObject == null) return;
var assembly = exceptionObject.TargetSite.DeclaringType.Assembly;
if (assembly == //your code)
{
//Do something
}
}

Idiomatic way to throw exceptions to a delegate

I have a monitor class which monitors a device and reports if that device successfully receives usable data. This can happen anytime.
A client creates its own monitor by passing delegates, starts it and waits for either the successfully read data or a kind of domain specific exception type (one base exception type)
What would be the idiomatic way of throwing subtypes of the base exception type and enable the client to respond to each subtype individually?
public class MyMonitor
{
private SuccessHandler _successHandler;
private ErrorHandler _errorHandler;
public delegate void SuccessHandler(MyDTO result);
public delegate void ErrorHandler(MyBaseException exception);
public MyMonitor(SuccessHandler successHandler, ErrorHandler errorHandler) {
_successHandler = successHandler;
_errorHandler = errorHandler;
}
public void start() {
try {
_successHandler(new MyDTP().doSomethingRisky());
} catch(Exception e) {
_errorHandler(e);
}
}
}
public class Client {
static void Main(string[] args) {
MyMonitor monitor = new MyMonitor(new MyMonitor.SuccessHandler(handleSuccess), new MyMonitor.ErrorHandler(handleException));
monitor.start();
}
static void handleSuccess(MyDTO result) {
// do something with result
}
static void handleException(MyBaseException e) {
try {
throw e;
} catch(UserException mbe) {
// present message to user
} catch(DataNotFoundException se) {
// log error and show generic error message
} catch(UnexpectedException ue) {
// log error and try to hide it from the user
}
}
}
So, why don't you handle the exceptions in your main instead of the monitor-class?
If that isn't an option, you have (at least) two alternatives:
static void handleException(MyBaseException e)
{
if (e is UserException)
{
// present message to user
}
else if (e is DataNotFoundException)
{
// log error and show generic error message
}
elseif (e is UnexpectedException)
{
// log error and try to hide it from the user
}
else
{
// might want to rethrow the exception, do a general handling,...
}
}
That way you don't have to rethrow the exception, just to catch it again.
But this can get ugly if you have many subtypes to handle and here is where multidispatch comes in.
static void HandleException(MyBaseException e)
{
HandleSubException((dynamic)e);
}
static void HandleSubException(MyBaseException e)
{
// might want to rethrow the exception, do a general handling,...
}
static void HandleSubException(DataNotFoundExceptione)
{
// log error and show generic error message
}
static void HandleSubException(UnexpectedException e)
{
// log error and try to hide it from the user
}
static void HandleSubException(UserExceptione)
{
// present message to user
}
Now you can tend to each exception in its own method and is much easier to read and maintain.
Having said that, I'm not entirely sure if this falls under best practice.

Categories

Resources