New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Redundantly decorated service when using nested ContainerBuilder #965
Comments
Verified - I'm able to reproduce this. Unclear at this moment exactly what's causing the issue, but since we can repro it, we can troubleshoot it. If you happen to be also troubleshooting and figure it out, let us know what you find. |
Notes as I continue research here. The difference between starting a scope with and without a lambda registration... // without lambda
container.BeginLifetimeScope();
// with lambda
container.BeginLifetimeScope(b => { }); ... is that without the lambda the lifetime scope uses a Admittedly, this has been a source of struggle for a few different issues. The primary difference is that when the If I set a breakpoint at the point where decorators are being applied then I see two different behaviors. With a no-lambda lifetime scope (
With a lambda configured scope (
The second time through, the call stack is identical to the earlier/simpler stack that doesn't go through the Basically, the delegate inside the I'm not sure as yet the right way to stop that from happening, but at least I'm getting closer to figuring out the root cause. |
I added a failing unit test marked as skip for this issue. I think there are a couple of potential solutions here. One would be to somehow signal to either the Another option might be to improve the overall resolve/decoration mechanism to track the set of decorators and ensure the same decorator registration isn't applied twice to the same resolve op. I think this would potentially impact memory usage and performance since there'd be more to pass around on each resolve and the check would happen for each decorator being applied. It could be an expensive add-on to handle a case that's potentially not as frequent. One challenge with the latter option of tracking the applied decorators is that technically the two decorations are happening separate from each other. We do have a decoration context we use to track the series of applied decorator instances, but one operation is happening inside that I do see there's an exception in the I could definitely use some input from @alexmg on this one. I'm not sure if there's something he's already seen or thought of during the creation of this new decorator mechanism. |
I tried updating the |
I expanded some of the other tests to include a type check on the inner service and found the double decoration issue also applied to adapters like Because the issue is specific to the decorator feature I made the fix in the |
Is there a use case where the same decorator type might actually need to be applied twice? For example, a logging decorator that might include additional information for nested lifetime scopes or something. You might also want to add a test where the container applies two decorators - resolution in a child scope should avoid duplicating both of them. |
…applied more than once if intentional.
Excellent test suggestions @tillig. There is plenty of craziness one might choose to get up to. 😄 The tests below intentionally add a second decorator of the same type, one in the root lifetime scope, and the other only in a child lifetime scope. These and the open generic equivalents seem to be holding up. I was rather relieved to find that to be the case. [Fact]
public void DecoratorCanBeAppliedTwice()
{
var builder = new ContainerBuilder();
builder.RegisterType<ImplementorA>().As<IDecoratedService>();
builder.RegisterDecorator<DecoratorA, IDecoratedService>();
builder.RegisterDecorator<DecoratorA, IDecoratedService>();
var container = builder.Build();
var service = container.Resolve<IDecoratedService>();
Assert.IsType<DecoratorA>(service);
Assert.IsType<DecoratorA>(service.Decorated);
Assert.IsType<ImplementorA>(service.Decorated.Decorated);
}
[Fact]
public void DecoratorCanBeAppliedTwiceInChildLifetimeScope()
{
var builder = new ContainerBuilder();
builder.RegisterType<ImplementorA>().As<IDecoratedService>();
builder.RegisterDecorator<DecoratorA, IDecoratedService>();
var container = builder.Build();
var scope = container.BeginLifetimeScope(b => b.RegisterDecorator<DecoratorA, IDecoratedService>());
var scopeInstance = scope.Resolve<IDecoratedService>();
Assert.IsType<DecoratorA>(scopeInstance);
Assert.IsType<DecoratorA>(scopeInstance.Decorated);
Assert.IsType<ImplementorA>(scopeInstance.Decorated.Decorated);
var rootInstance = container.Resolve<IDecoratedService>();
Assert.IsType<DecoratorA>(rootInstance);
Assert.IsType<ImplementorA>(rootInstance.Decorated);
} |
I added one more unit test with intentionally duplicated decorators and the empty builder lambda being provided when creating the child lifetime scope. |
This fix is included in |
Autofac 4.9.1
When resolving an decorated service from a LifetimeScope with nested ContainerBuilder,
the return service is decorated twice.
Repro:
The text was updated successfully, but these errors were encountered: