Event handling in a disconnected class - c#

I am looking for a way to handle an event raised by class A in class B without coupling the classes.
The situation is that class A is a state machine. I need to send an email when a state of class A becomes Active. I would like to create a class that would listen up for that event and handle it.
I encapsulate the state logic in class A and I instantiate it from a web page (asp.net webform).
I could instantiate the email class inside the webform as well, but I do not wish to do so, in that case, the developer that codes the webform will have to hook up the logic and events. I would like to push all that logic back to the business layer. But I can't seem to have a way to do it.
in BLL
class StateHandler()
{
public event EventHandler StateChanged;
SetState(int newState)
{
// to keep things simple for sample let's assume 2 states
if (newState ==1 ) this.State = "Active";
else this.State = "Inactive";
if (this.StateChanged != null)
this.StateChanged(this, new EventArgs());
}
}
class EmailProvider()
{
SendEmail()
{
// logic to send email
}
}
Asp.net page
....
void OnSubmit_click()
{
StateHandler sh = new StateHandler();
//adds an event handler to the StateChanged event
sh.StateChanged += new EventHandler(sh_StateChanged);
//setting this will fire the StateChanged event
sh.SetState(1);
}

EventBus might be a useful pattern for your case. It is simply a message broker that passes events to registered subscribers. I don't know if there is any C# library for that. But is should be easily implemented. Have a look at guava's EventBus. It's Java but you shoud get the idea.
In a container managed situation like EJB the container handles asynchronous message dispatch. In Java/Java EE again you'd have Message Driven Beans.

Related

Is there a better way to use barcode scanner?

