Skip to content
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

Resolving decorated service behavior changes when adding Named registration #1047

Closed
mchandschuh opened this issue Nov 7, 2019 · 3 comments
Labels

Comments

@mchandschuh
Copy link

Version Information

$ autofac --version
    4.9.4

$ dotnet --version
    3.0.100

$ cat global.json
{
  "sdk":{
    "version": "2.2.103"
  }
}

Description

While working with decorators, I'm observing an unexpected change in behavior by adding a Named<TService>(serviceName). It appears that the addition of the named registration is causing the decorators to be applied twice, resuling in D2(D2(D1(D1(root)))) I suspect I may be misunderstanding the interplay between the various registration types.

This seems similar to #965 which was fixed in 4.9.2 per comments in the issue.

Questions

  • How does adding multiple registrations for the same service impact service resolution?
  • If this is not a bug, then decorator application appears to be more complex than I anticipated. Is there anything useful that would help me in understanding why autofac behaves the way it does in the below unit tests?

Reproduction

private interface IContract
{
    IContract Inner { get; }
}
private class Root1 : IContract
{
    public IContract Inner => null;
}
private class Decorator1 : IContract
{
    public IContract Inner { get; }
    public Decorator1(IContract inner) { Inner = inner; }
}
private class Decorator2 : IContract
{
    public IContract Inner { get; }
    public Decorator2(IContract inner) { Inner = inner; }
}


[Test]
public void RegisterType_As_AndRegisterDecorators_AppliesDecoratorsInOrder_ResolvedByType()
{
    // this test passes and produces the following output:
    // Decorator2
    // Decorator1
    // Root1

    var builder = new ContainerBuilder();
    builder.RegisterType<Root1>().As<IContract>();
    builder.RegisterDecorator(typeof(Decorator1), typeof(IContract));
    builder.RegisterDecorator(typeof(Decorator2), typeof(IContract));

    var container = builder.Build();
    var component = container.Resolve<IContract>();

    WriteInners(component);
    Assert.IsInstanceOf<Decorator2>(component);
    Assert.IsInstanceOf<Decorator1>(component.Inner);
    Assert.IsInstanceOf<Root1>(component.Inner.Inner);
}

[Test]
public void RegisterType_Named_As_AndRegisterDecorators_AppliesDecoratorsInOrder_ResolvedByType()
{
    // this test fails and produces the following output:
    // Decorator2
    // Decorator2
    // Decorator1
    // Decorator1
    // Root1

    var builder = new ContainerBuilder();
    builder.RegisterType<Root1>().Named<IContract>("root1").As<IContract>();
    builder.RegisterDecorator(typeof(Decorator1), typeof(IContract));
    builder.RegisterDecorator(typeof(Decorator2), typeof(IContract));

    var container = builder.Build();
    var component = container.Resolve<IContract>();

    WriteInners(component);
    Assert.IsInstanceOf<Decorator2>(component);
    Assert.IsInstanceOf<Decorator1>(component.Inner);
    Assert.IsInstanceOf<Root1>(component.Inner.Inner);
}

[Test]
public void RegisterType_Named_AndRegisterDecorators_AppliesDecoratorsInOrder_ResolvedByName()
{
    // this test passes and produces the following output:
    // Decorator2
    // Decorator1
    // Root1

    var builder = new ContainerBuilder();
    builder.RegisterType<Root1>().Named<IContract>("root1");
    builder.RegisterDecorator(typeof(Decorator1), typeof(IContract));
    builder.RegisterDecorator(typeof(Decorator2), typeof(IContract));

    var container = builder.Build();
    var component = container.ResolveNamed<IContract>("root1");

    WriteInners(component);
    Assert.IsInstanceOf<Decorator2>(component);
    Assert.IsInstanceOf<Decorator1>(component.Inner);
    Assert.IsInstanceOf<Root1>(component.Inner.Inner);
}

[Test]
public void RegisterType_Named_As_AndRegisterDecorators_AppliesDecoratorsInOrder_ResolvedByName()
{
    // this test fails and produces the following output:
    // Decorator2
    // Decorator2
    // Decorator1
    // Decorator1
    // Root1

    var builder = new ContainerBuilder();
    builder.RegisterType<Root1>().Named<IContract>("root1").As<IContract>();
    builder.RegisterDecorator(typeof(Decorator1), typeof(IContract));
    builder.RegisterDecorator(typeof(Decorator2), typeof(IContract));

    var container = builder.Build();
    var component = container.ResolveNamed<IContract>("root1");

    WriteInners(component);
    Assert.IsInstanceOf<Decorator2>(component);
    Assert.IsInstanceOf<Decorator1>(component.Inner);
    Assert.IsInstanceOf<Root1>(component.Inner.Inner);
}

[Test]
public void RegisterType_Keyed_As_AndRegisterDecorators_AppliesDecoratorsInOrder_ResolvedByType()
{
    // this test fails and produces the following output:
    // Decorator2
    // Decorator2
    // Decorator1
    // Decorator1
    // Root1

    var builder = new ContainerBuilder();
    builder.RegisterType<Root1>().Keyed<IContract>("root1").As<IContract>();
    builder.RegisterDecorator(typeof(Decorator1), typeof(IContract));
    builder.RegisterDecorator(typeof(Decorator2), typeof(IContract));

    var container = builder.Build();
    var component = container.Resolve<IContract>();

    WriteInners(component);
    Assert.IsInstanceOf<Decorator2>(component);
    Assert.IsInstanceOf<Decorator1>(component.Inner);
    Assert.IsInstanceOf<Root1>(component.Inner.Inner);
}

[Test]
public void RegisterType_Named_Keyed_As_AndRegisterDecorators_AppliesDecoratorsInOrder_ResolvedByType()
{
    // this test fails and produces the following output:
    // Decorator2
    // Decorator2
    // Decorator1
    // Decorator1
    // Root1

    var builder = new ContainerBuilder();
    builder.RegisterType<Root1>().Named<IContract>("root1").Keyed<IContract>("root1").As<IContract>();
    builder.RegisterDecorator(typeof(Decorator1), typeof(IContract));
    builder.RegisterDecorator(typeof(Decorator2), typeof(IContract));

    var container = builder.Build();
    var component = container.Resolve<IContract>();

    WriteInners(component);
    Assert.IsInstanceOf<Decorator2>(component);
    Assert.IsInstanceOf<Decorator1>(component.Inner);
    Assert.IsInstanceOf<Root1>(component.Inner.Inner);
}

private void WriteInners(IContract component)
{
    var current = component;
    do
    {
        Console.WriteLine(current.GetType().Name);
        current = current.Inner;
    }
    while (current != null);
@tillig
Copy link
Member

tillig commented Nov 26, 2019

@alexmg might want to look at this since it overlaps a bit with the new decorator syntax and possibly work that's going on in there.

@tillig tillig added the bug label Nov 26, 2019
@RaymondHuy
Copy link
Member

Hi @tillig @mchandschuh I have run your reproduction on develop branch and I see that all test cases are passed. Is there anything wrong ?

@tillig
Copy link
Member

tillig commented Dec 3, 2019

Agreed. Testing against Autofac 5.0.0-develop-00640 from MyGet shows that this has been resolved for the 5.0.0 release.

@tillig tillig closed this as completed Dec 3, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants