seesharper / LightInject

An ultra lightweight IoC container
http://www.lightinject.net
MIT License
621 stars 120 forks source link

Interception - Are proxies created prior to applying the method selector? #85

Closed marceln closed 10 years ago

marceln commented 10 years ago

I'm trying to do this:

serviceRegistry.Intercept(sr => true, (sf, pd) => DefineProxyType(pd));
...
        private static void DefineProxyType(ProxyDefinition proxyDefinition)
        {
            proxyDefinition.Implement(
                () => new LoggingInterceptor(),
                x => x.GetCustomAttributes(false).Any(a => a.GetType() == typeof(LogInfoAttribute) || a.GetType() == typeof(LogTraceAttribute)));
        }

I think it tries to create proxies for all service types (because of sr => true) and this causes problems with services that are internal to this assembly (I have a few of these):

Additional information: Access is denied: 'Internal class full name'.

Is it possible to filter first and then create the proxy?

Thanks, Marcel

seesharper commented 10 years ago

Hi Marcel.

I guessing that you are doing interface based interception here and the important thing to remember is that the MethodInfo passed to the method selector function is the method from the interface(service type) and not the implementing type.

Given that you live with having the attributes on the interface rather than or the implementing type, you can do something like this.

using LightInject;
using LightInject.Interception;

class Program
{
    static void Main(string[] args)
    {
        var container = new ServiceContainer();
        container.Register<IFoo, Foo>();
        container.Intercept(
            sr => sr.ServiceType.GetMethods().Any(m => m.IsDefined(typeof(LogInfoAttribute), true)),
            (sf, pd) => DefineProxyType(pd));

        var instance = container.GetInstance<IFoo>();
        instance.A();

    }

    private static void DefineProxyType(ProxyDefinition proxyDefinition)
    {
        proxyDefinition.Implement(
            () => new LoggingInterceptor(),
            x => x.GetCustomAttributes(true).Any(a => a.GetType() == typeof(LogInfoAttribute) || a.GetType() == typeof(LogTraceAttribute)));
    }
}

public interface IFoo
{
    [LogInfo]
    void A();
    void B();
}

public class Foo : IFoo
{

    public void A()
    {            
    }

    public void B()
    {            
    }
}

public class LogInfoAttribute : Attribute
{

}

public class LogTraceAttribute : Attribute
{

}

public class LoggingInterceptor : IInterceptor
{
    public object Invoke(IInvocationInfo invocationInfo)
    {
        Console.WriteLine(invocationInfo.Method.Name);
        return invocationInfo.Proceed();
    }
}

Scheduled for LightInject v.3.0.1.8 is as post prossesor mechanism that lets you modify the actual instance before it is returned. This could be used to create a proxy based on information from the implementing type which I suspect is what you actually are trying to do here :)

Until then, I hope this works as a reasonable workaround.

Best regards Bernhard Richter

marceln commented 10 years ago

Yes, indeed. Some of the services were indeed registered with interfaces (while some were not). But this works just fine and I can move my attribute declarations to the interfaces, where applicable.

Thank you again! Marcel