Created
May 17, 2012 03:28
-
-
Save jpolvora/2716022 to your computer and use it in GitHub Desktop.
My custom implementation of DynamicDecorator from Gary H Guo
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Created by por Jone Polvora | |
// Twitter: @jpolvora | |
// WebSite: http://silverlightrush.blogspot.com | |
// Based on Dynamic Decorator / CBO Extender Project (http://www.codeproject.com/Articles/275292/Component-Based-Object-Extender) | |
// Depends on Impromptu-Interface (https://code.google.com/p/impromptu-interface/_ | |
// Last Update: 21/05/2012 10:40AM GMT -04:00 | |
// Brazil | |
/* USAGE EXAMPLE | |
static void DecorateContext(DbContext ctx) | |
{ | |
/********************/ | |
//fluent interface | |
/********************/ | |
var proxyCtx = ObjectProxyFactory | |
.Configure<IUnitOfWork>(ctx) //will return an IUnitOfWork object | |
.WithPreAspect(pre => | |
LogEntering(pre.CallCtx.MethodName)) //pre_decoration | |
.WithPostAspect(pos => | |
LogExiting(pos.CallCtx.MethodName)) //post_decoration | |
.FilterMethods(m1 => | |
m1.SaveChanges(), | |
m2 => m2.ToString(), | |
m3 => m3.Dispose()) | |
.Build(); //create and return | |
//execute a method | |
proxyCtx.SaveChanges(); //will be intercepted | |
/********************/ | |
//using buider | |
/********************/ | |
var builder = ObjectProxyFactory.Configure<IUnitOfWork>(ctx); | |
builder.WithPostAspect(p => LogExiting(p.CallCtx.MethodName)); | |
var proxyResult = builder.Build(); | |
proxyResult.SaveChanges(); | |
} | |
static void LogEntering(string methodName) | |
{ | |
System.Console.WriteLine(string.Format("Entering {0}", methodName)); | |
} | |
static void LogExiting(string methodName) | |
{ | |
System.Console.WriteLine(string.Format("Exiting {0}", methodName)); | |
} | |
*/ | |
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Linq.Expressions; | |
using System.Reflection; | |
using System.Runtime.Remoting; | |
using System.Runtime.Remoting.Messaging; | |
using System.Runtime.Remoting.Proxies; | |
using ImpromptuInterface; | |
namespace DynamicProxy.AllInOneFile | |
{ | |
public class AspectContext<TTarget> | |
{ | |
public TTarget Target { get; private set; } | |
public IMethodCallMessage CallCtx { get; private set; } | |
public dynamic Parameters { get; set; } | |
public bool Abort { get; set; } | |
public AspectContext(TTarget target, IMethodCallMessage callCtx, dynamic parameters) | |
{ | |
Target = target; | |
CallCtx = callCtx; | |
Parameters = parameters; | |
} | |
} | |
public interface IAspectBehavior<TTarget> | |
{ | |
string[] MethodsToIntercept { get; } | |
void PreAspect(AspectContext<TTarget> ctx); | |
void PostAspect(AspectContext<TTarget> ctx); | |
} | |
public static class Helpers | |
{ | |
#region public helper methods to get methodnames | |
public static string[] GetMethodNames<T>(params Expression<Action<T>>[] expressions) | |
{ | |
if (expressions == null) | |
return Enumerable.Empty<string>().ToArray(); | |
return expressions.Select(expression => expression.ExtractMethod()).ToArray(); | |
} | |
private static string ExtractMethod<T>(this Expression<Action<T>> expression) | |
{ | |
var methodCall = expression.Body as MethodCallExpression; | |
if (methodCall == null) | |
{ | |
throw new ArgumentException("expression"); | |
} | |
var method = methodCall.Method; | |
return method.Name; | |
} | |
public static string[] GetMethodNamesFromPropertyInfo(bool getters = true, bool setters = true, params PropertyInfo[] propertyInfos) | |
{ | |
var methodsProperties = new List<string>(); | |
foreach (var propertyName in propertyInfos.Select(propertyInfo => propertyInfo.Name)) | |
{ | |
if (getters) methodsProperties.Add("get_" + propertyName); | |
if (setters) methodsProperties.Add("set_" + propertyName); | |
} | |
return methodsProperties.ToArray(); | |
} | |
public static string[] GetMethodNamesFromLambdaProperty<T>(bool getters = true, bool setters = true, params Expression<Func<T, object>>[] expressions) where T : class | |
{ | |
var methodsProperties = new List<string>(); | |
foreach (var propertyName in expressions.Select(expression => expression.ExtractPropertyName())) | |
{ | |
if (getters) methodsProperties.Add("get_" + propertyName); | |
if (setters) methodsProperties.Add("set_" + propertyName); | |
} | |
return methodsProperties.ToArray(); | |
} | |
internal static string ExtractPropertyName<T>(this Expression<Func<T, object>> expression) | |
{ | |
var memberExpression = expression.ToMemberExpression(); | |
var member = memberExpression.Member; | |
return member.Name; | |
} | |
private static MemberExpression ToMemberExpression(this LambdaExpression expression) | |
{ | |
MemberExpression memberExpression; | |
if (expression.Body is UnaryExpression) | |
{ | |
var unary = (UnaryExpression)expression.Body; | |
memberExpression = (MemberExpression)unary.Operand; | |
} | |
else memberExpression = expression.Body as MemberExpression; | |
if (memberExpression == null) | |
{ | |
throw new ArgumentException("expression"); | |
} | |
return memberExpression; | |
} | |
#endregion | |
#region helper methods | |
public static bool Implements<TType>(this Type derived) | |
{ | |
return Implements(typeof(TType), derived); | |
} | |
public static bool Implements(this Type derived, Type baseType) | |
{ | |
return derived.IsAssignableFrom(baseType); | |
} | |
#endregion | |
public static PropertyInfo[] GetAllProperties(this Type type) | |
{ | |
List<Type> typeList = new List<Type> { type }; | |
if (type.IsInterface) | |
{ | |
typeList.AddRange(type.GetInterfaces()); | |
} | |
return typeList.SelectMany(interfaceType => interfaceType.GetProperties()).ToArray(); | |
} | |
} | |
internal class ObjectProxy<TInterface> : RealProxy, IRemotingTypeInfo | |
{ | |
private readonly Action<AspectContext<TInterface>> _preAspect = delegate { }; | |
private readonly Action<AspectContext<TInterface>> _postAspect = delegate { }; | |
private readonly IAspectBehavior<TInterface> _behavior; | |
private readonly bool _supressErrors; | |
private readonly String[] _arrMethods; | |
private readonly dynamic _parameters; | |
private readonly TInterface _target; | |
public TInterface Proxy { get; private set; } | |
/// <summary> | |
/// Creates a new instance | |
/// </summary> | |
/// <param name="target">The object that will be proxied</param> | |
/// <param name="preAspect">Optional action that will be fired before intercepted method execution</param> | |
/// <param name="postAspect">Optional action that will be fired after the execution of the intercepted method</param> | |
/// <param name="behavior">A class that implements IAspectBehavior. If this parameter is not null, the preAspect and postAspect are ignored</param> | |
/// <param name="parameters">dynamic parameters that will be available during the execution context</param> | |
/// <param name="supressErrors">True to supress exceptions, otherwise, false to throw exceptions</param> | |
/// <param name="arrMethods">List of methods that will be intercepted. If null or empty, all methods of the TInterface will be intercepted</param> | |
protected internal ObjectProxy( | |
TInterface target, | |
Action<AspectContext<TInterface>> preAspect, | |
Action<AspectContext<TInterface>> postAspect, | |
IAspectBehavior<TInterface> behavior, | |
dynamic parameters = null, | |
bool supressErrors = true, | |
params string[] arrMethods) | |
: base(typeof(TInterface)) //: base(typeof(MarshallByRefObject)) | |
{ | |
_target = target; | |
_preAspect = preAspect; | |
_postAspect = postAspect; | |
_behavior = behavior; | |
_parameters = parameters; | |
_supressErrors = supressErrors; | |
_arrMethods = _behavior != null ? _behavior.MethodsToIntercept : arrMethods; | |
Proxy = (TInterface)GetTransparentProxy(); | |
TypeName = string.Format("{0}_{1}", Proxy.GetType().Name, _target.GetType().Name); | |
} | |
public override sealed object GetTransparentProxy() | |
{ | |
return base.GetTransparentProxy(); | |
} | |
public override IMessage Invoke(IMessage message) | |
{ | |
var methodMessage = (IMethodCallMessage)message; | |
var method = methodMessage.MethodBase; | |
if (!HasMethod(method.Name)) | |
return CreateReturnMessage(InvokeOriginalMethod(methodMessage, false), methodMessage); | |
var context = new AspectContext<TInterface>(_target, methodMessage, _parameters); | |
// Perform the preprocessing | |
var returnMessage = ExecuteAspect(_behavior != null ? _behavior.PreAspect : _preAspect, context); | |
if (returnMessage != null) | |
return returnMessage; | |
// Perform the call | |
var returnValue = InvokeOriginalMethod(methodMessage, context.Abort); | |
// Perform the postprocessing | |
if (!context.Abort) | |
returnMessage = ExecuteAspect(_behavior != null ? _behavior.PostAspect : _postAspect, context); | |
if (returnMessage != null) | |
return returnMessage; | |
// Create the return message (ReturnMessage) | |
returnMessage = CreateReturnMessage(returnValue, methodMessage); | |
return returnMessage; | |
} | |
/// <summary> | |
/// Executes the pre or post aspect, returning null if sucess | |
/// </summary> | |
private ReturnMessage ExecuteAspect(Action<AspectContext<TInterface>> aspect, AspectContext<TInterface> context) | |
{ | |
try | |
{ | |
if (aspect != null) | |
aspect.Invoke(context); | |
} | |
catch (Exception e) | |
{ | |
if (_supressErrors) | |
{ | |
return new ReturnMessage(e, context.CallCtx); | |
} | |
throw; | |
} | |
return null; | |
} | |
/// <summary> | |
/// Call the current method being intercepted | |
/// </summary> | |
private object InvokeOriginalMethod(IMethodMessage methodMessage, bool abort) | |
{ | |
try | |
{ | |
if (abort) | |
return null; | |
return methodMessage.MethodBase.Name == "GetType" | |
? typeof(TInterface) | |
: methodMessage.MethodBase.Invoke(_target, methodMessage.Args); | |
} | |
catch (Exception ex) | |
{ | |
if (ex.InnerException != null) | |
throw ex.InnerException; | |
throw; | |
} | |
} | |
/// <summary> | |
/// Creates the return IMessage to the Invoke method | |
/// </summary> | |
private static ReturnMessage CreateReturnMessage(object returnValue, IMethodCallMessage methodMessage) | |
{ | |
return new ReturnMessage(returnValue, | |
methodMessage.Args, | |
methodMessage.ArgCount, | |
methodMessage.LogicalCallContext, | |
methodMessage); | |
} | |
/// <summary> | |
/// Will check if a method is going to be intercepted. Will return true if empty or null or contains the name. | |
/// </summary> | |
private bool HasMethod(string mtd) | |
{ | |
return _arrMethods == null || _arrMethods.Count() == 0 || _arrMethods.Any(s => s.Equals(mtd)); | |
} | |
public override ObjRef CreateObjRef(Type type) | |
{ | |
throw new NotSupportedException("ObjRef for DynamicProxy isn't supported"); | |
} | |
public bool CanCastTo(Type fromType, object realproxy) // refers to current TransparentProxy object | |
{ | |
var result = fromType == typeof(TInterface) || fromType == _target.GetType() || | |
typeof(TInterface).IsAssignableFrom(fromType) || _target.GetType().Implements(fromType); | |
return result; | |
} | |
public string TypeName { get; set; } | |
} | |
public class FluentBuilder<TInterface> where TInterface : class | |
{ | |
private readonly TInterface _target; | |
private Action<AspectContext<TInterface>> _preAspect; | |
private Action<AspectContext<TInterface>> _postAspect; | |
private dynamic _parameters; | |
private string[] _methodsFilter; | |
private IAspectBehavior<TInterface> _behavior; | |
private Expression<Action<TInterface>>[] _lambdas; | |
private readonly bool _supressErrors; | |
internal FluentBuilder(TInterface target, bool supressErrors = true) | |
{ | |
_target = target; | |
_supressErrors = supressErrors; | |
} | |
public FluentBuilder<TInterface> SetPreDecoration(Action<AspectContext<TInterface>> preAspect) | |
{ | |
_behavior = null; | |
_preAspect = preAspect; | |
return this; | |
} | |
public FluentBuilder<TInterface> SetPostDecoration(Action<AspectContext<TInterface>> postAspect) | |
{ | |
_behavior = null; | |
_postAspect = postAspect; | |
return this; | |
} | |
public FluentBuilder<TInterface> SetParameters(dynamic parameters) | |
{ | |
_parameters = parameters; | |
return this; | |
} | |
public FluentBuilder<TInterface> FilterMethods(params string[] methods) | |
{ | |
_lambdas = null; | |
_methodsFilter = methods; | |
return this; | |
} | |
public FluentBuilder<TInterface> FilterMethods(params Expression<Action<TInterface>>[] methods) | |
{ | |
_methodsFilter = null; | |
_lambdas = methods; | |
return this; | |
} | |
public FluentBuilder<TInterface> ApplyBehavior(IAspectBehavior<TInterface> behavior) | |
{ | |
_preAspect = null; | |
_postAspect = null; | |
_behavior = behavior; | |
return this; | |
} | |
public TInterface Build() | |
{ | |
if (_lambdas != null) | |
{ | |
_methodsFilter = Helpers.GetMethodNames(_lambdas); | |
} | |
var objectProxy = new ObjectProxy<TInterface>( | |
_target, | |
_preAspect, | |
_postAspect, | |
_behavior, | |
_parameters, | |
_supressErrors, | |
_methodsFilter); | |
return objectProxy.Proxy; //GetTransparentProxy() | |
} | |
} | |
public static class ObjectProxyFactory | |
{ | |
public static FluentBuilder<TInterface> Configure<TInterface>(object target, bool supressErrors = true) | |
where TInterface : class | |
{ | |
if (!typeof(TInterface).IsInterface) | |
throw new ArgumentException("TInterface"); | |
/* automatic duck typing if needed */ | |
TInterface typedTarget = target.GetType().Implements(typeof(TInterface)) | |
? (TInterface)target | |
: target.ActLike<TInterface>(); | |
return new FluentBuilder<TInterface>(typedTarget, supressErrors); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment