问题
Given the following open generic deocrator chain using SimpleInjector:
container.RegisterManyForOpenGeneric(typeof(IHandleQuery<,>), assemblies);
container.RegisterDecorator(
typeof(IHandleQuery<,>),
typeof(ValidateQueryDecorator<,>)
);
container.RegisterSingleDecorator(
typeof(IHandleQuery<,>),
typeof(QueryLifetimeScopeDecorator<,>)
);
container.RegisterSingleDecorator(
typeof(IHandleQuery<,>),
typeof(QueryNotNullDecorator<,>)
);
With SimpleInjector 2.4.0, I was able to unit test this to assert the decoration chain using the following code:
[Fact]
public void RegistersIHandleQuery_UsingOpenGenerics_WithDecorationChain()
{
var instance = Container
.GetInstance<IHandleQuery<FakeQueryWithoutValidator, string>>();
InstanceProducer registration = Container.GetRegistration(
typeof(IHandleQuery<FakeQueryWithoutValidator, string>));
instance.ShouldNotBeNull();
registration.Registration.ImplementationType
.ShouldEqual(typeof(HandleFakeQueryWithoutValidator));
registration.Registration.Lifestyle.ShouldEqual(Lifestyle.Transient);
var decoratorChain = registration.GetRelationships()
.Select(x => new
{
x.ImplementationType,
x.Lifestyle,
})
.Reverse().Distinct().ToArray();
decoratorChain.Length.ShouldEqual(3);
decoratorChain[0].ImplementationType.ShouldEqual(
typeof(QueryNotNullDecorator<FakeQueryWithoutValidator, string>));
decoratorChain[0].Lifestyle.ShouldEqual(Lifestyle.Singleton);
decoratorChain[1].ImplementationType.ShouldEqual(
typeof(QueryLifetimeScopeDecorator<FakeQueryWithoutValidator, string>));
decoratorChain[1].Lifestyle.ShouldEqual(Lifestyle.Singleton);
decoratorChain[2].ImplementationType.ShouldEqual(
typeof(ValidateQueryDecorator<FakeQueryWithoutValidator, string>));
decoratorChain[2].Lifestyle.ShouldEqual(Lifestyle.Transient);
}
After updating to SimpleInjector 2.6.1, this unit test fails. It seems that InstanceProducer.Registration.ImplementationType
now returns the first decoration handler rather than the decorated handler (meaning, it returns typeof(QueryNotNullDecorator<HandleFakeQueryWithoutValidator,string>)
instead of typeof(HandleFakeQueryWithoutValidator)
.
Also, InstanceProducer.GetRelationships()
no longer returns all of the decorators in the chain. it also only returns the first decorator.
Is this a bug and, if not, how can we unit test open generic decorator chains using SimpleInjector 2.6.1+?
回答1:
The detail available for the dependency graph has been greatly improved in 2.6. You can achieve the same thing with this code:
[Fact]
public void RegistersIHandleQuery_UsingOpenGenerics_WithDecorationChain()
{
var container = this.ContainerFactory();
var instance = container
.GetInstance<IHandleQuery<FakeQueryWithoutValidator, string>>();
var registration = (
from currentRegistration in container.GetCurrentRegistrations()
where currentRegistration.ServiceType ==
typeof(IHandleQuery<FakeQueryWithoutValidator, string>)
select currentRegistration.Registration)
.Single();
Assert.Equal(
typeof(QueryNotNullDecorator<FakeQueryWithoutValidator, string>),
registration.ImplementationType);
Assert.Equal(Lifestyle.Singleton, registration.Lifestyle);
registration = registration.GetRelationships().Single().Dependency.Registration;
Assert.Equal(
typeof(QueryLifetimeScopeDecorator<FakeQueryWithoutValidator, string>),
registration.ImplementationType);
Assert.Equal(Lifestyle.Singleton, registration.Lifestyle);
registration = registration.GetRelationships().Single().Dependency.Registration;
Assert.Equal(
typeof(ValidateQueryDecorator<FakeQueryWithoutValidator, string>),
registration.ImplementationType);
Assert.Equal(Lifestyle.Transient, registration.Lifestyle);
}
You can find more information here
Please note: I think you have a captive dependency - you have a transient handler inside of a singleton decorator ...
[Fact]
public void Container_Always_ContainsNoDiagnosticWarnings()
{
var container = this.ContainerFactory();
container.Verify();
var results = Analyzer.Analyze(container);
Assert.False(results.Any());
}
回答2:
Qujck is right. We greatly improved the way registrations and KnownDependency graphs are built, especially to improve diagnostics and to make it easier for users to query the registrations. So this is not a bug; this is a breaking change. We however didn't expect anyone to be affected by this and that's why we made the change in a minor release. I'm sorry you had to stumble upon this change, but at least it's just test code that breaks.
In previous versions the graph of KnownDependency objects was flattened when decorators where added. This made querying and visualising the object graph hard. In v2.6, from perspective of the diagnostic API, it is as if a decorator is a 'real' registration. This means that the InstanceProducer and Registration objects now show the real type that is returned and its lifestyle.
Much clearer, but a breaking change in the diagnostic API.
来源:https://stackoverflow.com/questions/27668782/how-to-unit-test-open-generic-decorator-chains-in-simpleinjector-2-6-1