Scrutor icon indicating copy to clipboard operation
Scrutor copied to clipboard

Decorated service called using different constructor than when not decorated

Open gambrose opened this issue 6 years ago • 1 comments

Found this issue when I was trying to decorate NewtonsoftJsonHubProtocol

To Reproduce

Given these classes

public interface IDecoratedService { }

public class DecoratedOptions
{
    public bool Option { get; set; }
}

public class DecoratedWithOptions : IDecoratedService
{
    public DecoratedWithOptions() : this(Options.Create(new DecoratedOptions()))
    { }

    public DecoratedWithOptions(IOptions<DecoratedOptions> options)
    {
        OptionalValue = options.Value.Option;
    }

    public bool OptionalValue { get; }
}

When setup using

services.AddSingleton<IDecoratedService, DecoratedWithOptions>();
services.Configure<DecoratedOptions>(options => options.Option = true);

DecoratedOptions is injected from container and OptionalValue set to true.

When decorator added is added the parameterless constructor is called and OptionalValue is false.

services.Decorate<IDecoratedService, Decorator>();

gambrose avatar Feb 20 '20 00:02 gambrose

I'm also running into this.

ActivatorUtilities.CreateInstance does have a concept of "better match" for constructors; however, that matching logic only compares the number of explicitly-provided arguments, not the number of constructor parameters, so it ends up taking the first one.

Meanwhile, the standard DI will consider constructors in decreasing order of number of parameters.

Not sure what the best solution is. Ideally, ActivatorUtilities would be brought into line with the standard DI. For this project, you might want to consider much more complex construction: essentially rewrite ActivatorUtilities.CreateInstance with your own implementation that chooses a constructor manually and builds a factory delegate.

StephenCleary avatar Jan 23 '21 01:01 StephenCleary