Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Decoration using Castle DynamicProxy and StructureMap 3 in a Convention - DecorateAllWith

How to use DecorateAllWith to decorate with a DynamicProxy all instances implements an interface?

For example:

public class ApplicationServiceInterceptor : IInterceptor
{
    public void Intercept(IInvocation invocation)
    {
        // ...
        invocation.Proceed();
        // ...
    }
}

public class ApplicationServiceConvention : IRegistrationConvention
{
    public void Process(Type type, Registry registry)
    {
        if (type.CanBeCastTo<IApplicationService>() && type.IsInterface)
        {
            var proxyGenerator = new ProxyGenerator();

            // ??? how to use proxyGenerator??
            // ???

            registry.For(type).DecorateAllWith(???); // How to use DecorateAllWith DynamicProxy ...??
        }
    }
}

I could decorate some interfaces to concrete types using (for example):

var proxyGenerator = new ProxyGenerator();
registry.For<IApplicationService>().Use<BaseAppService>().DecorateWith(service => proxyGenerator.CreateInterfaceProxyWithTargetInterface(....))

But havent able to using DecorateAll to do this.

To call registry.For<>().Use<>().DecorateWith() I have to do this:

if (type.CanBeCastTo<IApplicationService>() && !type.IsAbstract)
{
    var interfaceToProxy = type.GetInterface("I" + type.Name);
    if (interfaceToProxy == null)
        return null;
    var proxyGenerator = new ProxyGenerator();

    // Build expression to use registration by reflection
    var expression = BuildExpressionTreeToCreateProxy(proxyGenerator, type, interfaceType, new MyInterceptor());

    // Register using reflection
    var f = CallGenericMethod(registry, "For", interfaceToProxy);
    var u = CallGenericMethod(f, "Use", type);
    CallMethod(u, "DecorateWith", expression);
}

Only for crazy minds ...

I start to get very tired of StructureMap, many changes and no documentation, I have been read the source code but ... too many efforts for my objective ...

If someone can give me a bit of light I will be grateful.

Thanks in advance.

In addition ... I post here the real code of my helper to generate the expression tree an register the plugin family:

public static class RegistrationHelper
{
    public static void RegisterWithInterceptors(this Registry registry, Type interfaceToProxy, Type concreteType,
        IInterceptor[] interceptors, ILifecycle lifecycle = null)
    {
        var proxyGenerator = new ProxyGenerator();

        // Generate expression tree to call DecoreWith of StructureMap SmartInstance type
        // registry.For<interfaceToProxy>().Use<concreteType>()
        //      .DecoreWith(ex => (IApplicationService) 
        //                        proxyGenerator.CreateInterfaceProxyWithTargetInterface(interfaceToProxy, ex, interceptors)
        var expressionParameter = Expression.Parameter(interfaceToProxy, "ex");
        var proxyGeneratorConstant = Expression.Constant(proxyGenerator);
        var interfaceConstant = Expression.Constant(interfaceToProxy);
        var interceptorConstant = Expression.Constant(interceptors);

        var methodCallExpression = Expression.Call(proxyGeneratorConstant,
            typeof (ProxyGenerator).GetMethods().First(
                met => met.Name == "CreateInterfaceProxyWithTargetInterface"
                       && !met.IsGenericMethod && met.GetParameters().Count() == 3),
            interfaceConstant, 
            expressionParameter, 
            interceptorConstant);

        var convert = Expression.Convert(methodCallExpression, interfaceToProxy);

        var func = typeof(Func<,>).MakeGenericType(interfaceToProxy, interfaceToProxy);
        var expr = Expression.Lambda(func, convert, expressionParameter);

        // Register using reflection
        registry.CallGenericMethod("For", interfaceToProxy, new[] {(object) lifecycle /*Lifecicle*/})
            .CallGenericMethod("Use", concreteType)
            .CallNoGenericMethod("DecorateWith", expr);
    }
}
public static class CallMethodExtensions
{
    /// <summary>
    /// Call a method with Generic parameter by reflection (obj.methodName[genericType](parameters)
    /// </summary>
    /// <returns></returns>
    public static object CallGenericMethod(this object obj, string methodName, Type genericType, params object[] parameters)
    {
        var metod = obj.GetType().GetMethods().First(m => m.Name == methodName && m.IsGenericMethod);
        var genericMethod = metod.MakeGenericMethod(genericType);
        return genericMethod.Invoke(obj, parameters);
    }

    /// <summary>
    /// Call a method without Generic parameter by reflection (obj.methodName(parameters)
    /// </summary>
    /// <returns></returns>
    public static object CallNoGenericMethod(this object obj, string methodName, params object[] parameters)
    {
        var method = obj.GetType().GetMethods().First(m => m.Name == methodName && !m.IsGenericMethod);
        return method.Invoke(obj, parameters);
    }

}
like image 293
Javier Ros Avatar asked Oct 21 '22 00:10

Javier Ros


1 Answers

Almost two years later I have needed return this issue for a new project. This time I have solved it this time I have used StructureMap 4.

You can use a custom interceptor policy to decorate an instance in function of his type. You have to implement one interceptor, one interceptor policy and configure it on a registry.

The Interceptor

public class MyExInterceptor : Castle.DynamicProxy.IInterceptor
{
    public void Intercept(Castle.DynamicProxy.IInvocation invocation)
    {
        Console.WriteLine("-- Call to " + invocation.Method);
        invocation.Proceed();
    }
}

The interceptor policy

public class CustomInterception : IInterceptorPolicy
{
    public string Description
    {
        get { return "good interception policy"; }
    }

    public IEnumerable<IInterceptor> DetermineInterceptors(Type pluginType, Instance instance)
    {
        if (pluginType == typeof(IAppService))
        {
            // DecoratorInterceptor is the simple case of wrapping one type with another
            // concrete type that takes the first as a dependency
            yield return new FuncInterceptor<IAppService>(i =>
                        (IAppService)
                            DynamicProxyHelper.CreateInterfaceProxyWithTargetInterface(typeof(IAppService), i));
        }
    }
}

Configuration

var container = new Container(_ =>
{
    _.Policies.Interceptors(new CustomInterception());

    _.For<IAppService>().Use<AppServiceImplementation>();
});

var service = container.GetInstance<IAppService>();
service.DoWork();

You can get a working example on this gist https://gist.github.com/tolemac/3e31b44b7fc7d0b49c6547018f332d68, in the gist you can find three types of decoration, the third is like this answer.

Using it you can configure the decorators of your services easily.

like image 92
Javier Ros Avatar answered Oct 24 '22 00:10

Javier Ros