I'm currently using using a BarcodeScanner in UWP App.
To implement it I followed some tutorials on Microsoft docs.
It's working fine but not like I want it to work.
The barcode scanner can only get the value through a DataReceived event.
So when I want to return a value from a BarcodeScanner, it's impossible.
Here I'm registering the scanner :
private static async Task<bool> ClaimScanner()
{
bool res = false;
string selector = BarcodeScanner.GetDeviceSelector();
DeviceInformationCollection deviceCollection = await
DeviceInformation.FindAllAsync(selector);
if (_scanner == null)
_scanner = await BarcodeScanner.FromIdAsync(deviceCollection[0].Id);
if (_scanner != null)
{
if (_claimedBarcodeScanner == null)
_claimedBarcodeScanner = await _scanner.ClaimScannerAsync();
if (_claimedBarcodeScanner != null)
{
_claimedBarcodeScanner.DataReceived += ClaimedBarcodeScanner_DataReceivedAsync;
[...]
}
}
}
And once I'm receiving data it triggers that event :
private static async void ClaimedBarcodeScanner_DataReceivedAsync(ClaimedBarcodeScanner sender, BarcodeScannerDataReceivedEventArgs args)
{
await Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
if (CurrentDataContext != null && CurrentDataContext is IScannable)
{
IScannable obj = (IScannable)CurrentDataContext;
obj.NumSerie = CryptographicBuffer.ConvertBinaryToString(BinaryStringEncoding.Utf8, args.Report.ScanDataLabel);
}
else if (CurrentDataContext != null && CurrentDataContext is Poste)
{
Poste p = (Poste)CurrentDataContext;
string code = CryptographicBuffer.ConvertBinaryToString(BinaryStringEncoding.Utf8, args.Report.ScanDataLabel);
p.CodePoste = code.Substring(0, 6);
}
});
}
And as you can see I'm kind of forced to do everything in that method (updating instances of others classes, etc.).
Currently I'm calling the BarcodeScanner like that in the ViewModel :
public void ScanPosteCodeAsync()
{
BarcodeScannerUtil.ScanBarcodeUtil(CurrentPoste);
}
But I have no control of my CurrentPoste instance and what I would do is more like :
public void ScanPosteCodeAsync()
{
string returnedCode = BarcodeScannerUtil.ScanBarcodeUtil()
this.CurrentPoste.Code = returnedCode;
}
Is there any way to return the value of the scanner in order to use the returned value in my ViewModel ?
Well a similar pattern exists for WPF devs when using MVVM and you need to get/update the models that your view model (VM) is exposing. Perhaps they are in a database. Rather than pollute your nice VM with ugly DB code, a "service" can be passed into the VM. Now, "serivce" doesn't necessarily mean SOA/microservices, maybe its just another class in a different project. The point is you put all your barcode stuff there and when something is received, perhaps it fires an event that your VM listens to or perhaps it just queues it up somewhere ready for your VM to request via the service interface.
I already have all the barcode code in a service class, and there's the problem because I don't want the service class to update my current model. The major issue I have is that I don't know how to do to make my VM listen to the DataReceived event
Well, from what I can see your service is not decoupled from UWP MVVM. For the event, have you considered exposing a secondary event purely for the VM client? I find that works well for me.
Like an event in the VM listening to the data received event ?
Yes, but it doesn't have to be a listending to a physical event type just the concept. C# events imply that can be more than one subscriber which doesn't really make sense for barcode apps. There should only be one foreground reader.
Here I shall use Action<string> to pass the barcode from BarcodeScanner to the client, in this case the VM. By using an Action and moving the barcode processing to the client we keep the BarcodeScanner completely unaware of MVVM. Windows.ApplicationModel.Core.CoreApplication.MainView was making BarcodeScanner incredibly coupled to stuff it shouldn't care about.
First of all we want to decouple everything so first up is an interface representing the important bits of the barcode scanner:
public interface IBarcodeScanner
{
Task<bool> ClaimScannerAsync();
void Subscribe(Action<string> callback);
void Unsubscribe();
}
With that defined we shall pass it into your VM like so:
public class MyViewModel
{
private readonly IBarcodeScanner _scanner;
/// <summary>
/// Initializes a new instance of the <see cref="MyViewModel"/> class.
/// </summary>
/// <param name="scanner">The scanner, dependency-injected</param>
public MyViewModel(IBarcodeScanner scanner)
{
// all business logic for scanners, just like DB, should be in "service"
// and not in the VM
_scanner = scanner;
}
Next we add some command handlers. Imagine we have a button that when clicked, kicks off a barcode subscription. Add the following to the VM:
public async void OnWidgetExecuted()
{
await _scanner.ClaimScannerAsync();
_scanner.Subscribe(OnReceivedBarcode);
}
// Barcode scanner will call this method when a barcode is received
private void OnReceivedBarcode(string barcode)
{
// update VM accordingly
}
Finally, the new look for the BarcodeScanner:
public class BarcodeScanner : IBarcodeScanner
{
/// <summary>
/// The callback, it only makes sense for one client at a time
/// </summary>
private static Action<string> _callback; // <--- NEW
public async Task<bool> ClaimScannerAsync()
{
// as per OP's post, not reproduced here
}
public void Subscribe(Action<string> callback) // <--- NEW
{
// it makes sense to have only one foreground barcode reader client at a time
_callback = callback;
}
public void Unsubscribe() // <--- NEW
{
_callback = null;
}
private void ClaimedBarcodeScanner_DataReceivedAsync(ClaimedBarcodeScanner sender, BarcodeScannerDataReceivedEventArgs args)
{
if (_callback == null) // don't bother with ConvertBinaryToString if we don't need to
return;
// all we need do here is convert to a string and pass it to the client
var barcode = CryptographicBuffer.ConvertBinaryToString(BinaryStringEncoding.Utf8,
args.Report.ScanDataLabel);
_callback(barcode);
}
}
So what was the problem?
In summary you sort of got yourself caught up in somewhat of a circular dependency problem whereby the VM depended on the BarcodeScanner and the BarcodeScanner depended on presentation APIs - something it shouldn't need to know about. Even with the good attempt at abstractions you had in the BarcodeScanner with regards to IScannable (sadly not a case with Poste), the scanning layer is making assumptions as to the type of users using it. It was just to vertical.
With this new approach you could very use it for other types of apps including UWP console apps if you needed to.

Chain modifications in events

ASP.NET Boilerplate have EventBus system and we have events
EntityCreatingEventData
EntityCreatedEventData
EntityDeletingEventData
EntityDeletedEventData
…
But these events work after calling SaveChanges() (data already in DB). We want event system before calling SaveChanges(), when data is not written in DB yet
We also want recursive event system, for example :
creating object A => call EntityCreatingBeforeSaveEventData(a) => in this handle we create new object B and call Repository.Insert(b) => call EntityCreatingBeforeSaveEventData(b)...
And this process calling while exist any modification in DB Context.
Dependent on entity.Id
It is not possible to have a domain event system before calling SaveChanges().
ASP.NET Boilerplate detects changes when calling SaveChanges().
Recursive events can cause an infinite loop — see #1616.
An object with an Id that has not been set (auto-generated) cannot be identified.
If you are dependent on such a system, then it may be poor separation of concerns.
Independent of entity.Id
You can use IEventBus directly.
Trigger event:
public class AManager : DomainService, IAManager
{
public void CreateA()
{
var a = new A();
Repository.Insert(a);
EventBus.Trigger(new EntityCreatingBeforeSaveEventData<A>
{
Property = a.SomeProperty // Can pass other properties
});
}
}
public class BManager : DomainService, IBManager
{
// Similar to above
}
Handle event:
public class AHandler : IEventHandler<EntityCreatingBeforeSaveEventData<A>>, ITransientDependency
{
public IBManager BManager { get; set; }
public void HandleEvent(EntityCreatingBeforeSaveEventData<A> eventData)
{
var aSomeProperty = eventData.Property;
BManager.CreateB();
}
}

How to get the callback to viewmodel from library

I have a UIControl(LatestHeadline) in a view(Home.xaml)(there are around 10 more controls on the same page) which is a textblock.I want to set the text of this control when user clicks on a button(named "Retrieve") from the same view.I have to call the method in utilities library from my viewmodel .The method(GetLatestHeadline) in the utilitieslibrary connects to a web api through webclient class and it fires the (deleagate)method Retrieve_Completed event.
Once this event is completed the required data is retrieved from eventargs paramater e.Now from the utilitieslibrary I want to return the data to the viewmodel so that the I can bind this to the LatestHeadline textblock.
LatestHeadline textblock has binding to a property(named "PropHeadLine") defined in viewmodel.
Is there a way we can achieve this in C#.Net?
If you have access to the utility class from the view model, then surely you can just add a handler to your Retrieve_Completed delegate in the view model.
utilityClass.Retrieve_Completed += UtilityClassRetrieve_Completed;
utilityClass.GetLatestHeadline();
...
public void UtilityClassRetrieve_Completed(EventArgs e)
{
// Do something with your e.New value here in the view model
LatestHeadlineProperty = e.New;
}
Of course, I'm just guessing at what your Retrieve_Completed definition is here, but hopefully you get the idea.
I like the concept of a "callback" for this kind of thing -- specifically, pass an Action<string> from the View Model to the service. The service can then save a reference to the callback, or pass it along to the completed-handler in the "UserState". So, the utilities method would look something like this:
public void GetLatestHeadline(Action<string> callback)
{
_apiClient.Retrieve(userState: callback);
}
private void Retrieve_Completed(object sender, RetrieveCompletedEventArgs args)
{
var callback = args.UserState as Action<string>;
if (callback != null)
callback(args.Result.Headline);
}
Now the view model can pass a callback to the utilities method -- the callback should presumably just set the property "PropHeadLine" to the result value:
private void Button_Click()
{
Utilities.GetLatestHeadline( result => PropHeadLine = result );
}
Expose a Delegate or Event in your service, in your view model just subscribe or hook to
that delegate or event
when ever you want to notify the viewmodel Once a particular operation is completed, just invoke the delegate or event
The method which is hooked in your viewmodel will get called.
In your viewmodel now you can do the necessary actions you want

How to get what event raise in Http Module

I want to get what event in what class cause request from server in Http Module.
I mean foe example : when user clicks on a button in Page1 I want to get : Button1_Click in Page1 class or when user changes a dropdown list selected index in that page I want to get DropdownList1_SelectedIndexChange in Page1 class.
thanks
Page events are associated with the page. modules are lifecycle events. You won't see any click type event from events modules listen to like these from another post
BeginRequest
AuthenticateRequest
AuthorizeRequest
ResolveRequestCache
AcquireRequestState
PreRequestHandlerExecute
PostRequestHandlerExecute
ReleaseRequestState
UpdateRequestCache
EndRequest
HTTPModule Event Execution Order?
Your question is very wide and the following MSDN Library documentation references might help you to understand this process:
ASP.NET Application Life Cycle Overview for IIS 5.0 and 6.0
ASP.NET Application Life Cycle Overview for IIS 7.0
Here are the events and the request pipeline for ASP.NET 4.0:
Validate the request, which examines the information sent by the browser and determines whether it contains potentially malicious markup.
Perform URL mapping, if any URLs have been configured in the UrlMappingsSection section of the Web.config file.
Raise the BeginRequest event.
Raise the AuthenticateRequest event.
Raise the PostAuthenticateRequest event.
Raise the AuthorizeRequest event.
Raise the PostAuthorizeRequest event.
Raise the ResolveRequestCache event.
Raise the PostResolveRequestCache event.
[IIS 5.0/6.0] Based on the file name extension of the requested resource (mapped in the application's configuration file), select a class that implements IHttpHandler to process the request. If the request is for an object (page) derived from the Page class and the page needs to be compiled, ASP.NET compiles the page before creating an instance of it. [IIS 7.0] Raise the MapRequestHandler event. An appropriate handler is selected based on the file name extension of the requested resource. The handler can be a native-code module such as the IIS 7.0 StaticFileModule or a managed-code module such as the PageHandlerFactory class (which handles .aspx files).
Raise the PostMapRequestHandler event.
Raise the AcquireRequestState event.
Raise the PostAcquireRequestState event.
Raise the PreRequestHandlerExecute event.
Call the ProcessRequest method (or the asynchronous version IHttpAsyncHandler.BeginProcessRequest) of the appropriate IHttpHandler class for the request. For example, if the request is for a page, the current page instance handles the request.
Raise the PostRequestHandlerExecute event.
Raise the ReleaseRequestState event.
Raise the PostReleaseRequestState event.
Perform response filtering if the Filter property is defined.
Raise the UpdateRequestCache event.
Raise the PostUpdateRequestCache event.
[IIS 7.0] Raise the LogRequest event.
[IIS 7.0] Raise the PostLogRequest event.
Raise the EndRequest event.
Raise the PreSendRequestHeaders event.
Raise the PreSendRequestContent event.
Note: The MapRequestHandler, LogRequest, and PostLogRequest events are supported only if the application is running in Integrated mode in IIS 7.0 and with the .NET Framework 3.0 or later.
References to follow: HTTP Handlers and HTTP Modules Overview
The events that you are looking for are specific to the asp.net page model. Http module is at a lower level ( transport based ) and would not be used to capture page events.
Can you provide more detail please?
I'm going to suggest that if you are starting with a website to inherit a class from page and make all your pages to inherit from this class.
public abstract class LoggingPage : System.Web.UI.Page
{
protected override void RaisePostBackEvent(
IPostBackEventHandler sourceControl, string eventArgument)
{
//doing something with the information.
EventLog.WriteEntry("Page event for " + sourceControl.UniqueID + " at " + this.Request.Url);
//then call the base implementation
base.RaisePostBackEvent(sourceControl, eventArgument);
}
}
If you need to get the information of the event because of exceptions and you never get to RaisePostBackEvent, then you will need to handle PreRequestHandlerExecute of HttpApplication in your module and get 2 fields in the request
public class LoggingModule : System.Web.IHttpModule
{
private HttpApplication _app;
public void Dispose()
{
this._app.PreRequestHandlerExecute -= new EventHandler(this.PreRequestExecution);
}
public void Init(HttpApplication application)
{
this._app = application;
this._app.PreRequestHandlerExecute += new EventHandler(this.PreRequestExecution);
}
private void PreRequestExecution(object sender, EventArgs e)
{
var request = this._app.Context.Request;
var target = request.Form["__EVENTTARGET"];
var arg = request.Form["__EVENTARGUMENT"];
//this gives you enough information about events
//you need to check if they are null before using them (target and arg)
//through the same request you can get extra info including URL
}
}
UPDATE:
If your concern is security and since you have roles implemented in your system I would recommend to make use of System.Security.Permissions.PrincipalPermissionAttribute to decorate your event handlers like this:
protected void Page_Load()
{
myButton.Click += new EventHandler(this.myButton_Click);
}
[PrincipalPermission(SecurityAction.Demand, Role = "Administrator")]
private void myButton_Click(object sender, EventArgs e)
{
//your code to handle the event
}
You can add the attributes multiple times to fit your needs.
hope this helps.
Regarding your question I noticed the remark:
I want to develop a security system that add some attributes to event (for roles that can access to this event) and check authorization using session and this attributes.I want to get event name and then attributes that belong to it and check authorization
Because events are registered in the class, which is not available during the Module/Handler phase, what you ask cannot be done.
However, there are always options, and I can see what you're trying to achieve :-) My solution is to simply register all events by calling a method (myEventHooks.HookAll(this);) and in the hook implementation check security (and either throwing an exception or removing all registered events when the check fails -- fill in the gaps however you see fit).
Note that when you change the Children / tree, you need to update the event hooks to bind to all their methods as well. The easiest way to do the binding is by overriding RaisePostBackEvent in a base page and then hooking everything.
This solution can be improved in a couple of different ways; most obviously making the handling more generic as well and lack of the 0-parameter handler. I kept it as simple as possible for clearance. This should get you started.
My solution has 2 parts: (1) a generic hook class and (2) the implementation in the form. Currently the solution is lazy, e.g. I put event handlers at the end, not at the front of the queue. You should be able to fix this by using GetInvocationList or something similar.
The generic hook class basically hooks the events and fires when an event is called:
public class EventHooks
{
private class EventHooksEquality : IEqualityComparer<Tuple<string, object>>
{
public bool Equals(Tuple<string, object> x, Tuple<string, object> y)
{
return x.Item1.Equals(y.Item1) && object.ReferenceEquals(x.Item2, y.Item2);
}
public int GetHashCode(Tuple<string, object> obj)
{
return obj.Item1.GetHashCode();
}
}
public void CheckSecurity(string eventName, object container)
{
// Add your security code that checks attributes and the likes here
}
private abstract class BaseHookHandler
{
protected BaseHookHandler(object container, string eventName, EventHooks hooks)
{
this.hooks = hooks;
this.container = container;
this.eventName = eventName;
}
protected string eventName;
protected object container;
protected EventHooks hooks;
}
private class HookHandler<T1> : BaseHookHandler
{
public HookHandler(object container, string eventName, EventHooks hooks)
: base(container, eventName, hooks)
{
}
public void Handle(T1 t1)
{
hooks.CheckSecurity(eventName, container);
}
}
private class HookHandler<T1, T2> : BaseHookHandler
{
public HookHandler(object container, string eventName, EventHooks hooks)
: base(container, eventName, hooks)
{
}
public void Handle(T1 t1, T2 t2)
{
hooks.CheckSecurity(eventName, container);
}
}
// add more handlers here...
public void HookAll(object obj)
{
foreach (var eventHandler in obj.GetType().GetEvents())
{
Hook(obj, eventHandler.Name);
}
}
public void Hook(object obj, string eventHandler)
{
if (obj == null)
{
throw new Exception("You have to initialize the object before hooking events.");
}
// Create a handler with the right signature
var field = obj.GetType().GetEvent(eventHandler);
var delegateInvoke = field.EventHandlerType.GetMethod("Invoke");
Type[] parameterTypes = delegateInvoke.GetParameters().Select((a) => (a.ParameterType)).ToArray();
// Select the handler with the correct number of parameters
var genericHandler = Type.GetType(GetType().FullName + "+HookHandler`" + parameterTypes.Length);
var handlerType = genericHandler.MakeGenericType(parameterTypes);
var handlerObject = Activator.CreateInstance(handlerType, obj, eventHandler, this);
var handler = handlerType.GetMethod("Handle");
// Create a delegate
var del = Delegate.CreateDelegate(field.EventHandlerType, handlerObject, handler);
// Add the handler to the event itself
field.AddEventHandler(obj, del);
}
}
Usage in the base class can be done as follows (example):
protected override void RaisePostBackEvent(
IPostBackEventHandler sourceControl, string eventArgument)
{
// Hook everything in Page.Controls
Stack<Control> st = new Stack<Control>();
st.Push(Page);
while (st.Count > 0)
{
var control = st.Pop();
eventHooks.HookAll(control);
foreach (Control child in control.Controls)
{
st.Push(child);
}
}
// Raise events
base.RaisePostBackEvent(sourceControl, eventArgument);
}
private EventHooks hooks = new EventHooks();

Exception Handling : AOP vs Classic Handling?

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.

Categories

Resources