Skip to content

ServiceType/TService with SelfWithProxyFactory registers all implemented interfaces #312

@Cologler

Description

@Cologler

ServiceType / generic TService appears to be combined with RegistrationStrategy expansion, rather than limiting the registration to the specified service type.

The documentation/comment says:

ServiceType - "The type of the service. If not set, the Registration property used to determine what is registered."

I interpreted this as: when ServiceType or <TService> is specified, only that service type should be registered; otherwise Registration determines whether to register self, implemented interfaces, etc.

However, with SelfWithProxyFactory, Injectio also registers all implemented interfaces and self, even when a generic service type is explicitly specified.

Reproduction

public interface IPrimaryService { }

public interface IStartupInitializer { }

[RegisterSingleton<IPrimaryService>(
    Registration = RegistrationStrategy.SelfWithProxyFactory,
    Duplicate = DuplicateStrategy.Skip)]
[RegisterSingleton<IStartupInitializer>(
    Registration = RegistrationStrategy.SelfWithProxyFactory,
    Duplicate = DuplicateStrategy.Append)]
internal sealed class MyService : IPrimaryService, IStartupInitializer, IAsyncDisposable
{
    public ValueTask DisposeAsync() => ValueTask.CompletedTask;
}

Actual generated behavior

The first attribute generates registrations for:

  • IPrimaryService
  • IStartupInitializer
  • IAsyncDisposable
  • MyService

The second attribute also generates registrations for:

  • IStartupInitializer
  • IPrimaryService
  • IAsyncDisposable
  • MyService

Because the second attribute uses DuplicateStrategy.Append, it appends all of those registrations, not only IStartupInitializer.

This also causes unrelated implemented interfaces such as IAsyncDisposable to be registered as services.

Expected behavior

I expected the explicit generic service type to constrain the registration.

For example:

[RegisterSingleton<IPrimaryService>(
    Registration = RegistrationStrategy.SelfWithProxyFactory)]

would register something equivalent to:

services.TryAddSingleton<MyService>();
services.TryAddSingleton<IPrimaryService>(
    sp => sp.GetRequiredService<MyService>());

but would not also register every implemented interface.

Similarly:

[RegisterSingleton<IStartupInitializer>(
    Registration = RegistrationStrategy.SelfWithProxyFactory,
    Duplicate = DuplicateStrategy.Append)]

would append only IStartupInitializer, not IPrimaryService, IAsyncDisposable, and MyService.

Question

Is the current behavior intentional?

If it is intentional, the documentation/comment for ServiceType may be misleading because Registration is still used to expand additional service types even when ServiceType / TService is explicitly set.

If it is not intentional, this may be a generator bug in how explicit service types interact with RegistrationStrategy.SelfWithProxyFactory.

Version

Injectio version: 6.0.0

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions