Skip to content
This repository was archived by the owner on Nov 2, 2018. It is now read-only.
This repository was archived by the owner on Nov 2, 2018. It is now read-only.

Mixing open generic and closed types for IEnumerable<T> #500

Closed
@Antaris

Description

@Antaris

Hi Team,

I'm having trouble getting a particular scenario to work. I want to resolve all instances of a closed generic type, where the registrations may contain both concrete and open registrations:

public interface IHandler<TRequest, TResponse>
{
    TResponse Handle(TRequest request);
}

public class OpenHandler<TRequest, TResponse> : IHandler<TRequest, TResponse>
{
    public TResponse Handle(TRequest request)
    {
        return default(TResponse);
    }
}

public class ClosedHandler : IHandler<string, int>
{
    public int Handle(string request)
    {
        return int.Parse(request);
    }
}

When I try and resolve all instances of IHandler<string, int>, only the concrete implementation is returned:

var services = new ServiceCollection();
services.AddTransient<IHandler<string, int>, ClosedHandler>();
services.AddTransient(typeof(IHandler<,>), typeof(OpenHandler<,>));

var provider = services.BuildServiceProvider();
var handlers = provider.GetServices<IHandler<string, int>>().ToArray(); // Only 1 item  - ClosedHandler

I thought I'd test other containers to see what they do:

class Program
{
    static void Main(string[] args)
    {
        #region Stock
        {
            var services = new ServiceCollection();
            services.AddTransient<IHandler<string, int>, ClosedHandler>();
            services.AddTransient(typeof(IHandler<,>), typeof(OpenHandler<,>));

            var provider = services.BuildServiceProvider();
            var handlers = provider.GetServices<IHandler<string, int>>().ToArray(); // Returns 1
        }
        #endregion

        #region StructureMap
        {
            var container = new StructureMap.Container(c =>
            {
                c.For<IHandler<string, int>>().Use<ClosedHandler>();
                c.For(typeof(IHandler<,>)).Use(typeof(OpenHandler<,>));
            });

            var handlers = container.GetAllInstances<IHandler<string, int>>().ToArray(); // Returns 1
        }
        #endregion

        #region Autofac
        {
            var builder = new Autofac.ContainerBuilder();
            builder.RegisterType<ClosedHandler>().As<IHandler<string, int>>().InstancePerLifetimeScope();
            builder.RegisterGeneric(typeof(OpenHandler<,>)).As(typeof(IHandler<,>)).InstancePerLifetimeScope();

            var container = builder.Build();

            var handlers = container.Resolve<IEnumerable<IHandler<string, int>>>().ToArray(); // Returns 2
        }
        #endregion

        #region Ninject
        {
            var kernel = new Ninject.StandardKernel();
            kernel.Bind<IHandler<string, int>>().To<ClosedHandler>();
            kernel.Bind(typeof(IHandler<,>)).To(typeof(OpenHandler<,>));

            var handlers = kernel.GetAll<IHandler<string, int>>().ToArray(); // Returns 2
        }
        #endregion

        #region SimpleInjector
        {
            var container = new SimpleInjector.Container();
            container.RegisterCollection(typeof(IHandler<,>), new[]
            {
                typeof(ClosedHandler),
                typeof(OpenHandler<,>)
            });

            var handlers = container.GetAllInstances<IHandler<string, int>>().ToArray(); // Returns 2
        }
        #endregion
    }
}

Here is my project file:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp1.1</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Autofac" Version="4.4.0" />
    <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="1.1.0" />
    <PackageReference Include="Ninject" Version="4.0.0-beta-0134" />
    <PackageReference Include="SimpleInjector" Version="4.0.0-beta1" />
    <PackageReference Include="StructureMap" Version="4.4.3" />
  </ItemGroup>

</Project>

In all but Microsoft.Extensions.DependencyInjection and StructureMap, all other contains return both instances.

What would your recommendation be to achieve this? Is this functionality missing?

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions