Exception Handling : AOP vs Classic Handling? - c#

I'm upgrading a Plugin Loading Engine (.NET) which does the following :
Loads Plugins
Connects them to the appropriate Data Source
Launchs the Plugins
Displays the results
All the plugins implement the same Interface : IPluginand each one is launched in a separate BackGroundWorker. All the BackgroundWorkers are managed by a module called Host.
My problem is the Errors/Exceptions Handling. The engine is already deployed and I want to find an elegant way to handle the Errors/Exceptions that could be thrown when the plugins run. Some Exceptions are caught in the plugins but not all of them.
I was thinking about a separate layer that could catch the errors and treat them for all the plugins.
I imagined a kind of Context attached to each Plugin which contain its progress level (BackgroundWorker.ReportProgress), its status, the thrown exceptions(using the RunWorkerCompletedEvent) but the errors are thrown only after the BackgroundWorker stops. I would like to interrupt it when an exception is thrown.
I was also thinking that Aspect Oriented Programming could be a great way. I took a look on the net and found some framework like Spring.NET. But not sure if it could be appropriate in my case.
[UPDATE]
Here are more design details as requested :
IPlugin Interface : Called AbstractEnvChecker :
The application is a Rich Client App. After compiling the plugins, the generated DLL are loaded and a List is displayed to the users in a simple Windows Form. The user selects then the plugins to launch and the Plugin.DoWork() method is called.
And here is how the Host launches the selected Plugins :
void LaunchPlugin(AbstractEnvChecker p_Plugin)
{
if (p_Plugin != null)
{
BackgroundWorker l_BackgroundWorker = new BackgroundWorker();
l_BackgroundWorker.WorkerReportsProgress = true;
l_BackgroundWorker.WorkerSupportsCancellation = true;
l_BackgroundWorker.DoWork +=
new DoWorkEventHandler(bw_DoWork);
l_BackgroundWorker.ProgressChanged +=
new ProgressChangedEventHandler(bw_ProgressChanged);
l_BackgroundWorker.RunWorkerCompleted +=
new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
m_PluginByThreadMap.Add(l_BackgroundWorker, p_Plugin);
l_BackgroundWorker.DoWork += p_Plugin.DoWork;
l_BackgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(l_BackgroundWorker_RunWorkerCompleted);
l_BackgroundWorker.RunWorkerAsync(p_Plugin);
}
}
Is AOP an appropriate solution to add the Error Handling Layer ?

The simplest way would be to just wrap the IPlugin.DoWork() method in a try/catch clause. something like this:
l_BackgroundWorker.DoWork += (o, e) => ExecutePlugin(o, e, p_plugin);
private void ExecutePlugin(object sender, DoWorkEventArgs e, IPlugin plugin)
{
try
{
plugin.DoWork(o, e);
}
catch (Exception e)
{
//do something with the error. disable the plugin maybe?
}
}
If this work then using Spring just for the Error handling is a bit overkill in my opinion.
Something additional you could do is throwing a custom exception (e.g. a PluginException) and handle those globally in your Application, this can be achieved by attaching to:
Application.ThreadException and AppDomain.CurrentDomain.UnhandledException events

Spring.net uses dynamic weaving, which basically means that at runtime Spring.net aop can wrap exception handlers around method calls. But Spring.net aop needs a seam to position it's interceptor in.
If your plugins should be loaded into a UI, then the user (probably) can invoke methods that don't go through the host or IPlugin interface at all, making it hard (if not impossible) for spring.net aop to intercept and wrap exception handlers.
If your host is a console application or service that calls myPlugin.DoWork(), then it's definitely possible to intercept any exceptions thrown by the plugin using Spring.net aop. If you could provide a bit more detail (see comments to your question) then I can show you how to do this.
Below an example that uses Spring.net AOP to proxy a plugin instance and wrap it with an interceptor, that catches a thrown exception and delegates it back to the host. Note that you can do this without AOP too ... that's up to you.
using System;
using AopAlliance.Intercept;
using NUnit.Framework;
using Spring.Aop.Framework;
namespace Aop
{
[TestFixture]
public class SimpleProxyFactoryTests
{
[Test]
public void Main()
{
var host = new Host();
var mp = new SimplePlugin();
var pf = new ProxyFactory(mp);
pf.AddAdvice(new DelegateToHostExceptionHandlingAdvice(host));
var proxy = (IPlugin)pf.GetProxy();
proxy.DoWork();
}
}
public interface IPlugin
{
void DoWork();
}
public class Host
{
public void HandleExceptionFromPlugin(Exception ex)
{
Console.WriteLine("Handling exception: {0}", ex.Message);
}
}
public class SimplePlugin : IPlugin
{
public void DoWork()
{
Console.WriteLine("Doing it and throwing an exception ... ");
throw new ApplicationException("Oops!");
}
}
public class DelegateToHostExceptionHandlingAdvice : IMethodInterceptor
{
private readonly Host _host;
public DelegateToHostExceptionHandlingAdvice(Host host)
{
_host = host;
}
public object Invoke(IMethodInvocation invocation)
{
try
{
return invocation.Proceed();
}
catch (Exception ex)
{
_host.HandleExceptionFromPlugin(ex);
return null;
}
}
}
}
Discussion
I hope I've shown you how you could leverage an aop framework to do exception handling. As Sebastian mentions in his answer, using Spring aop only for exception wrapping might be considered overkill - and I agree; compare the simplicity of his code example to the complexity of mine. Imagine explaining either one to a new developer on your team.
Spring aop starts to "shine" when you use it in combination with the Spring IOC container.

