Search code examples
genericsdependency-injectionunity-containerioc-containerservice-locator

How to Resolve Generic Class With Unity Container Resolved Generic Interface as Constructor Parameter


I can't figure out how to register a generic abstract class that has a injected generic interface with my Unity (2.0) container. Here's the interface and classes:

public interface IReportFactory<in TEntity, out TReport>
{ ... }

public class ExperienceReportBaseFactory :
    IReportFactory<Experience, ExperienceReportBase>
{ ... }

public abstract class ReportProvider<TEntity, TReport>
{
    protected ReportProvider(
        IReportFactory<TEntity, TReport> reportFactory)
    { ... }
    ...
}

public class ExperienceDetailsReportProvider
    : ReportProvider<Experience, ExperienceReportBase>
{
    public ExperienceDetailsReportProvider(
        IReportFactory<Experience, ExperienceReportBase>
        reportFactory)
        : base(reportFactory) { ... }
    ...
}

Here's the registration code:

Container.RegisterInstance(typeof(IReportFactory<Experience, ExperienceReportBase>), new ExperienceReportBaseFactory())
         .RegisterType(typeof(ReportProvider<Experience, ExperienceReportBase>),
             typeof (ExperienceDetailsReportProvider),
             new InjectionConstructor(Container.Resolve(typeof (IReportFactory<Experience, ExperienceReportBase>))));

Here's my provider factory that's tries to resolve the ReportProvider<,>:

public class ReportProviderFactory<TEntity> : IReportProviderFactory<TEntity>
{
    private readonly IServiceLocator _serviceLocator;

    public ReportProviderFactory(IServiceLocator serviceLocator)
    {
        _serviceLocator = serviceLocator;
    }

    public ReportProvider<TEntity, TReport> Create<TReport>()
        where TReport : ActiveReport
    {
        var reportProvider =
            _serviceLocator.GetService(typeof (ReportProvider<TEntity, TReport>)) as
                ReportProvider<TEntity, TReport>;
        return reportProvider;
    }
}

I get, "The type ReportProvider`2 cannot be constructed. You must configure the container to supply this value," on the _serviceLocator.GetService(...) line.


Solution

  • I'm an idiot... The client calling ReportProviderFactory<Experience>.Create<>() is specifying a different type for TReport:

    var reportProvider = _reportProviderFactory<Experience>.Create<ExperienceDetailsReport>();
    

    This means I have the wrong type for TReport on ExperienceReportBaseFactory and ExperienceDetailsReportProvider because the client that is using . It should be:

    public class ExperienceReportBaseFactory : IReportFactory<Experience, ExperienceDetailsReport> { ... }
    
    public class ExperienceDetailsReportProvider : ReportProvider<Experience, ExperienceDetailsReport>
    {
        public ExperienceDetailsReportProvider(IReportFactory<Experience, ExperienceDetailsReport> reportFactory) : base(reportFactory) { ... }
        ...
    }