Git Product home page Git Product logo

lightinject.interception's Introduction

AppVeyor NuGet GitHub tag

Interception

LightInject supports Aspect Oriented Programming through proxy-based method interceptors.

Installing

LightInject.Interception provides two distribution models via NuGet

Binary

PM> Install-Package LightInject.Interception

This adds a reference to the LightInject.Interception.dll in the target project.

Source

PM> Install-Package LightInject.Interception.Source

This will install a single file (LightInject.Interception.cs) into the current project.

Interceptors

An interceptor sits between the call site and the target instance and intercepts method calls.

public class SampleInterceptor : IInterceptor
{
    public object Invoke(IInvocationInfo invocationInfo)
    {
        // Perform logic before invoking the target method
        var returnValue = invocationInfo.Proceed();
        // Perform logic after invoking the target method
        return returnValue;           
    }        
}

The IInvocationInfo instance passed into the Invoke method contains information about the method being intercepted.

The Proceed method calls down the chain of interceptors and ultimately the actual target instance.

Asynchronous Interceptors

When intercepting asynchronous methods we need to be able to await the target method. This can be done by inheriting from the abstract AsyncInterceptor class that does the heavy lifting with invoking the asynchronous wrapper methods. The AsyncInterceptor class is a decorator that wraps around another IInterceptor.

public class SampleAsyncInterceptor : AsyncInterceptor
{    
    public SampleAsyncInterceptor(IInterceptor targetInterceptor) : base(targetInterceptor)
    {
    }

    protected override async Task InvokeAsync(IInvocationInfo invocationInfo)
    {
        InterceptedTaskMethod = true;
        // Before method invocation
        await base.InvokeAsync(invocationInfo);
        // After method invocation
    }

    protected override async Task<T> InvokeAsync<T>(IInvocationInfo invocationInfo)
    {
        InterceptedTaskOfTMethod = true;
        // Before method invocation
        var value = await base.InvokeAsync<T>(invocationInfo);
        // After method invocation           
        return value;
    }
}

Note: Do not call invocationInfo.Proceed() directly when inheriting from the AsyncInterceptor class.

We can now create a new instance of the SampleAsyncInterceptor class like this:

var asyncInterceptor = new SampleAsyncInterceptor(new SampleInterceptor());

Another option is to register our IInterceptor with the container and use the Decorate method to apply the SampleAsyncInterceptor as a decorator.

container.Register<IInterceptor, SampleInterceptor>();
container.Decorate<IInterceptor, SampleAsyncInterceptor>();
container.Intercept(sr => sr.ServiceType == typeof(IFoo), factory => factory.GetInstance<IInterceptor>()); 

Note: Only synchronous methods are passed down to the decorated IInterceptor

Single Interceptor

This example shows how to configure the service container with a single interceptor to handle all method calls.

container.Register<IFoo, Foo>();
container.Intercept(sr => sr.ServiceType == typeof(IFoo), sf => new SampleInterceptor());

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

The instance returned is a proxy object that forwards method calls to the SampleInterceptor class.

The first parameter of the Intercept method is a selector function used to select the services that should have this interceptor applied.
The second parameter is a function delegate that used to create an IInterceptor instance.

Note: Proxy types are lazy in the sense that they will not create the target instance or any interceptors until the first method call is made.

Dependencies

Interceptors might also have dependencies and by resolving the interceptor through the container, those dependencies can be injected into the interceptor itself.

public class SampleInterceptor : IInterceptor
{
    private IBar bar;

    public SampleInterceptor(IBar bar) 
    {
        this.bar = bar;    
    }

    public object Invoke(IInvocationInfo invocationInfo)

        // Perform logic using the injected dependency before invoking the target method             
        return invovationInfo.Proceed();                      
        // Perform logic using the injected dependency after invoking the target method
    }        
}

The following example shows how to configure the container so that the SampleInterceptor instance is resolved through the container.

container.Register<IFoo, Foo>()
container.Register<IBar, Bar>();
container.Register<IInterceptor, SampleInterceptor>();
container.Intercept(sr => sr.ServiceType == typeof(IFoo), sf => sf.GetInstance<IInterceptor>()); 

Note: When injecting depndencies into an interceptor we must make sure that the injected dependency is NOT intercepted by the same interceptor as this would cause a StackOverFlowException.

Multiple Interceptors

Interceptors can be set up to handle a lot of cross cutting concerns such as logging, caching, null check and so on. According to the Single Responsibility Principle, we can separate the combined logic into a set of interceptor that each only does "one" thing.

We can do this by using another overload of the Intercept method that allows us to set up a ProxyDefinition instance that gives us more control over the proxy type that is being created.

container.Intercept(sr => sr.ServiceType == typeof(IFoo), (sf,pd) =>  DefineProxyType(pd));

