Giter Site home page Giter Site logo

interception's People

Contributors

bennage avatar chavoshi avatar eniks avatar fsimonazzi avatar istagir avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

interception's Issues

ICallHandler did not intercept class with HandlerAttribute after upgrading from Unity 4 to Unity 5

Hi,

I am trying to update the Unity from 4.0 to 5.10, and my logging interceptor did not work, can somebody give me some idea? Thanks.

My Container setup/

container.Register<ICallHandler, LogCallHandler>(nameof(LogCallHandler));
container.AddNewExtension<Interception>();

and all my interface-implementaion registrations are going through the following helper function.

 if (typeof(Service).IsInterface)
{
        injectionMembers.Add(new InterceptionBehavior<PolicyInjectionBehavior>());
        injectionMembers.Add(new Interceptor<InterfaceInterceptor>());
 }
        var lifetimeManager = registerAsSingleton ? new ContainerControlledLifetimeManager() : null;

         container.RegisterType<Service, Implementation>(name, lifetimeManager, injectionMembers.ToArray());

and my targetted class are annotated with a HandlerAttribute

Remove SetInterceptorXX methods

Due to the change in how types are created late interceptor additions no longer supported.

Unity makes all decisions about how to create object during registration. These methods do not register types but merely add indicator policies to already registered types and no longer work as expected.

exception.StackTrace is truncated when using Interceptors

@cshung wrote:

Consider the following code snippet

using Microsoft.Practices.Unity;
using Microsoft.Practices.Unity.InterceptionExtension;
using System;
using System.Collections.Generic;

namespace ConsoleApplication1
{
public interface IService
{
void work();
}

   public class Service : IService
   {
       public void work() { a(); }
       private void a() { b(); }
       private void b() { c(); }
       private void c() { throw new NotImplementedException { }; ; }
   }

   public class Interceptor : IInterceptionBehavior
   {
       public bool WillExecute { get { return true; } }
       public IEnumerable<Type> GetRequiredInterfaces() { return Type.EmptyTypes; }

       public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext)
       {
           Console.WriteLine("Enter invoke");
           var result = getNext()(input, getNext);
           Console.WriteLine("Exit invoke");
           return result;
       }
   }

   public class UnityStackTraceMain
   {
       static void Main(string[] args)
       {
           var container = new UnityContainer { };
           container.AddNewExtension<Interception>();
           //container.AddNewExtension<AppContainerExtension>();

           container.RegisterType<IService, Service>(
               new ContainerControlledLifetimeManager(),
               new Interceptor<InterfaceInterceptor>(),
               new InterceptionBehavior<Interceptor>());

           try
           {
               container.Resolve<IService>().work();
           }
           catch (System.Exception ex)
           {
               Console.WriteLine(ex.ToString());
               Console.WriteLine(ex.InnerException);
           }

           Console.ReadLine();
       }
   }

}

We expect the ex.ToString() has the frames a(), b() and c(), but instead, it only have the dynamic generated function.

The root cause of this is because the exception is caught and thrown again. .NET does not preserve the stack trace in the exception in that case and is clearly documented.

The proper way to fix this is to wrap the exception. In order to make existing code work with the fix. I wrap the exception with the same type and same message, that should allow most code to work with it.

Since the interceptor code is not available in this repo, I created the pull request to fix this in CodePlex instead.

https://unity.codeplex.com/SourceControl/network/forks/cshung/unity/contribution/8726

Interface interception doesn't work if the interface is implemented by a base type of the implementing type

@vmelamed wrote:

The following code illustrates the problem:

public interface ITestCalls
{
    void Test1();
}

public abstract class BaseTestCalls : ITestCalls
{
    public void Test()
    {
    }
}

[Tag("track")]
public class TrackTestCalls : BaseTestCalls
{
}

[Tag("track1")]
public class TrackTestCalls1 : ITestCalls
{
    public void Test()
    {
    }
}

static void Main(string[] args)
{
    IUnityContainer container = new UnityContainer();

    container.AddNewExtension<Interception>();

    container
        .Configure<Interception>()
        .AddPolicy("track")
        .AddMatchingRule<TagAttributeMatchingRule>(
                            new InjectionConstructor("track", false))
        .AddCallHandler<TrackCallHandler>(
                            new ContainerControlledLifetimeManager())
        ;

    container
        .RegisterType<ITestCalls, TrackTestCalls>(
            "track",
            new InterceptionBehavior<PolicyInjectionBehavior>(),
            new Interceptor<TransparentProxyInterceptor>())

        .RegisterType<ITestCalls, TrackTestCalls1>(
            "track1",
            new InterceptionBehavior<PolicyInjectionBehavior>(),
            new Interceptor<TransparentProxyInterceptor>())
        ;

    var test = container.Resolve<ITestCalls>("track");

    test.Test();

    var test1 = container.Resolve<ITestCalls>("track1");

    test1.Test();
}