Related

Safely release dependency injected wcf client in .net core

I want to use Microsoft's dependency injection in .Net Core (2.2) to inject and safely release WCF clients. I'm using the "WCF Web Service Reference Provider Tool" in VS2019 to add WCF proxy classes to my solution. Using Microsoft.Extensions.DependencyInjection I can register the clients in the services collection, but I can't seem to find a way of hooking into a release lifecycle event (as can be done in various other IoC frameworks, e.g. Autofac), to add code for doing a safe release according to Microsoft's recommendations described here: https://learn.microsoft.com/en-us/dotnet/framework/wcf/samples/use-close-abort-release-wcf-client-resources
Is there any way of doing something like that in the quite basic dependency injection functionality that comes with .Net Core framework? Or am I forced to use 3rd party IoC framework?
Pseudo code:
So basically I want to do something like this:
// Register the channel factory for the service. Make it
// Singleton since you don't need a new one each time.
services.AddSingleton(p => new ChannelFactory<IWcfService>(
new BasicHttpBinding(),
new EndpointAddress("http://localhost/WcfService")));
// Register the service interface using a lambda that creates
// a channel from the factory.
// TODO: need a way to handle proper disposal, e.g. like OnRelease().
services.AddTransient<IWcfService>(p =>
p.GetService<ChannelFactory<IWcfService>>().CreateChannel())
.OnRelease(CloseChannel); // <---This is what I would like to do
static void CloseChannel<T>(T channel)
{
var disp = (ICommunicationObject) channel;
try
{
if (disp.State == CommunicationState.Faulted)
disp.Abort();
else
disp.Close();
}
catch (TimeoutException)
{
disp.Abort();
}
catch (CommunicationException)
{
disp.Abort();
}
catch (Exception)
{
disp.Abort();
throw;
}
}
But I need a way to hook into the service release lifecycle event, e.g. something like .OnRelease() in Autofac, so I can do proper disposal.
I dont know if you still need a response, but on my end to resolve this issue I implemented the dispose into the partial class.
Each time the wcf client is disposed the correct clean up is made:
public partial class MyWcfClient : IDisposable
{
protected void Dispose(bool disposing)
{
if (disposing)
{
bool success = false;
try
{
if (State != CommunicationState.Faulted)
{
Close();
}
success = true;
}
finally
{
if (!success)
{
Abort();
}
}
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}

AOP with Autofac and DynamicProxy2 Exception Handling

I'm trying to centrally manage Exception handling for a certain method but I can't seem to get there.
public class ExceptionInterceptor : IInterceptor
{
private readonly Logger _logger;
public ExceptionInterceptor(Logger logger)
{
_logger = logger;
Measure.Configure(new StatsdConfig());
}
public void Intercept(IInvocation invocation)
{
try
{
invocation.Proceed();
//if ((Task<System.Threading.Tasks.VoidTaskReturn>) invocation.ReturnValue.Status == "Failed")
//{
// throw new Exception(invocation.ReturnValue.Exception[0]);
//}
}
catch (Exception e)
{
var errorMessage =
String.Format(
"An error occurred while retrieving fund data. Error Message: {0} Inner Exception: {1}",
e.Message, e.InnerException != null ? e.InnerException.Message : "<None>");
_logger.Log(errorMessage);
Measure.Counter("Exception", 1);
Measure.Event("Exception", errorMessage);
throw;
}
}
I'm wiring this up in a module like so:
builder.RegisterType<DataConsumer>().
As<IConsumer<DataRequest>>().
EnableInterfaceInterceptors().
InterceptedBy(typeof(ExceptionInterceptor));
builder.RegisterType<ExceptionInterceptor>().AsSelf();
var loggingInterceptor = new LoggingInterceptor(Logger);
builder.Register(c => loggingInterceptor);
However when I throw an exception in the method invocation this doesn't bubble up to the interceptor as an exception thrown, so it never enters the catch block.
Is there any way to catch the intercepted method's exception in the interceptor?
I also can't access the invocation.ReturnValue.Status for some reason and as such can't test whether there was a thrown exception in order to re-throw.
Can anyone shed some light into what I might nor be doing right here?
Ta
I'm having a difficult time reproducing your issue due to a bit of incomplete information. For example, you noted that the IConsumer<T> interface is a MassTransit interface, but the interface in MassTransit isn't generic. It also specifically mentions that the interface is supposed to be a marker just for IoC containers, which may have some implications on your wire-up.
First, let's post a working exception handling example. To be self-contained, I'll create an IWorker<T> interface in place of IConsumer<T> and a simple implementation:
public interface IWorker<T>
{
bool DoWork(T message);
}
public class StringWorker : IWorker<string>
{
public bool DoWork(string message)
{
throw new DivideByZeroException();
}
}
Now I'll create a simple exception logger that just pipes info to the console.
public class ExceptionLogger : IInterceptor
{
private readonly TextWriter _output;
public ExceptionLogger(TextWriter output)
{
_output = output;
}
public void Intercept(IInvocation invocation)
{
try
{
invocation.Proceed();
}
catch(Exception ex)
{
_output.WriteLine("Logged Exception: {0}", ex.Message);
throw;
}
}
}
I can then wire it up and see it in action like this:
var builder = new ContainerBuilder();
builder.RegisterInstance(Console.Out).As<TextWriter>();
builder.RegisterType<ExceptionLogger>();
builder.RegisterType<StringWorker>()
.As<IWorker<string>>()
.EnableInterfaceInterceptors()
.InterceptedBy(typeof(ExceptionLogger));
var container = builder.Build();
var worker = container.Resolve<IWorker<string>>();
worker.DoWork("Test!");
When I run this, I see on the console just before the program dies (with the unhandled exception - note my handler didn't swallow it, just logged it):
Logged Exception: Attempted to divide by zero.
So it's working.
I think there's more in your environment that may be causing trouble here. It could be something you think is unrelated but is actually important.
General things to investigate:
Temporarily update your DataConsumer to throw an exception immediately inside one of the interface methods. After building your container, resolve a, IConsumer<DataRequest> and call that interface method. Does it get logged?
Look at the places that you're expecting to see logging occur. Are you resolving and working with IConsumer<DataRequest> or something else? It's wrapping the interface methods, not the object type, so not all methods are covered.
Set a breakpoint in the interceptor and see if any invocation at all is passing through it. It won't catch exceptions if it's not being hit. :)
Check to see if there's any other exception handling policies or code in action. For example, some folks use the Enterprise Library exception handling block to deal with exceptions and that may be interfering with your work here.
I've not used MassTransit, but check to see if there's any other object proxying going on. (Doubtful, but I know I've run into this with products like Glimpse, so you end up with proxies wrapped around proxies, which becomes challenging.)
Is the exception actually happening in the place you think it is? It may be happening and getting handled somewhere that isn't wrapped with the proxy.
Basically, reduce the working pieces to the smallest set possible until you can see it working, then slowly expand until you find the place it breaks down. I don't know if any of these apply to your situation, but these are the things I'd start looking at if I was troubleshooting.
But... exception handling in an AOP fashion using interceptors does work, so it's something else going on that's causing the challenge.
It seems that it isn't possible for the exception thrown in the target proxy to be pushed up to the interceptor, and as such what I was trying to do didn't work. I ended up handling the exceptions at the class they occur.
Disappointed I didn't manage to get this to work the way I was intending to.

System.Web.Services.WebService - Is it possible to isolate the service per client

I have an legacy System.Web.Services.WebService (not WCF) that I have to maintain.
Ocassionly I run into some wired behaviours that I would describe as race conditions.
Either the service hangs and has to be restarted.
Sometimes I get this exception:
System.NotSupportedException: Multiple simultaneous connections
or connections with different connection strings inside the same
transaction are not currently supported.
at MySql.Data.MySqlClient.MySqlConnection.Open()
...
I know whats the root cause. The service utilizes a lib that talks to mysql and was not designed with webservices in mind. Unfortunatly I cannot change this lib.
One example webmethod looks like this:
[WebMethod(EnableSession = true)]
public void DoSomething()
{
var login = this.Session["login"] as LoginDetails;
ExternalLib.SetLoginData(login.Schema, login.User, login.Pass);
ExternalLib.PerformTask();
}
So the problem here is this:
ExternalLib.SetLoginData just set's some global vars
ExternalLib.PerformTask performs database calls, some inside a transaction.
The process is like 1. Create MySqlConnection or take it from cache 2. Create MySqlCommand 3. Execute Command 4. Dispose command
Client a) calls DoSomething() and I init his connection. Half way done with his job Client b) calls DoSomething() which apparently changes the Login-Data for client a and the next call inside the transaction will use the login from client b) which causes the transaction.
Anyway, I know this is a bad design but my question is how to workaround this.
Currently (since I only have 10 clients) I created a dedicated Website on a differnet port which all point to the same root directory but this is an akward solution.
Maybe there is a possibility to run every session inside its on realm. Any suggestions. If I understand this page correctly for WCF is is the default behaviour: http://msdn.microsoft.com/en-us/magazine/cc163590.aspx
Per-Call Services
Per-call services are the Windows Communication
Foundation default instantiation mode. When the service type is
configured for per-call activation, a service instance, a common
language runtime (CLR) object, exists only while a client call is in
progress. Every client request gets a new dedicated service instance.
Seeing as this is probably a threading issue you can lock the ExternalLib to prevent separate instances from calling the code.
public class ExtenalLibWrapper
{
private static object Locker = new object();
public void DoSomething(LoginDetails details)
{
lock(Locker)
{
ExternalLib.SetLoginData(login.Schema, login.User, login.pass);
ExternalLib.PerformTask();
}
}
}
I already wrapped all my public methods in a neat execute wrapper to provide global exception logging.
This forces my webservice to process one request after another, but like I mentioned, the max. number of simultanious clients is 10
public class MyService : System.Web.Services.WebService
{
[WebMethod(EnableSession = true)]
public static int Add(int value1, int value2)
{
return Execute(() =>
{
var calculator = new Calculator();
return calculator.Add(value1, value2);
});
}
private static Logger logger =
LogManager.GetLogger(typeof(MyService).Name);
private static System.Threading.SemaphoreSlim ss =
new System.Threading.SemaphoreSlim(1, 1);
private void Execute(Action method)
{
ss.Wait();
try { method.Invoke(); }
catch (Exception ex)
{
logger.FatalException(method.Method + " failed", ex); throw;
}
finally { ss.Release(); }
}
private T Execute<T>(Func<T> method)
{
ss.Wait();
try { return method.Invoke(); }
catch (Exception ex)
{
logger.FatalException(method.Method + " failed", ex); throw;
}
finally
{
ss.Release();
}
}
}

MAF (System.Addin) property of serializable type in contract?

We are testing the MAF addin to use as our addin framework. But we get stuck at a basic issue. Can we use serializable types as IContract parameters?
Both the contract and the parameter type is defined in the same assembly:
public interface IHostContract : IContract
{
void SetCurrent(TheValue tagValue); // does not work
void SetCurrentSimple(double value); // works fine
}
[Serializable]
public sealed class TheValue
{
public int Id { get; set; }
public double Value { get; set; }
}
We are able to get everything up and running. Calling the SetCurrent results in an exception:
AppDomainUnloadedException :
The application domain in which the thread was running has been unloaded.
Server stack trace:
at System.Threading.Thread.InternalCrossContextCallback(Context ctx, IntPtr ctxID, Int32 appDomainID, InternalCrossContextDelegate ftnToCall, Object[] args)
at System.Runtime.Remoting.Channels.CrossAppDomainSink.DoTransitionDispatch(Byte[] reqStmBuff, SmuggledMethodCallMessage smuggledMcm, SmuggledMethodReturnMessage& smuggledMrm)
at System.Runtime.Remoting.Channels.CrossAppDomainSink.SyncProcessMessage(IMessage reqMsg)
Exception rethrown at [0]:
Loading and running of plugins:
public void Run(string PluginFolder)
{
AddInStore.Rebuild(PluginFolder);
Collection<AddInToken> tokens = AddInStore.FindAddIns(typeof(Plugins.IPlugin), PluginFolder);
foreach (var token in tokens)
{
Console.WriteLine("Found addin: " + token.Name + " v" + token.Version);
try
{
var plugin = token.Activate<Plugins.IPlugin>(AddInSecurityLevel.FullTrust);
plugin.PluginHost = this;
plugin.Start();
plugin.Stop();
}
catch (Exception exception)
{
Console.WriteLine("Error starting plugin: " + exception.Message);
}
}
}
Plugin:
[System.AddIn.AddIn("Plugin1", Version = "1.0.0")]
public class Plugin1 : IPlugin
{
private int started;
public Plugin1()
{
Console.WriteLine("Plugin 1 created");
}
public void Start()
{
Console.WriteLine("Plugin 1 started: {0}", started);
started++;
var tagValue = new TheValue { Id = 1, Value = 4.32 };
PluginHost.SetCurrent(tagValue);
}
public void Stop()
{
Console.WriteLine("Plugin 1 stopped");
}
public IPluginHost PluginHost { get; set; }
}
You need to follow the guidelines for lifetime management. In each contract-to-view adapter you need to store a ContractHandle. This is necessary for the lifetime management of the proxies that System.AddIn implicitly creates (remember that System.AddIn is based on .NET Remoting).
Taken from MSDN:
The ContractHandle is critical to lifetime management. If you fail to
keep a reference to the ContractHandle object, garbage collection will
reclaim it, and the pipeline will shut down when your program does not
expect it. This can lead to errors that are difficult to diagnose,
such as AppDomainUnloadedException. Shutdown is a normal stage in the
life of a pipeline, so there is no way for the lifetime management
code to detect that this condition is an error.
If you decide to use System.AddIn in your application then you need the PipelineBuilder. In the discussion board you will find help on how to make it work with VS2010 (it is quite simple). I guess it will not be hard to make it work with VS2012 as well. This tool will take care all the System.AddIn intricacies for you. All you will need to do is create the contracts and PipelineBuilder will create the rest of the pipeline for you. It will also make sure that you follow the guidelines on how to build your contracts which is the most important thing with System.AddIn.
Before you decide on an add-in framework, don't forget to check out MEF. MEF can be used with Autofac and provide versioning through adapters. IMHO, the only reason that anyone should choose System.AddIn is for the isolation feature. But note that 100% isolation is only when the add-ins are loaded in a different process from the host.

log all handled exception using log4net + AutoFac interceptor

I'm using Log4Netto log my application. currently I want to log every methods enter in my app (for testing purpose). Therefor I'm using AutoFac interception capabilities, somethong like this:
builder.Register(c=> new MyClass()).As<IMyInterface>().EnableInterfaceInterceptors().InterceptedBy(typeof(LoggerClass));
builder.Build();
and my LoggerClass looks something like this:
public class LoggerClass : StandartInterceptor
{
ILog _log = LogManager.GetLogger(typeof(LoggerClass));
override void PreProceed(IInvocation invovation)
{
_log.Info(string.Format("entering method: {0}",invocation.Method.Name);
}
}
for now this implementation will print message for all method invocation (the interceptor catches all methods entries).
Question
I would like to use this interception mechanism to log every handled Exception.
For example instead of coding this:
catch (myException ex)
{
_log.Error(string.Format("catches exception {0}", ex.Message));
}
I'll have extra method in my LoggerClass that will wrap the catch statement and inject to it log message.
Is there any way to do it using Log4Net? since basically the interceptor work around the method, and I need it to work inside method.
On your caught exceptions, you will never enter into the catch block of your interceptor.
Therefore, in your method when you catch an exception, you can handle the caught exception in an additional intercepted method that will get logged appropriately. This will inflate your codebase a bit, but you will end up getting the information you want without sacrificing your architecture.
public void InterceptedMethod()
{
try
{
//Some code that fails.
}
catch
{
HandleException();
}
}
//Intercept this method also
public void HandleException()
{
}

Categories

Resources