private void DefineProxyType(ProxyDefinition proxyDefinition)
{
    proxyDefinition.Implement(new FirstInterceptor());
    proxyDefinition.Implement(new SecondInterceptor());
}

Note: The interceptors are invoked in the same order as they are registered with the Implement method.

Method Selectors

Method selectors are used to select the methods that should be intercepted by an interceptor.

The following example shows how to set up the container so that only calls method A is passed to the interceptor.

container.Intercept(sr => sr.ServiceType == typeof(IFoo), (sf, pd) =>  DefineProxyType(pd));

private void DefineProxyType(ProxyDefinition proxyDefinition)
{
    proxyDefinition.Implement(() => new SampleInterceptor(), m => m.Name == "SomeMethodName");       
}

Methods that does not match the method selector predicate will NOT be intercepted and method calls will be passed directly down to the target instance.

If we omit the method selector, LightInject will intercept all methods from the target type and any additional interface, except methods that are inherited from System.Object.

  • Equals(Object)
  • GetHashCode
  • GetType
  • ToString

If we choose to use a method selector, these methods will also be intercepted if they match the predicate in the method selector.

proxyDefinition.Implement(() => new SampleInterceptor(), m => m.IsDeclaredBy<object>());

We can also use a method selector with the Intercept method that allows easy interception of any method without implementing an IInterceptor.

container.Intercept(m => m.Name == "SomeMethodName", invocationInfo => invocationInfo.Proceed());

Extension Methods

LightInject provides a set of extension method that simplifies method selector predicates.

  • IsPropertySetter - Returns true if the method represents a property setter, otherwise false.
  • IsPropertyGetter - Returns true if the method represents a property getter, otherwise false.
  • GetProperty - Returns the property for which the target method either represents the property getter or the property setter.

Chaining Interceptors

As already seen in the example with multiple interceptors, we can chain interceptors together. We can also combine this with method selectors that will affect the call sequence from the call site down to the actual target instance.

Consider an interface with three methods.

public interface IFoo 
{
    void A();
    void B();
    void C();
}

The following example shows how we can control the call sequence for each method.

container.Intercept(sr => sr.ServiceType == typeof(IFoo), (sf, pd) =>  DefineProxyType(pd));

private void DefineProxyType(ProxyDefinition proxyDefinition)
{
    proxyDefinition.Implement(() => new FirstInterceptor(), m => m.Name == "A");
    proxyDefinition.Implement(() => new SecondInterceptor(), m => m.Name == "B");   
    proxyDefinition.Implement(() => new ThirdInterceptor(), m => m.Name == "A" || m.Name == "B" || m.Name == "C");
}

Method A call sequence

FirstInterceptor -> ThirdInterceptor -> Target

Method B call sequence

SecondInterceptor -> ThirdInterceptor -> Target

Method C call sequence

ThirdInterceptor -> Target

Implementing additional interfaces

Another powerful feature of proxy objects is the ability to implement additional interfaces that is not implemented by the target type.

The Intercept method has an overload that lets us specify a set of interfaces to be implemented by the proxy type.

container.Intercept(sr => sr.ServiceType == typeof(IFoo), new []{ typeof(IBar) }, (sf, pd) =>  DefineProxyType(pd));

private void DefineProxyType(ProxyDefinition proxyDefinition)
{
    proxyDefinition.Implement(() => new BarInterceptor(), m => m.IsDeclaredBy<IBar>());        
} 

When implementing additional interfaces we must make sure that all methods are intercepted by either one or a combined set of interceptors. This is because we are now dealing with methods that does not exist in the target type and we must do all implementation through interceptors.

IProxy

/// <summary>
/// Implemented by all proxy types.
/// </summary>
public interface IProxy
{
    /// <summary>
    /// Gets the proxy target.
    /// </summary>
    object Target { get; }
}

We can get to the underlying target instance through the IProxy interface

container.Register<IFoo, Foo>();
container.Intercept(sr => sr.ServiceType == typeof(IFoo), sf => new SampleInterceptor());

var instance = container.GetInstance<IFoo>();
var actualTarget = ((IProxy)instance).Target;

This

One of the things to be aware of when working with proxy based interception is that it all relies on method calls being made through the proxy. Method calls that are made directly to the target instance will NOT be intercepted.

public interface IFoo
{
    void A();
}

public class Foo : IFoo
{
    public void A() {}

    private void B()
    {
        //Calls the target (this) directly and interceptors are not invoked.
        this.A();
    }
}

Another scenario is when the proxy instance itself is leaking its target.

public interface IFoo
{
    IFoo A();
}

public class Foo
{
    public IFoo A()
    {
        return this;
    }
}

LightInject will take care of this scenario and detect that we are about to return this from a method and replace the return value with the proxy instance instead.

Other scenarios such as event handlers or passing "this" to another method is NOT taken care of by LightInject as it is not possible without modifying the code in the target type itself.

Class Proxies