test.Test() does not exhibit the decorated behavior and the object test is not supplied with a pipeline. However test1.Test() behaves as expected and test1 has the pipeline.

I could not find this being documented anywhere.

Incorrect lifetime management for CallHandlers

Description

When configuring an interception policy that uses a CallHandler which it depends upon injection of other services registered in the container, the lifetime is ignored and the same instance of the call handler is used for different resolution contexts.

This used to work fine up to:

  • Unity.Abstractions: 3.2.0
  • Unity.Container: 5.6.1
  • Unity.Interception: 5.4.0

But started failing on the next release:

  • Unity.Abstractions: 3.3.0
  • Unity.Container: 5.7.0
  • Unity.Interception: 5.5.0

To Reproduce

See the following test

        [TestMethod]
        public void TestLifetimeManagement()
        {
            //Container setup
            var container = new UnityContainer();
            container.AddNewExtension<Interception>();
            container.RegisterType<ILog, Log>(new TransientLifetimeManager());
            container.RegisterType<ITestService, TestService>(new TransientLifetimeManager(),
                new InterceptionBehavior<PolicyInjectionBehavior>(), new Interceptor<VirtualMethodInterceptor>());
            container.Configure<Interception>()
                .AddPolicy("logging")
                .AddMatchingRule(new TypeMatchingRule(typeof(TestService)))
                .AddCallHandler<LogCallHandler>(
                    new TransientLifetimeManager(),
                    new InjectionConstructor(new ResolvedParameter<ILog>()),
                    new InjectionProperty("Order", 1)
                );

            var testService1 = container.Resolve<ITestService>();
            testService1.GetValues();
            Assert.AreEqual(1, LogCallHandler.InstanceCount);

            var testService2 = container.Resolve<ITestService>();
            testService2.GetValues();
            Assert.AreEqual(2, LogCallHandler.InstanceCount);
        }

And considder that the LogCallHandler is defined as

    public class LogCallHandler : ICallHandler
    {
        public static int InstanceCount { get; private set; }

        private readonly ILog log;

        public LogCallHandler(ILog log)
        {
            this.log = log;
            ObjectInstance = ++InstanceCount;
        }

        public int Order { get; set; }

        public int ObjectInstance { get; }

        public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
        {
            var method = $"{input.MethodBase.DeclaringType}.{input.MethodBase.Name}";

            log.Write($"{method} - starting invocation by call handler instance {ObjectInstance}");

            var result = getNext()(input, getNext);

            log.Write($"{method} - invocation ended by call handler instance {ObjectInstance}");

            return result;
        }
    }

Depending on the set of libraries used (as described on the first section) the second assert will succeed or fail.

You'd expect that the call handler is resolved by the container in the same way as any other type, according to what the lifetime manager dictates.

Additional context
This might look trivial at a first glance, but when using a LifetimeManager such as the PerRequestLifetimeManager and when the injected member for the CallHandler is something that can't be treated as a singleton, a big problem arises (for instance: the object will be already disposed when the call handler tryes to use it on the second round).

I have both a unit test project as well as a realistic web application with this behavior which I'm happy to provide. I've tried to go into the belly of Unity to understand this better, but got a bit lost when doing so. If I happen to discover more details on the source of the problem will update this (and if I get it right, even send you a pull request).

NullReferenceException in Unity.Interception.Interceptors.GenericParameterMapper.Map

We are currently finding an issue where we are testing a DC failover and are getting null exceptions when we fail over to another DC. The app recovers, but the first few hits give us this error:

problemId: System.NullReferenceException at Unity.Interception.Interceptors.GenericParameterMapper.Map
method: Unity.Interception.Interceptors.GenericParameterMapper.Map
outerType: Unity.Exceptions.ResolutionFailedException
assembly: Unity.Interception, Version=5.5.3.0, Culture=neutral, PublicKeyToken=489b6accfaf20ef0
outerAssembly: Unity.Container, Version=5.8.6.0, Culture=neutral, PublicKeyToken=489b6accfaf20ef0

It's resolving a type that is clearly defined and works, but the initial hits fail and we don't know why.

Here is the stack trace:

