Search code examples
c#dependency-injectionautofac

Autofac register generic instance with concrete type


I want to register a interface to a specific implementation. The interface and the class both are generic instances, here it goes:

This is the container registration:

builder.RegisterType<ParsedStatement>()
       .As<IParsedStatement>()
       .InstancePerDependency();
builder.RegisterType<NpoiParser<ParsedStatement>>()
       .As<IParser<IParsedStatement>>()
       .InstancePerDependency();

The interface for IParser:

 public interface IParser<T> where T : class

The implementation of NpoiParser

 public class NpoiParser<T> : BaseParser<T>, IParser<T> where T : class

ParsedStatement class already implements the interface IParsedStatement. Although when i start the application I get this error from the the build of the autofac container

System.ArgumentException: 'The type RF.Infrastructure.Parser.NpoiParser`1[RF.Domain.ValueObjects.ParsedStatement] is not assignable to service RF.Domain.Interfaces.Parser.IParser`1[[RF.Domain.Interfaces.ValueObjects.IParsedStatement, RF.Domain.Interfaces, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]].'

I could use a generic approach like this:

builder.RegisterGeneric(typeof(NpoiParser<>))
       .As(typeof(IParser<>))
       .InstancePerLifetimeScope();

It works but doesn't allow me to use IParser<ParsedStatement> on the services since the NpoiParser class needs a concrete class since it uses a library and in that library only classes are permitted otherwise returns me the error "Cannot create an instance of an interface.".

The stack trace for the error when using generic is the following one:

at System.RuntimeTypeHandle.CreateInstance(RuntimeType type, Boolean publicOnly, Boolean wrapExceptions, Boolean& canBeCached, RuntimeMethodHandleInternal& ctor, Boolean& hasNoDefaultCtor)    
at System.RuntimeType.CreateInstanceDefaultCtorSlow(Boolean publicOnly, Boolean wrapExceptions, Boolean fillCache)    
at System.RuntimeType.CreateInstanceDefaultCtor(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache, Boolean wrapExceptions)   
at System.Activator.CreateInstance(Type type, Boolean nonPublic, Boolean wrapExceptions)    
at Npoi.Mapper.Mapper.<Take>d__66`1.MoveNext()    
at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)   
at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)    
at RF.Infrastructure.Parser.NpoiParser`1.ParseFile(Stream fileToParse,  ITemplateDefinition info) in D:\Trabalho\RoyaltyFlush\royaltyflush.backend\RF.Infrastructure.Parser\NpoiParser.cs:line 52
at RF.Infrastructure.Parser.NpoiParser`1.ConvertStreamFileToObjectList(Stream fileToParse, ITemplateDefinition info) in D:\Trabalho\RoyaltyFlush\royaltyflush.backend\RF.Infrastructure.Parser\NpoiParser.cs:line 35

Any ideas on how i can register it on AutoFac with a specific class type?


Solution

  • Registering NpoiParser<ParsedStatement> as IParser<IParsedStatement> is like registering apple as orange.

    NpoiParser<ParsedStatement> does not implement IParser<IParsedStatement>, it only implements IParser<ParsedStatement>.

    // Compile error
    IParser<IParsedStatement> obj = new NpoiParser<ParsedStatement>();
    

    There are two ways you could fix this:

    1. Inject IParser<ParsedStatement> instead of IParser<IParsedStatement>.
    2. Change IParser<T> to covariant generic interface by using out keyword. So change IParser<T> to IParser<out T>. But that requires the interface is indeed an covariant interface.