Starting from version 1.0.0.4, LightInject.Interception can be used to intercept classes with virtual members.

public class Foo
{
	public virtual void A()
	{
	}
}

Any member that is marked as virtual can be intercepted.

var container = new ServiceContainer();
container.Register<Foo>();
container.Intercept(sr => sr.ServiceType == typeof(Foo), factory => new SampleInterceptor());

Class proxies are implemented internally by subclassing the target type and overriding virtual members to support interception.

lightinject.interception's People

Contributors

geirsagberg avatar kemsky avatar leandro86 avatar seesharper avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

lightinject.interception's Issues

Interception of base class protected virtual methods broken since version 2.0.0

The following test fails.

    public class LightInjectInterceptionTests
    {
        [Fact]
        public void ShouldAbleInterceptBaseClassProtectedVirtualMethods()
        {
            var container = new ServiceContainer();

            container.Register<Derived>();

            var interceptor = new ProtectedVirtualMethodInterceptor();
            container.Intercept(
                sr => typeof(Derived).IsAssignableFrom(sr.ServiceType),
                (r, d) => d.Implement(() => interceptor, m => m.Name == "ProtectedVirtualMethod"));

            var instance = container.GetInstance<Derived>();
            instance.Call();

            Assert.True(interceptor.IsInvoked);
        }

        public class Base
        {
            public void Call()
            {
                ProtectedVirtualMethod();
            }

            protected virtual void ProtectedVirtualMethod()
            {
            }
        }

        public class Derived : Base
        {
        }

        internal class ProtectedVirtualMethodInterceptor : IInterceptor
        {
            public bool IsInvoked { get; set; }

            public object Invoke(IInvocationInfo invocationInfo)
            {
                IsInvoked = true;
                return invocationInfo.Proceed();
            }
        }
    }

It's interesting that the same test with Base class instead of Derived succeeds.

Little investigation showed that test works with version 1.2.1, but does not wrok with version 2.0.0 of LightInject.Interception.

The reason in method MethodSelector.Execute code. It was

        public MethodInfo[] Execute(Type targetType, Type[] additionalInterfaces)
        {
            MethodInfo[] interceptableMethods;

            if (targetType.GetTypeInfo().IsInterface)
            {
                interceptableMethods = targetType.GetMethods()
                                          .Where(m => !m.IsSpecialName)
                                          .Concat(typeof(object).GetMethods().Where(m => m.IsVirtual))
                                          .Concat(additionalInterfaces.SelectMany(i => i.GetMethods()))
                                          .Distinct()
                                          .ToArray();
            }
            else
            {
                interceptableMethods = targetType.GetMethods(BindingFlags.Public | BindingFlags.Instance)
                                          .Concat(targetType.GetMethods(BindingFlags.NonPublic | BindingFlags.Instance).Where(m => m.IsFamily && !m.IsDeclaredBy<object>()))
                                          .Where(m => m.IsVirtual && !m.IsFinal)
                                          .Concat(additionalInterfaces.SelectMany(i => i.GetMethods()))
                                          .Distinct()
                                          .ToArray();
            }

            return interceptableMethods;
        }

But in revision 544c1c7 it was changed to.

        public MethodInfo[] Execute(Type targetType, Type[] additionalInterfaces)
        {
            MethodInfo[] interceptableMethods;

            if (targetType.GetTypeInfo().IsInterface)
            {
                interceptableMethods = targetType.GetTypeInfo().DeclaredMethods
                                          .Where(m => !m.IsSpecialName)
                                          .Concat(typeof(object).GetTypeInfo().DeclaredMethods.Where(m => m.IsVirtual && !m.IsFamily))
                                          .Concat(additionalInterfaces.SelectMany(i => i.GetTypeInfo().DeclaredMethods))
                                          .Distinct()
                                          .ToArray();                
            }
            else
            {                
                interceptableMethods = targetType.GetRuntimeMethods().Where(m => m.IsPublic)
                    .Concat(
                        targetType.GetTypeInfo()
                            .DeclaredMethods.Where(
                                m => !m.IsPublic && m.IsFamily && !m.IsDeclaredBy<object>()))
                    .Where(m => m.IsVirtual && !m.IsFinal && !m.IsStatic)
                    .Concat(additionalInterfaces.SelectMany(i => i.GetTypeInfo().DeclaredMethods))
                    .Distinct().ToArray();                
            }

            return interceptableMethods;
        }

targetType.GetTypeInfo().DeclaredMethods does not include base class methods. This brokes LightInject.SignalR support - it based on interception of virtual protected method Hub.Dispose(bool), which belongs to HubBase class.

Async interception bug

Hi,

I tried to use new asynchronous interception feature, but it seems like it has a bug: method async Task<T> InvokeAsync<T> isn't fired. I checked object Invoke and async Task InvokeAsync methods and they work fine.

Could not load type with LightInject.Interception + Microsoft DI

I'm getting a type load yellow screen when trying to load an MVC controller that has a dependency. I'll post the yellow screen at the bottom of this. I'm using Microsoft Dependency Injection. The container registration is in one project (let's call it ProjectDI), and the class the controller that's throwing an error is in another project (let's call it ProjectMVC).

ProjectMVC references ProjectDI. The dependency (CreatorLog) is being registered in ProjectMVC by adding it to the IServiceCollection. The intercept code in ProjectDI gets the list of ServiceDescriptors and registers them like this:

foreach (ServiceDescriptor descriptor in serviceCollection)
{
    string assemblyName = descriptor.ServiceType.AssemblyQualifiedName ?? "";
    if (WhitelistedAssemblies.Any(assemblyName.StartsWith))
    {
        container.Intercept(s => s.ServiceType == descriptor.ServiceType, i => new MiniProfilerInterceptor());
    }
}

The stack trace I'm getting is:

[TypeLoadException: Could not load type 'CreatorLogProxyProxy' from assembly 'LightInject.Interception.ProxyAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'.]
   System.Reflection.Emit.TypeBuilder.TermCreateClass(RuntimeModule module, Int32 tk, ObjectHandleOnStack type) +0
   System.Reflection.Emit.TypeBuilder.CreateTypeNoLock() +1765
   System.Reflection.Emit.TypeBuilder.CreateTypeInfo() +95
   LightInject.Interception.TypeBuilderFactory.CreateType(TypeBuilder typeBuilder) in C:\projects\lightinject-interception\build\tmp\Net46\Binary\LightInject.Interception\LightInject.Interception.cs:1093
   LightInject.Interception.ProxyBuilder.GetProxyType(ProxyDefinition definition) in C:\projects\lightinject-interception\build\tmp\Net46\Binary\LightInject.Interception\LightInject.Interception.cs:1203
   LightInject.InterceptionContainerExtensions.CreateProxyServiceRegistration(ServiceRegistration registration, Type[] additionalInterfaces, IServiceFactory serviceFactory, Action`2 defineProxyType) in C:\projects\lightinject-interception\build\tmp\Net46\Binary\LightInject.Interception\LightInject.Interception.cs:156
   LightInject.ServiceContainer.EmitNewInstanceWithDecorators(ServiceRegistration serviceRegistration, IEmitter emitter) in C:\projects\lightinject\build\tmp\Net46\Binary\LightInject\LightInject.cs:3180
   LightInject.ServiceContainer.CreateDynamicMethodDelegate(Action`1 serviceEmitter) in C:\projects\lightinject\build\tmp\Net46\Binary\LightInject\LightInject.cs:3044
   LightInject.ServiceContainer.CreateInstanceDelegateIndex(Action`1 emitMethod) in C:\projects\lightinject\build\tmp\Net46\Binary\LightInject\LightInject.cs:3862
   LightInject.ServiceContainer.EmitLifetime(ServiceRegistration serviceRegistration, Action`1 emitMethod, IEmitter emitter) in C:\projects\lightinject\build\tmp\Net46\Binary\LightInject\LightInject.cs:3851
   LightInject.<>c__DisplayClass152_0.<CreateEmitMethodWrapper>b__0(IEmitter ms) in C:\projects\lightinject\build\tmp\Net46\Binary\LightInject\LightInject.cs:3122
   LightInject.ServiceContainer.EmitConstructorDependency(IEmitter emitter, Dependency dependency) in C:\projects\lightinject\build\tmp\Net46\Binary\LightInject\LightInject.cs:3421
   LightInject.ServiceContainer.EmitConstructorDependencies(ConstructionInfo constructionInfo, IEmitter emitter, Action`1 decoratorTargetEmitter) in C:\projects\lightinject\build\tmp\Net46\Binary\LightInject\LightInject.cs:3383
   LightInject.ServiceContainer.EmitNewInstanceUsingImplementingType(IEmitter emitter, ConstructionInfo constructionInfo, Action`1 decoratorTargetEmitMethod) in C:\projects\lightinject\build\tmp\Net46\Binary\LightInject\LightInject.cs:3344
   LightInject.ServiceContainer.EmitNewInstance(ServiceRegistration serviceRegistration, IEmitter emitter) in C:\projects\lightinject\build\tmp\Net46\Binary\LightInject\LightInject.cs:3301
   LightInject.ServiceContainer.CreateDynamicMethodDelegate(Action`1 serviceEmitter) in C:\projects\lightinject\build\tmp\Net46\Binary\LightInject\LightInject.cs:3044
   LightInject.ServiceContainer.CreateInstanceDelegateIndex(Action`1 emitMethod) in C:\projects\lightinject\build\tmp\Net46\Binary\LightInject\LightInject.cs:3862
   LightInject.ServiceContainer.EmitLifetime(ServiceRegistration serviceRegistration, Action`1 emitMethod, IEmitter emitter) in C:\projects\lightinject\build\tmp\Net46\Binary\LightInject\LightInject.cs:3851
   LightInject.<>c__DisplayClass152_0.<CreateEmitMethodWrapper>b__0(IEmitter ms) in C:\projects\lightinject\build\tmp\Net46\Binary\LightInject\LightInject.cs:3122
   LightInject.ServiceContainer.EmitConstructorDependency(IEmitter emitter, Dependency dependency) in C:\projects\lightinject\build\tmp\Net46\Binary\LightInject\LightInject.cs:3421
   LightInject.ServiceContainer.EmitConstructorDependencies(ConstructionInfo constructionInfo, IEmitter emitter, Action`1 decoratorTargetEmitter) in C:\projects\lightinject\build\tmp\Net46\Binary\LightInject\LightInject.cs:3383
   LightInject.ServiceContainer.EmitNewInstanceUsingImplementingType(IEmitter emitter, ConstructionInfo constructionInfo, Action`1 decoratorTargetEmitMethod) in C:\projects\lightinject\build\tmp\Net46\Binary\LightInject\LightInject.cs:3344
   LightInject.ServiceContainer.EmitNewInstance(ServiceRegistration serviceRegistration, IEmitter emitter) in C:\projects\lightinject\build\tmp\Net46\Binary\LightInject\LightInject.cs:3301
   LightInject.ServiceContainer.CreateDynamicMethodDelegate(Action`1 serviceEmitter) in C:\projects\lightinject\build\tmp\Net46\Binary\LightInject\LightInject.cs:3044
   LightInject.ServiceContainer.CreateInstanceDelegateIndex(Action`1 emitMethod) in C:\projects\lightinject\build\tmp\Net46\Binary\LightInject\LightInject.cs:3862
   LightInject.ServiceContainer.EmitLifetime(ServiceRegistration serviceRegistration, Action`1 emitMethod, IEmitter emitter) in C:\projects\lightinject\build\tmp\Net46\Binary\LightInject\LightInject.cs:3851
   LightInject.<>c__DisplayClass152_0.<CreateEmitMethodWrapper>b__0(IEmitter ms) in C:\projects\lightinject\build\tmp\Net46\Binary\LightInject\LightInject.cs:3122
   LightInject.ServiceContainer.CreateDynamicMethodDelegate(Action`1 serviceEmitter) in C:\projects\lightinject\build\tmp\Net46\Binary\LightInject\LightInject.cs:3044
   LightInject.ServiceContainer.CreateDelegate(Type serviceType, String serviceName, Boolean throwError) in C:\projects\lightinject\build\tmp\Net46\Binary\LightInject\LightInject.cs:3909
   LightInject.ServiceContainer.CreateDefaultDelegate(Type serviceType, Boolean throwError) in C:\projects\lightinject\build\tmp\Net46\Binary\LightInject\LightInject.cs:3872
   LightInject.ServiceContainer.TryGetInstance(Type serviceType) in C:\projects\lightinject\build\tmp\Net46\Binary\LightInject\LightInject.cs:2742
   Sitecore.Mvc.Controllers.SitecoreDependencyResolver.GetService(Type serviceType) +38
   System.Web.Mvc.DefaultControllerActivator.Create(RequestContext requestContext, Type controllerType) +59

The MVC controller is registered, as is the dependency.

Question: Intercepting async and non-async methods

Hi,

For example we have a class with async and non-async methods and I want to use interceptor for both method types. AsyncInterceptor class already have the object Invoke(IInvocationInfo invocationInfo) method, but it is called even for async methods and if I create interception behavior (AOP advice) for async and non-async interceptor methods (e.g. object Invoke(IInvocationInfo invocationInfo) and async Task InvokeAsync(IInvocationInfo invocationInfo)), it will be called twice. It would be cool if object Invoke(IInvocationInfo invocationInfo) is called only for non async methods.

Interceptor not called unless called by resolving from the container

Guys love LightInject its sooo much faster than other DI containers!
But I am unable to get the interceptor to work unless I resolve the object from the container,
I would have thought that once the interception is setup as long as you use the same object it should work?
Unable to determine the implementing type.
Here is a little test app.
TestAOP.zip

This package is not compatible with Annotations

LightInject.Interception.ProxyBuilder.ImplementAllConstructorsFromBaseClass method does not copy original attributes, as a result AnnotatedConstructorDependencySelector can not find InjectAttribute's in proxy constructor:

                foreach (var parameterInfo in constructorInfo.GetParameters())
                {
                    constructorBuilder.DefineParameter(
                        parameterInfo.Position + 1,
                        parameterInfo.Attributes,
                        parameterInfo.Name); // no custom attributes anymore, maybe use SetCustomAttribute and copy
                }

Maybe something like this will work:

                foreach (var parameterInfo in constructorInfo.GetParameters())
                {
                    var parameterBuilder = constructorBuilder.DefineParameter(
                        parameterInfo.Position + 1,
                        parameterInfo.Attributes,
                        parameterInfo.Name);

                    foreach (var customAttribute in parameterInfo.CustomAttributes)
                    {
                        parameterBuilder.SetCustomAttribute(CreateCustomAttributeBuilder(customAttribute));
                    }
                }

Xamarin.iOS AOT

Do you support Xamarin.iOS AOT mode? as far as I know, Castle.Core is not acceptable.

ArgumentException: TypePassedMustBeInterface

In 2.0.3 you use GetRuntimeInterfaceMap to properly map methods in inherited classes, but when target method is System.Object methods

  • GetHashCode
  • Equals
  • ToString

ArgumentException: TypePassedMustBeInterface is thrown.
InterfaceMapping interfaceMapping = proxyDefinition.ImplementingType.GetTypeInfo().GetRuntimeInterfaceMap(targetMethod.DeclaringType);

targetMethod.DeclaringType is SystemObject

Here is the test demonstrates this


        [Fact]
        public void Intercept_Service_Throws_TypePassedMustBeInterface()
        {
            var container = new ServiceContainer();
            container.Register<IFoo, Foo>();
            container.Intercept(sr => sr.ServiceType == typeof(IFoo), (sf, pd) => pd.Implement(() => new SampleInterceptor(), m => !m.IsPropertyGetter()));

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

            Assert.IsAssignableFrom<IProxy>(instance);
        }

to bypass this behaviour we should write MethodSelector like this


container.Intercept(sr => sr.ServiceType == typeof(IFoo), (sf, pd) => pd.Implement(() => new SampleInterceptor(), m => !m.IsPropertyGetter() && m.DeclaringType != typeof(object)));

Async interceptor calls target method twice bug

Hi,

I've noticed that asynchronous interceptor (AsyncInterceptor) calls target method twice even if it doesn't have any implementation:

internal class LoggingInterceptor : AsyncInterceptor
{
}

Regular interceptor (IInterceptor) does't have this problem.

Intercept methods that have a custom attribute

Hello,

As the title says, is this possible? Here's my code:

public class RequiresTransactionAttribute : Attribute
{
}

public interface IFoo
{
    void DoSomething();
}

public class Foo : IFoo
{
    [RequiresTransaction]
    public void DoSomething()
    {
        // ...
    }
}

container.Register<IFoo, Foo>();
container.Intercept(sr => sr.ServiceType == typeof(IFoo), (sf, pd) => DefineProxyType(sf, pd));

private void DefineProxyType(IServiceFactory sf, ProxyDefinition pd)
{
    pd.Implement(() => sf.GetInstance<IInterceptor>(), mi => mi.GetCustomAttribute<RequiresTransactionAttribute>() != null);
}

This is not working, as I'm getting, in the proxy definition, a reference to the type IFoo (which doesn't have the attribute), instead of the target Foo. If I do this:

public interface IFoo
{
    [RequiresTransaction]   
    void DoSomething();
}

Then the method is intercepted. But I would like to avoid setting the attribute in the interface.

CreateCustomAttributeBuilder fail

Hi,
updating Lightinject, in LightInejct.Iterception v 1.0.0.10 (.Net v 4.6.1) an exception is thrown when I try to create a ServiceHost (Lightinject.Wcf )
Inside the method
private static CustomAttributeBuilder CreateCustomAttributeBuilder(CustomAttributeData customAttributeData)

this statement fail
var constructor = customAttributeData.AttributeType.GetTypeInfo().DeclaredConstructors.Where(c => c.GetParameters().Select(p => p.ParameterType).SequenceEqual(constructorArguments)).Single();

Unsing Lightinject.Wcf we have a this object {Name = "ServiceBehaviorAttribute" FullName = "System.ServiceModel.ServiceBehaviorAttribute"}
that has 2 contruction with 0 parameters (one of them is static)

image

so the single statement fails

Intermittent: Value cannot be null.Parameter name: con

From @cephius473 on December 4, 2015 10:33

Hi,

I am currently using:
id="Castle.Core" version="3.3.0" targetFramework="net45"
id="LightInject" version="3.0.2.6" targetFramework="net45"
id="LightInject.Interception" version="1.0.0.8" targetFramework="net45"

Setup:

 public class MyClass : BaseClass
 {
 }

 public abstract class BaseClass : IDisposable
 {
    public virtual void Dispose()
    {
    }
 }

T = MyClass

My code goes like this:

 public void Test()
 {            
      _serviceContainer.Register<T>();            
      _serviceContainer.Intercept(
                     sr => sr.ServiceType == typeof(T),
                     (factory, definition) => Instance.defineProxyType(definition, new MyInterceptor()));

      T instance = default(T);

        using (Instance._serviceContainer.BeginScope())
        {
            instance = (T)(Instance._serviceContainer.TryGetInstance<T>());
        }
 }

 private void defineProxyType(ProxyDeefinition definition, IInterceptor myInterceptor)
 {
      definition.Implement(
               () => myInterceptor,
               m =>m.IsDeclaredBy(definition.TargetType) && m.IsPublic)
 }

When calling the TryGetInstance, intermittently, I encounter the error. Also for GetInstance

Trying to figure out
It seems when this is called, targetField is null

 private void PushProxyInstanceIfReturnValueEqualsTargetInstance(ILGenerator il, Type returnType)
         {
             var returnValueVariable = il.DeclareLocal(returnType);
             il.Emit(OpCodes.Stloc, returnValueVariable);
             var lazyTargetType = typeof(Lazy<>).MakeGenericType(proxyDefinition.TargetType);
             var getValueMethod = lazyTargetType.GetMethod("get_Value");
             il.Emit(OpCodes.Ldarg_0);
             il.Emit(OpCodes.Ldfld, targetField);

....

Then I became confused because the method which sets value for targetField, will always return since the proxyDefinition.TargetType.IsClass is true

  private void DefineTargetField()
  {
       if (proxyDefinition.TargetType.IsClass)
        {
             return;
        }

        Type targetFieldType;
        if (proxyDefinition.UseLazyTarget)
        {
            targetFieldType = typeof(Lazy<>).MakeGenericType(proxyDefinition.TargetType);    
        }
        else
        {
            targetFieldType = proxyDefinition.TargetType;
        }

        targetField = typeBuilder.DefineField("target", targetFieldType, FieldAttributes.Private);            
    }

Please correct me if I'm wrong at this.

might be related to:
seesharper/LightInject#201

StackTrace:
at System.Reflection.Emit.ModuleBuilder.GetFieldTokenNoLock(FieldInfo field)
at System.Reflection.Emit.ModuleBuilder.GetFieldToken(FieldInfo field)
at System.Reflection.Emit.ILGenerator.Emit(OpCode opcode, FieldInfo field)
at LightInject.Interception.ProxyBuilder.PushProxyInstanceIfReturnValueEqualsTargetInstance(ILGenerator il, Type returnType) in c:\GitHub\LightInject\NuGet\Build\Net45\LightInject.Interception\LightInject.Interception.cs:line 1512
at LightInject.Interception.ProxyBuilder.PushReturnValue(ILGenerator il, Type returnType) in c:\GitHub\LightInject\NuGet\Build\Net45\LightInject.Interception\LightInject.Interception.cs:line 1496
at LightInject.Interception.ProxyBuilder.ImplementInterceptedMethod(MethodInfo targetMethod, Int32[] interceptorIndicies) in c:\GitHub\LightInject\NuGet\Build\Net45\LightInject.Interception\LightInject.Interception.cs:line 1712
at LightInject.Interception.ProxyBuilder.ImplementMethod(MethodInfo targetMethod) in c:\GitHub\LightInject\NuGet\Build\Net45\LightInject.Interception\LightInject.Interception.cs:line 1680
at LightInject.Interception.ProxyBuilder.ImplementMethods() in c:\GitHub\LightInject\NuGet\Build\Net45\LightInject.Interception\LightInject.Interception.cs:line 1670
at LightInject.Interception.ProxyBuilder.GetProxyType(ProxyDefinition definition) in c:\GitHub\LightInject\NuGet\Build\Net45\LightInject.Interception\LightInject.Interception.cs:line 1233
at LightInject.InterceptionContainerExtensions.CreateProxyType(Type serviceType, Type[] additionalInterfaces, IServiceFactory serviceFactory, Action2 defineProxyType, ServiceRegistration registration) in c:\GitHub\LightInject\NuGet\Build\Net45\LightInject.Interception\LightInject.Interception.cs:line 136 at LightInject.InterceptionContainerExtensions.CreateProxyServiceRegistration(ServiceRegistration registration, Type[] additionalInterfaces, IServiceFactory serviceFactory, Action2 defineProxyType) in c:\GitHub\LightInject\NuGet\Build\Net45\LightInject.Interception\LightInject.Interception.cs:line 171
at LightInject.InterceptionContainerExtensions.<>c__DisplayClass3.b__2(IServiceFactory serviceFactory, ServiceRegistration registration) in c:\GitHub\LightInject\NuGet\Build\Net45\LightInject.Interception\LightInject.Interception.cs:line 66
at LightInject.ServiceContainer.EmitNewInstanceWithDecorators(ServiceRegistration serviceRegistration, IEmitter emitter) in c:\GitHub\LightInject\NuGet\Build\Net45\LightInject\LightInject.cs:line 3114
at LightInject.ServiceContainer.<>c__DisplayClass9e.b__9a(IEmitter methodSkeleton) in c:\GitHub\LightInject\NuGet\Build\Net45\LightInject\LightInject.cs:line 3739
at LightInject.ServiceContainer.<>c__DisplayClass39.b__38(IEmitter ms) in c:\GitHub\LightInject\NuGet\Build\Net45\LightInject\LightInject.cs:line 3059
at LightInject.ServiceContainer.CreateDynamicMethodDelegate(Action`1 serviceEmitter) in c:\GitHub\LightInject\NuGet\Build\Net45\LightInject\LightInject.cs:line 2980
at LightInject.ServiceContainer.CreateDelegate(Type serviceType, String serviceName, Boolean throwError) in c:\GitHub\LightInject\NuGet\Build\Net45\LightInject\LightInject.cs:line 3824
at LightInject.ServiceContainer.CreateDefaultDelegate(Type serviceType, Boolean throwError) in c:\GitHub\LightInject\NuGet\Build\Net45\LightInject\LightInject.cs:line

