structuremapstructuremap3

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);
    }

}

Solution

  • 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.