As an option, RealProxy may help. There's a nice article by Bruno Sonnino on this topic: Aspect-Oriented Programming : Aspect-Oriented Programming with the RealProxy Class.
Also you may be able to use AOP features of Unity (the DI framework) for this purpose. There's a nice article on this topic by Dino Esposito: Interceptors in Unity.
Using a weaving tool like Fody is also another option.
Creating proxy classes using Reflection.Emit could be another option.
Example - AOP using RealProxy
A LogAttribute which Has MethodExecuting and MethodExecuted and runs before and after methods
Using RealProxy, you can create a proxy for your class, so that when you call a method, the Invoke
method of the proxy will run and you can run any logic there, for example you can run something before or after the actual method call.
In this example, I show how you can create a MethodFilterAttribute
having two methods OnMethodExecuting
and OnMethodExecuted
, and then if you decorate your method with an attribute derived from this attribute, those methods will run before and after executing of the original method.
Looking into the code, you see you don't necessarily need the attributes, and attributes are just there as an extensibility point.
The usage of the code in this example is something like this:
var calc = CalculatorFactory.GetInstance();
var a = calc.Add(1, 2);
var b = calc.Subtract(1, 2);
Which produce the output:
Add executing.
Add executed.
Subtract executing.
Subtract executed.
Using statements
This is an attribute that could be used for methods. It has OnMethodExecuting and OnMethodExecuted methods, and when you get a proxy of your class, and run the methods, these two filter methods will be executed before and after the method which is decorated by this attribute:
using System;
using System.Reflection;
using System.Runtime.Remoting.Messaging;
using System.Runtime.Remoting.Proxies;
using System.Linq;
[AttributeUsage(AttributeTargets.Method)]
public class MethodFilterAttribute : Attribute
{
public int Order { get; set; }
public virtual void OnMethodExecuting(
MethodInfo methodInfo, object[] args) { }
public virtual void OnMethodExecuted(
MethodInfo methodInfo, object[] args, object result) { }
}
LogAttribute
An implementation of MethodFilterAttribute which performs log before and after method execution:
public class LogAttribute : MethodFilterAttribute
{
override public void OnMethodExecuting(
MethodInfo methodInfo, object[] args)
{
Console.WriteLine($"{methodInfo.Name} executing.");
}
override public void OnMethodExecuted(
MethodInfo methodInfo, object[] args, object result)
{
Console.WriteLine($"{methodInfo.Name} executed.");
}
}
The DynamicProxy class
Creates a proxy of your object, and if you run methods of the object, if the methdod is decorated with a method filter attribute, then OnActionExecuting and OnActionExecuted will run.
Looking into the code, you see you don't necessarily need the attributes, and attributes are just there as an extensibility point.
public class DynamicProxy<T> : RealProxy
{
private readonly T original;
public DynamicProxy(T original)
: base(typeof(T))
{
this.original = original;
}
public override IMessage Invoke(IMessage msg)
{
var methodCall = msg as IMethodCallMessage;
var methodInfo = methodCall.MethodBase as MethodInfo;
try
{
var filters = methodInfo.GetCustomAttributes<MethodFilterAttribute>();
if (filters.Any())
{
filters.OrderBy(x => x.Order).ToList()
.ForEach(f => f.OnMethodExecuting(methodInfo, methodCall.InArgs));
}
var result = methodInfo.Invoke(original, methodCall.InArgs);
if (filters.Any())
{
filters.OrderBy(x => x.Order).ToList()
.ForEach(f => f.OnMethodExecuted(methodInfo, methodCall.InArgs, result));
}
return new ReturnMessage(result, null, 0,
methodCall.LogicalCallContext, methodCall);
}
catch (Exception e)
{
return new ReturnMessage(e, methodCall);
}
}
}
ICalculator interface and Calculator class
Methods of Calculator are decorated with Log attribute, which means before and after execution of those methods, log will run.
Please note: In the implementation of the proxy we are looking for the attributes, using the interface. Also having the interface is necessary.
public interface ICalculator
{
[Log]
int Add(int x, int y);
[Log]
int Subtract(int x, int y);
}
public class Calculator : ICalculator
{
public int Add(int x, int y)
{
return x + y;
}
public int Subtract(int x, int y)
{
return x - y;
}
}
CalculatorFactory
The factory which returns a proxy instance of ICalculator:
public class CalculatorFactory
{
public static ICalculator GetInstance()
{
var original = new Calculator();
return new DynamicProxy<ICalculator>(original)
.GetTransparentProxy() as ICalculator;
}
}
Usage
Get an proxied instance of the interface using the factory and run methods:
var calc = CalculatorFactory.GetInstance();
var a = calc.Add(1, 2);
var b = calc.Subtract(1, 2);
Which produce the output:
Add executing.
Add executed.
Subtract executing.
Subtract executed.