Copied from original issue: seesharper/LightInject#229

LightInject.WebApi/build/common.csx ValidateCodeCoverage string comparison failure

This regex
var coverage = Regex.Match(summaryContent, "LineCoverage>(.*)<",RegexOptions.IgnoreCase).Groups[1].Captures[0].Value;
gets from report "100% (886 of 886)" instead of "100%" and then compares it with if (coverage != "100%")
As result throw new InvalidOperationException("Deploy failed. Test coverage is only " + coverage);
And AppVeyor build fails

"Value cannot be null. Parameter name: meth" since 2.0.2

Hi,

An error occur since we upgrade from the version 2.0.1 to 2.0.2.

We are using LightInject v5.1.6

This are the way we use the interception

public static void Register(HttpConfiguration config)
        {
            var container = new ServiceContainer();

            RegisterInjection(container);

            container.EnablePerWebRequestScope();
            container.ScopeManagerProvider = new PerLogicalCallContextScopeManagerProvider();
            container.RegisterApiControllers();
            container.EnableWebApi(GlobalConfiguration.Configuration);
        }

        private static void RegisterInjection(ServiceContainer container)
        {
            // Register business layer
            container.Register<ICriteriaDomain, CriteriaDomain>();
            ...
            
            // Register data layer
            container.Register<ICriteriaRepository, CriteriaRepository>(new PerRequestLifeTime());
            ...
            
           // Register container itself
            container.RegisterInstance<IServiceContainer>(container);
            
            // Intercept
            container.Intercept(sr => sr.ServiceType == typeof(ICriteriaDomain), factory => new LogErrorsInterceptor());
            

            container.Intercept(sr => sr.ServiceType == typeof(ICriteriaRepository), factory => new RefreshViewInterceptor());
        }