[
{
"parsedStack": [
{
"assembly": "Unity.Container, Version=5.8.6.0, Culture=neutral, PublicKeyToken=489b6accfaf20ef0",
"method": "Unity.UnityContainer.ThrowingBuildUp",
"level": 0,
"line": 0
},
{
"assembly": "Unity.Container, Version=5.8.6.0, Culture=neutral, PublicKeyToken=489b6accfaf20ef0",
"method": "Unity.UnityContainer.Resolve",
"level": 1,
"line": 0
},
{
"assembly": "Unity.Abstractions, Version=3.3.0.0, Culture=neutral, PublicKeyToken=6d32ff45e0ccc69f",
"method": "Unity.UnityContainerExtensions.Resolve",
"level": 2,
"line": 0
},
{
"assembly": "Company.Cs.ObjectFactory.Unity, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
"method": "Company.Cs.ObjectFactory.Unity.ObjectFactory.Create",
"level": 3,
"line": 0
},
{
"assembly": "Company.Cs.Web.Mvc, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
"method": "Company.Cs.Web.Mvc.Filters.SingleSignOnAuthorizationFilter.get_TokenStore",
"level": 4,
"line": 0
},
{
"assembly": "Company.Cs.Web.Mvc, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
"method": "Company.Cs.Web.Mvc.Filters.SingleSignOnAuthorizationFilter.OnAuthorization",
"level": 5,
"line": 0
},
{
"assembly": "Company.Cs.InternalChat.Web.Ui, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
"method": "Company.Cs.InternalChat.Web.Ui.Filters.SsoAuthorizationFilter.OnAuthorization",
"level": 6,
"line": 0
},
{
"assembly": "System.Web.Mvc, Version=5.2.6.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35",
"method": "System.Web.Mvc.ControllerActionInvoker.InvokeAuthorizationFilters",
"level": 7,
"line": 0
},
{
"assembly": "System.Web.Mvc, Version=5.2.6.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35",
"method": "System.Web.Mvc.Async.AsyncControllerActionInvoker+<>c__DisplayClass3_1.b__0",
"level": 8,
"line": 0
}
],
"outerId": "0",
"message": "Resolution of the dependency failed, type = 'Company.Cs.Interfaces.TokenStores.ISingleSignOnTokenStore', name = '(none)'.\nException occurred while: while resolving.\nException is: NullReferenceException - Object reference not set to an instance of an object.\n-----------------------------------------------\nAt the time of the exception, the container was: \r\n Resolving Company.Cs.Interfaces.TokenStores.ISingleSignOnTokenStore,(none)\r\n",
"type": "Unity.Exceptions.ResolutionFailedException",
"id": "9781729"
},
{
"parsedStack": [
{
"assembly": "Unity.Interception, Version=5.5.3.0, Culture=neutral, PublicKeyToken=489b6accfaf20ef0",
"method": "Unity.Interception.Interceptors.GenericParameterMapper.Map",
"level": 0,
"line": 0
},
{
"assembly": "Unity.Interception, Version=5.5.3.0, Culture=neutral, PublicKeyToken=489b6accfaf20ef0",
"method": "Unity.Interception.Interceptors.InstanceInterceptors.InterfaceInterception.InterfaceMethodOverride.CreateDelegateImplementation",
"level": 1,
"line": 0
},
{
"assembly": "Unity.Interception, Version=5.5.3.0, Culture=neutral, PublicKeyToken=489b6accfaf20ef0",
"method": "Unity.Interception.Interceptors.InstanceInterceptors.InterfaceInterception.InterfaceImplementation.Implement",
"level": 2,
"line": 0
},
{
"assembly": "Unity.Interception, Version=5.5.3.0, Culture=neutral, PublicKeyToken=489b6accfaf20ef0",
"method": "Unity.Interception.Interceptors.InstanceInterceptors.InterfaceInterception.InterfaceImplementation.Implement",
"level": 3,
"line": 0
},
{
"assembly": "Unity.Interception, Version=5.5.3.0, Culture=neutral, PublicKeyToken=489b6accfaf20ef0",
"method": "Unity.Interception.Interceptors.InstanceInterceptors.InterfaceInterception.InterfaceInterceptorClassGenerator.CreateProxyType",
"level": 4,
"line": 0
},
{
"assembly": "Unity.Interception, Version=5.5.3.0, Culture=neutral, PublicKeyToken=489b6accfaf20ef0",
"method": "Unity.Interception.Interceptors.InstanceInterceptors.InterfaceInterception.InterfaceInterceptor.CreateProxy",
"level": 5,
"line": 0
},
{
"assembly": "Unity.Interception, Version=5.5.3.0, Culture=neutral, PublicKeyToken=489b6accfaf20ef0",
"method": "Unity.Interception.Intercept.ThroughProxyWithAdditionalInterfaces",
"level": 6,
"line": 0
},
{
"assembly": "Unity.Interception, Version=5.5.3.0, Culture=neutral, PublicKeyToken=489b6accfaf20ef0",
"method": "Unity.Interception.ContainerIntegration.ObjectBuilder.InstanceInterceptionStrategy.PostBuildUp",
"level": 7,
"line": 0
},
{
"assembly": "Unity.Container, Version=5.8.6.0, Culture=neutral, PublicKeyToken=489b6accfaf20ef0",
"method": "Unity.UnityContainer.ThrowingBuildUp",
"level": 8,
"line": 0
}
],
"outerId": "9781729",
"message": "Object reference not set to an instance of an object.",
"type": "System.NullReferenceException",
"id": "20687649"
}
]

The "PerResolve" lifetime is not properly respected when the resolving interception behaviors for an object

I've noticed some recent activity on these repos so I'm going to shoot my shot.

I am upgrading some code (that's an understatement) from Unity 3 to Unity 5 and have encountered what is either a bug or a significant behavioral difference regarding interception between the versions.

In Unity 5.11 (and likely earlier versions, I haven't explored deep enough to find where the issue starts), Unity will re-resolve objects registered with the "PerResolveLifetimeManager" when resolving objects needed for any defined interception behavior. I have verified that the same object will be used across the object graph for any reference that is not part of interception behavior.

Here is a test case that should demonstrate the issue. I tried another lifetime ("PerThread") to demonstrate that it does seem to specifically be an issue with "PerResolve".

UnityPerResolveInterceptionBrokenPoc.zip

I've pulled the interception repo and have been trying to uncover the issue, but I'm quite a bit out of my depth. Depending on the scope of the fix (I would have to evaluate fixing this vs other options for my team to consider), I'd be willing to submit a PR if I can get pointed in the right direction.

Method injection call handler not being released

We are attempting to upgrade from Unity 2x to Unity 5.8.11. Everything appears to be working except that the memory allocated for CallHandlers associated with a class that uses a TransientLifetimeManager is not be released by Unity.

I have tried registering the class two different ways with the same result:

Unity Interception Documentation

Is there any documentation about the Interception extension?
I was only able to find blog posts but they seem to use older versions of Unity

Thanks.-

Interceptor is registered for ToType instead of FromType

Current implementation follows the suit with Unity and exhibits the same behavior as described here

It associates Intercept information with ToType and as result suffers from the same error. Subsequent registrations could affect previously registered unrelated types.

.NetStandard - Interception works 1 level, but not 2.

Interception at one level works...
ControllerA(IBusinessServiceA businessService)

BusinessService is injected, I see WillExecute, GetRequiredInterfaces, Invoke all run

Interception 2-deep does not work...
ControllerB(IBusinessServiceB businessService)
BusinessServiceB(IDal dal)

I see Will Execute, GetRequiredInterfaces for Dal, and I see the DAL constructor fire, I see the BusinessServiceB constructor fire. WillExecute and GetRequiredInterfaces fire, then I get an exception.

container.RegisterType<IBusinessServiceA, BusinessServiceA>(new Interceptor<InterfaceInterceptor>(), new InterceptionBehavior<MyInterceptionBehaviorA>());
container.RegisterType<IBusinessServiceB, BusinessServiceB>(new Interceptor<InterfaceInterceptor>(), new InterceptionBehavior<MyInterceptionBehaviorA>());
container.RegisterType<IDal, Dal>(new Interceptor<InterfaceInterceptor>(), new InterceptionBehavior<MyInterceptionBehaviorB>());

Works fine with normal registration.

container.RegisterType<IBusinessServiceA, BusinessServiceA>();
container.RegisterType<IBusinessServiceB, BusinessServiceB>();
container.RegisterType<IDal, Dal>();

Here's the exception...

warn: Microsoft.AspNetCore.Server.Kestrel[0]
Overriding address(es) 'http://localhost:53382/'. Binding to endpoints defined in UseKestrel() instead.
Hosting environment: Development
Content root path: c:\users\scovel\Source\Repos\TestUnity\TestUnity
Now listening on: http://127.0.0.1:8088
Application started. Press Ctrl+C to shut down.
fail: Microsoft.AspNetCore.Server.Kestrel[13]
Connection id "0HLDAT7258BVT", Request id "0HLDAT7258BVT:00000001": An unhandled exception was thrown by the application.
System.InvalidOperationException: Unable to resolve service for type 'TestUnity.Implementations.IBusinessServiceB' while attempting to activ
ate 'TestUnity.Controllers.ValuesControllerB'.
at Microsoft.Extensions.Internal.ActivatorUtilities.GetService(IServiceProvider sp, Type type, Type requiredBy, Boolean isDefaultParamete
rRequired)
at lambda_method(Closure , IServiceProvider , Object[] )
at Microsoft.AspNetCore.Mvc.Controllers.ControllerActivatorProvider.<>c__DisplayClass4_0.b__0(ControllerContext controll
erContext)
at Microsoft.AspNetCore.Mvc.Controllers.ControllerFactoryProvider.<>c__DisplayClass5_0.g__CreateController|0(Con
trollerContext controllerContext)
at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.d__14.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.d__22.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Rethrow(ResourceExecutedContext context)
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.d__17.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.d__15.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.Builder.RouterMiddleware.d__4.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Swashbuckle.AspNetCore.SwaggerUI.SwaggerUIIndexMiddleware.d__3.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.d__6.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.Hosting.Internal.RequestServicesContainerMiddleware.d__3.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.Frame`1.d__2.MoveNext()

RegisterFactory with Interceptors

Hi,

Since the InjectionFactory is deprecated, I am trying to use the RegisterFactory method.
I have registrations which use a factory and also have interceptor(s):

Func<IUnityContainer, object> factoryFunc = c => new FileSystemWithWaitAndRetry(
	new FileSystemService(container.Resolve<System.IO.Abstractions.IFileSystem>()),
	container.Resolve<ILogger>());

container.RegisterType<IFileSystemService>(
	createLifetimeManager(),
	new InjectionFactory(factoryFunc),
	new Interceptor<InterfaceInterceptor>(),
	new InterceptionBehavior(new ImpersonationBehavior(sSettings.ImpersonatorDomain, sSettings.ImpersonatorUsername, sSettings.ImpersonatorPassword, container.Resolve<ILogger>())));

(This is a service for filesystem access with a 'wait and retry' decorator. Interception adds impersonation)

How can the RegisterType be rewritten to RegisterFactory and still pass the InterceptionBehaviour?
Is the RegisterFactory method missing some functionality?

Thanks!

Interception doesn't work for instance added with RegisterInstance

  1. Prepare some class and its interface.
  2. Manually create an instance of that class and register it using RegisterInstance method.
  3. Set transparent proxy interceptor for the interface.
  4. Resolve an object by the interface.

Current: container returns original instance, all method calls go directly to that instance.
Expected: container returns a proxy wrapper, method calls can be handled by the interceptor.

Broken versions: 5.x (tried 5.9.0 and latest stable 5.11.1).
Working versions: 4.0.1 and older.

There is no problem with RegisterType method, unity returns a proxy wrapper as expected in a such scenario. The issue affects only RegisterInstance-way. Both default and named instances are being returned unwrapped.

Below is a unit test to demonstrate the issue, it works well with Unity versions up to 4.0.1 and fails with 5.x:

[TestFixture]
public class InterceptionTests
{
    [Test]
    public void RegisteredInstanceIntercepting_ReturnsProxy()
    {
        var container = new UnityContainer();
        container.AddNewExtension<Interception>();

        var instance = new SomeService();

        container
            .RegisterInstance<ISomeService>(instance)
            .Configure<Interception>()
            .SetInterceptorFor<ISomeService>(new TransparentProxyInterceptor())
            .AddPolicy("SomePolicy")
            .AddMatchingRule(new TypeMatchingRule(typeof(ISomeService)))
            .AddCallHandler(new SomeCallHandler());

        var wrapper = container.Resolve<ISomeService>();

        Assert.IsTrue(RemotingServices.IsTransparentProxy(wrapper), "Wrapper is not a proxy");
        Assert.AreNotSame(instance, wrapper);
    }

    private interface ISomeService
    {
        void Foo();
    }

    private sealed class SomeService : ISomeService
    {
        public void Foo() => throw new NotImplementedException();
    }

    private sealed class SomeCallHandler : ICallHandler
    {
        public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext) => throw new NotImplementedException();
        public int Order { get; set; }
    }
}

Is it enough for you or should I attach a zip with the whole project?
Am I missing some breaking change with interceptors between 4.x and 5.x?
Is there some workaround?

System.ArgumentOutOfRangeException with VirtualMethodInterceptor and ref parameters

@kostemar Wrote:

I ran into a problem with the Unity.Interception extension (using Unity v4.0.1 and Unity.Interception v4.0.1) and VirtualMethodInterception.

Since the issue is a bit difficult to explain, I attached a console application that reproduces the issue. Basically, I have some "domain objects" that look something like this:

public interface IDataImportJob
{
    void Run();
}

public abstract class DataImportBase : IDataImportJob
{
    public virtual void Run()
    {
        OnRun();
    }

    protected abstract bool OnRun();
}

public interface IProductPartAndOptionMasterDataImport : IDataImportJob
{
}

public class ProductPartAndOptionMasterDataImport : DataImportBase, IProductPartAndOptionMasterDataImport
{
    private readonly IOtherObject _otherObject;

    public ProductPartAndOptionMasterDataImport(IOtherObject otherObject)
    {
        _otherObject = otherObject;
    }

    protected override bool OnRun()
    {
        var productsOk = RunProductPartAndOptionImport();

        return productsOk;
    }

    protected virtual bool RunProductPartAndOptionImport()
    {
        int partsProcessed = 42;
        ImportProductPartFromProduct(ref partsProcessed);
        return true;
    }

    [TracingCallHandler(true, true, true)]
    // if you remove the "partsProcessed" parameter here, the issue does not occur.
    protected virtual void ImportProductPartFromProduct(ref int partsProcessed)
    {
        int someParameter = 0;
        _otherObject.SomeMethod(ref someParameter);
    }
}

public interface IOtherObject
{
    void SomeMethod(ref int someArgument);
}

public class OtherObject : IOtherObject
{
    public virtual void SomeMethod(ref int someArgument)
    {
        throw new Exception("Something bad happened.");
    }
}

The TracingCallHandler is a CallHandler that can log and swallow exceptions.

Now, when I run the console application, the "Something bad happened" exception is logged as expected, but then I get a second exception
(System.ArgumentOutOfRangeException) that crashes the application.

I guess the problem is that somewhere in the dynamically generated code (DynamicModule.ns.Wrapped_ ...) a ParameterCollection is
created with an empty arguments array. Then, the indexer of that ParameterCollection is used with an index of 0, which will eventually result in that System.ArgumentOutOfRangeException.

If you remove the partsProcessed parameter from the ImportProductPartFromProduct method, the issue does not occur.

Async method support in IInterceptionBehaviour

My usecase is that I'm trying to implement entry/exit/exception logging on an async method.
I an IInterceptionBehavoiur implementation, the call to getNext()(...) is performed asynchronously and I don't get to log the exit of the funciton.

Optimize ITypeInterceptionPolicy

Interface is required in order to retrieve ITypeInterceptor but it is not required and could be changed to simple IUnityContainer. This will allow to move interception analysis into registration phase and will noticeable boost performance.
The legacy way of calling this interface will be preserved via Extension method.

Optimize IInterceptionBehaviorsPolicy

Interface is requires IBuilderContext in order to retrieve IEnumerable<IInterceptionBehavior> but it could be changed to use IUnityContainer. This will allow to move interception analysis into registration phase and will noticeable boost performance.

The legacy way of calling this interface will be preserved via Extension method.

Bug: Registrations are wrong for .NET Core version.

Description

It's not the same as unitycontainer/container#160, but perhaps it is related to each other.

How to reproduce

Compile and run the following code for .NET Core (I tried for 2.1) and Framework (tried for 4.7.2). The output is different.

class Program
{
    static void Main(string[] args)
    {
        var container = new UnityContainer()
            .AddNewExtension<Interception>()
            .RegisterType<Foo>()
            .RegisterType<IBar, Bar>();

        var child = container.CreateChildContainer()
            .RegisterType<IFoo, Foo>(new ContainerControlledLifetimeManager())
            .RegisterType<Foo>(new ContainerControlledLifetimeManager());

        var registrations = child.Registrations.Where(r => r.RegisteredType == typeof(Foo));

        foreach (var registration in registrations)
        {
            Console.WriteLine(
                $"{registration.RegisteredType.Name}->" +
                $"{registration.MappedToType.Name}" +
                $"[{registration.LifetimeManager}]");
        }
    }

    public interface IFoo { }
    public class Foo : IFoo { }

    public interface IBar { }
    public class Bar : Foo, IBar { }
}

For Framework version the output is

Foo->Foo[Lifetime:PerContainer]

But for .NET Core version is:

Foo->Foo[Lifetime:Transient]
Foo->Foo[Lifetime:PerContainer]

Expected behavior

For .NET Core must be the same registration as for Framework. The transient registration is obviously wrong.

Unable to use InterfaceInterception with Covariant / Contravariant Interfaces

@japrom wrote:

I have a pretty trivial example using latest on NuGet (Unity / Unity.Interception 4.0.1)

public interface IInterface<in TIn, out TOut>
{
  TOut DoSomething(TIn input);
}

public class Thing : IInterface<string, string>
{
  public string DoSomething(string input)
  {
    return input;
  }
}

public class TestInterceptor : IInterceptionBehavior
{
    public bool WillExecute => true;

    public IEnumerable<Type> GetRequiredInterfaces()
    {
        return Type.EmptyTypes;
    }

    public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext)
    {
        return getNext()(input, null);
    }
}

public void Main(string[] args)
{
    var proxy = Intercept.ThroughProxy<IInterface<string, string>>(
      new Thing(),
      new InterfaceInterceptor(),
      new[] { new TestInterceptor() });

    proxy.DoSomething("hello world");
}

Results in the exception:

TypeLoadException: Could not load type 'DynamicModule.ns.Wrapped_IInterface`2_f77b7d7f16c54c3393a389eedeced496' from assembly 'Unity_ILEmit_InterfaceProxies, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' because it declares a covariant or contravariant type parameter and is not an interface or delegate.

I dug into the code a bit and found InterfaceInterceptorClassGenerator.cs.

Zeroing the *variant flags seems to make it work for me.

genericTypes[i].SetGenericParameterAttributes(
  genericArguments[i].GenericParameterAttributes & ~GenericParameterAttributes.VarianceMask
);

Is there any reason why they should be there?

Resolve slows down when an interceptor is set

Problem

The problem is resolve slows down when an interceptor is set. Our company had several production incidents when it happens. It is clearly visible when resolve in multi-threaded with high degree of parallelism. If you want we can provide benchmark tests shown difference between current implementation and the suggested one (see below).

Root Cause

Let's suppose we have the following code:

var container = new UnityContainer()
    .AddNewExtension<Interception>()
    .RegisterType<IFoo, Foo>();

container
    .Configure<Interception>()
    .SetDefaultInterceptorFor<IFoo>(new InterfaceInterceptor());

container.Resolve<IFoo>(); // Resolve #1
container.Resolve<IFoo>(); // Resolve #2

Every resolve PipelineManager is instantiated here. Then CreatePipeline method invokes GetBaseDefinition() twice here and here. GetBaseDefinition uses inside locks that leads to slowing down of whole dependency resolves.

Possible Solution

Base method definitions can be cached in static ConcurrentDictionary, for example. So CreatePipeline can be rewritten something like this:

private static readonly ConcurrentDictionary<HandlerPipelineKey, MethodInfo> BaseMethodDefinitions =
    new ConcurrentDictionary<HandlerPipelineKey, MethodInfo>();

private HandlerPipeline CreatePipeline(MethodInfo method, IEnumerable<ICallHandler> handlers)
{
    var key = HandlerPipelineKey.ForMethod(method);
    if (_pipelines.TryGetValue(key, out var pipeline))
    {
        return pipeline;
    }

    var baseMethodDefinition = BaseMethodDefinitions
        .GetOrAdd(key, pipelineKey => method.GetBaseDefinition());

    if (baseMethodDefinition == method)
    {
        _pipelines[key] = pipeline = new HandlerPipeline(handlers);
        return pipeline;
    }

    _pipelines[key] = pipeline = CreatePipeline(baseMethodDefinition, handlers);
    return pipeline;
}

@ENikS If you want we can prepare a PR, so you do not spend time on it. Also, as I mentioned, we can provide benchmark tests of current solution and supposed one.

Configuration of Named interception no longer supported

These methods are deprecated:

public Interception SetInterceptorFor(Type typeToIntercept, string? name, ITypeInterceptor interceptor)
public Interception SetInterceptorFor(Type typeToIntercept, string? name, IInstanceInterceptor interceptor)

Unity Interception and strong named asseblies won't work

@mizasie wrote:

Hello,

We use strong named assemblies and internal interfaces/classes in our solutions. Unfortunately it is not possible to use Unity Interception in that case. It creates a dynamic assembly which is not signed and therefore not loaded. We figured out how easily that could be implemented. We did the change locally and rebuild Unity Interception by ourselves. It works great in our particular case.

Important hint: This is only an issue, if you work with internal interfaces/classes. You have to add the following line to your AssemblyInfo.cs

[assembly: InternalsVisibleTo("Unity_ILEmit_InterfaceProxies, PublicKey=002400000480000094000000060200000024000052534131000400000100010095d32d9c234a5c26a67e6f9e63c75501460914df0da0774b35cfd527aaaf774d8b751091c06d63e22d5704eafda93e7666bb7b446abfda77696aec773acff37d117ddd0e688a468505776c154915906181812fb3191b99d389dc5fc4faca70dc7113932ea239c9299edb068d157ab375e38408f54f1f9e69b69ae8e989daafb1")]

Have a look into the constructor without parameters:
https://github.com/unitycontainer/unity/blob/master/source/Unity.Interception/Src/Interceptors/InstanceInterceptors/InterfaceInterception/InterfaceInterceptorClassGenerator.cs

We changed it into that:

[SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline",
    Justification = "Need to use constructor so we can place attribute on it.")]
static InterfaceInterceptorClassGenerator()
{
    var assemblyName = new AssemblyName("Unity_ILEmit_InterfaceProxies")
                           {
                               KeyPair = new StrongNameKeyPair(Resources.unity)
                           };

    AssemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(
        assemblyName,
#if DEBUG_SAVE_GENERATED_ASSEMBLY
        AssemblyBuilderAccess.RunAndSave);
#else
        AssemblyBuilderAccess.Run);
#endif
}

The key file itself could be found on the top level within the build folder. Its name is “unity.snk”. We imported that file as resource file in Visual Studio and named it unity. Therefore it’s possible to access the content with the command Resources.unity.

It would be great to have that functionality in the official builds too.
Thanks for all your efforts.

Cheers
Micha

Error 'The type is not interceptable' for overwritten registrations

Hi,

The following error occurs when resolving the interface IADUtilities:

The type X.Business.Utilities.ADUtilitiesStubbed is not interceptable.
Parameter name: interceptedType


Exception occurred while:

·resolving type: 'ADUtilitiesStubbed'
•resolving type: 'IADUtilities' mapped to 'ADUtilitiesStubbed'

My setup:

public interface IAdUtilities {}
public class AdUtilities : IAdUtilities {}
public class AdUtilitiesStubbed : IAdUtilities {}

The registrations are build like:
1. Registration by convention:

var assemblies = System.IO.Directory.GetFiles(AppDomain.CurrentDomain.BaseDirectory, AssemblyConstants.AssemblyNamePrefix + "*.dll")
	.Select(f => System.Reflection.Assembly.LoadFile(f));

container.RegisterTypes(
	AllClasses.FromAssemblies(assemblies).Where(t => t.Namespace.StartsWith(AssemblyConstants.AssemblyNamePrefix, StringComparison.OrdinalIgnoreCase)),
	WithMappings.FromMatchingInterface,
	WithName.Default,
	t => lifetimeManager());

