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