unitycontainer / interception

Unity.Interception package
Apache License 2.0
22 stars 17 forks source link

Incorrect lifetime management for CallHandlers #18

Open dmalanij opened 5 years ago

dmalanij commented 5 years ago

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:

But started failing on the next release:

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

prashanthmp7 commented 4 years ago

Hi,

I am facing the exact issue. We are using the latest version as of today that is 5.11.1.