Result of this:

  • IAdUtilities is registered to AdUtilities and resolvable
  • AdUtilitiesStubbed is registered to itself and resolvable

2. Overwrite convention registrations by more advanced registration

container.RegisterType<IADUtilities, ADUtilities>(
	createLifetimeManager(),
	new Interceptor<InterfaceInterceptor>(),
	new InterceptionBehavior<LogBehavior>(),
	new InterceptionBehavior(new ImpersonationBehavior("mydomain", "myUsername", "myPassword", container.Resolve<Interfaces.ILogger>()))
);

Result of this:

  • IAdUtilities is registered to AdUtilities with 2 interceptors, and is resolvable
  • AdUtilitiesStubbed is not changed but is not resolvable anymore:

The type X.Business.Utilities.ADUtilitiesStubbed is not interceptable.
Parameter name: interceptedType


Exception occurred while:

·resolving type: 'ADUtilitiesStubbed'

This is probably caused by the fact that interception is registered on the FromType (here IAdUtilities).

3. For unit testing, the standard registrations should be overwritten by stubbed classes
container.RegisterType<Business.Interfaces.Utilities.IADUtilities, Business.Utilities.ADUtilitiesStubbed>(createLifetimeManager());

Result of this:

  • IAdUtilities is registered to AdUtilitiesStubbed, resolving fails:

The type X.Business.Utilities.ADUtilitiesStubbed is not interceptable.
Parameter name: interceptedType


Exception occurred while:

·resolving type: 'ADUtilitiesStubbed'
•resolving type: 'IADUtilities' mapped to 'ADUtilitiesStubbed'

It seems like the resolve happens in 2 steps:

  • IAdUtilities -> AdUtilitiesStubbed
  • AdUtilitiesStubbed -> AdUtilitiesStubbed (which fails to resolve)

Why does resolving the Stubbed class fail? This is not as expected (in old unity versions this works correct).
Can this be fixed please?

fyi: When reregistereing AdUtilitiesStubbed as last, resolve succeeds:
container.RegisterType<Business.Utilities.ADUtilitiesStubbed, Business.Utilities.ADUtilitiesStubbed>(createLifetimeManager());

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.