and there are interceptors :

public object Invoke(IInvocationInfo invocationInfo)
{
            var output = invocationInfo.Proceed();

            if (invocationInfo.Method.Name.Contains("Create") || invocationInfo.Method.Name.Contains("Update") ||
                invocationInfo.Method.Name.Contains("Delete") || invocationInfo.Method.Name.Contains("Add"))
            {
                // Refresh
                ...
            }

            return output;
}
public object Invoke(IInvocationInfo invocationInfo)
{
            try
            {
                return invocationInfo.Proceed();
            }
            catch (Exception ex)
            {
                log.Error(...);
                throw;
            }
}

Could you help me please ?

Thank for help.

Diplomegalo

Intercepting asynchronous methods

Hello,

I have repository with async methods and a want to create logging interceptor. The problem is that catch block doesn't fire because of async method and I can't await invovationInfo.Proceed() inside Invoke method:

        public object Invoke(IInvocationInfo invocationInfo)
        {
            try
            {
                Debug.WriteLine("Start");
                var returnValue = invocationInfo.Proceed();
                Debug.WriteLine("End");
                return returnValue;
            }
            catch(Exception e)
            {
                Debug.WriteLine("Exception: " + e.Message);
                throw;
            }
        }

So the question is: how to work with async method inside interceptor and how to use the same interceptor with async and non-async methods?

P.S. I've found this https://msdn.microsoft.com/en-us/magazine/dn574805.aspx article about intercepting asynchronous methods using Unity, but it looks quite complicated and how to use this approach with LightInject?

Directly registered classes are not intercepted

public class Foo { public string GetName() { return nameof(Foo); } }
public class Interceptor : IInterceptor
{
    public bool Invoked { get; private set; }
    public object Invoke(IInvocationInfo invocationInfo)
    {
        this.Invoked = true;
        return invocationInfo.Proceed();
    }
}

// Directly register a class as service type and implementing type
container.Register<Foo>();
var interceptor = new Interceptor();
container.Intercept(sr => sr.ServiceType == typeof(Foo), _ => interceptor);
var foo = container.GetInstance<Foo>();
foo.GetName();

// Assertion fails
Assert.True(interceptor.Invoked);

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.