Skip to content

Allow services to decorate each other by preventing recursive resolution #2350

@aspnet-hello

Description

@aspnet-hello

From @tuespetre on Friday, December 18, 2015 8:00:37 AM

User Story / Use Case Scenario

As a developer, I want to extend a previously registered service by wrapping it with another service that exposes the same interface.

See: aspnet/Mvc#3739

Example interface and implementations

public interface IMyService
{
    void DoSomething();
}

public partial class DefaultMyService
{
    public partial void DoSomething();
}

public partial class DecoratorForMyService
{
    private IMyService decorated;

    public DecoratorForMyService(IMyService decorated)
    {
        this.decorated = decorated;
    }

    public partial void DoSomething();
}

Examples of ways to set up the service collection

Using constructor resolution

var collection = new ServiceCollection();
collection.AddTransient<IMyService, DefaultMyService>();
collection.AddTransient<IMyService, DecoratorForMyService>();
var provider = collection.BuildServiceProvider();
var service = collection.GetService<IMyService>(); // Returns the constructed DecoratorForMyService

Using implementation factories

var collection = new ServiceCollection();
collection.AddTransient<IMyService, DefaultMyService>();
collection.AddTransient<IMyService>(_provider =>
{
    // Returns the previously registered IMyService.
    // Calling _provider.GetService<IEnumerable<IMyService>>() here
    // would return a collection containing all registered IMyServices
    // except for this one.
    var decorated = _provider.GetService<IMyService>(); 
    return new DecoratorForMyService(decorated);
});
var provider = collection.BuildServiceProvider();
var service = collection.GetService<IMyService>(); // Returns the constructed DecoratorForMyService

Behavioral Considerations

Currently, the above two approaches result in 'Circular Dependency' exception and a StackOverflowException, respectively. An implementation that allowed for the above scenarios would effectively prevent the StackOverflowException entirely, and throw the 'Circular Dependency' exception only when there are no other previously registered services for the type that is being resolved result in circular dependency scenarios throwing the "Unable to resolve service..." kind of exception, which should in itself sufficiently convey the circularity for those cases.

Copied from original issue: aspnet/DependencyInjection#340

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions