Trying to resolve an interface implementation based on generic type passed at runtime in autofac

StackOverflow https://stackoverflow.com/questions/23295594

  •  09-07-2023
  •  | 
  •  

Question

I want to resolve a specific interface implementation from autofac based on the type of an input parameter passed at runtime.

In my code I have different implementations of IMyTask<>:

MySillyTask : IMyTask<SillyData>

MyAwesomeTask : IMyTask<AwesomeData>

I register the implementations of MyTask<> like this:

builder.RegisterAssemblyTypes(typeof (IMyTask<>).Assembly)
       .AsClosedTypesOf(typeof (IMyTask<>));

I want use a class with an injected Autofac IComponentContext to resolve the data based on the type of Data passed:

public IMyTask<T> GetTask<T>(T input) where T : IData
{
  var myTask = _componentContext.Resolve<IMyTask<T>>(); // fails
  return myTask;
}

I get The requested service has not been registered error.

If I try to resolve it for a hardcoded IData type it works:

var myTask = _componentContext.Resolve<IMyTask<SillyData>>(); // works! MySillyTask resolved

I think it is because at runtime T is IData instead of the specific IData implementation I pass but is there a way to change the code to get the functionality I require?

EDIT: As requested the complete error message:

The requested service 'Whatever.Tasks.IMyTask`1[[Whatever.Inputs.IData, Whatever, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]' has not been registered. To avoid this exception, either register a component to provide the service, check for service registration using IsRegistered(), or use the ResolveOptional() method to resolve an optional dependency.

How I am calling GetTask:

 IData myData = input.MyData;
 taskResolver.GetTask(myData);

I think I realise it is because T is IData instead of an implementation that causes my code to fail. Though I have changed my code to use named instances and returning a non-generic 'IMyTask' I am still interested if I can use autofac to resolve it the way I originally intended.

Was it helpful?

Solution

I don't know much about AutFac but I suppose component.Resolve(...) has an overload which accepts a type.

I sucessfully do something like this with Ninject

    public IMyTask GetTask<T>(T input) where T : IData
    {
        var taskType = typeof(IMyTask<>);
        var inputType = input.GetType();

        var genericType = taskType.MakeGenericType(inputType);
        return (IMyTask)_componentContext.Resolve(genericType);
    }

OTHER TIPS

You didn't supply the code that calls this, but I suspect the problem is that the type T is based on what you're calling code knows, not the actual type of input. If the calling code called it as GetTask<SillyData>(obj); I suspect it'd work but I assume your obj is a IData that just happens to be a SillyData.

If you need to call a generic method without knowing the Type in advance you'll have to use Reflection. You could use input.GetType() to get the real underlying type then use MakeGenericType to make the ITask<SillyData> then MakeGenericMethod to invoke Resolve.

Here's another stack overflow question that'll show you how to call a generic by passing a Type argument:

How do I use reflection to call a generic method?

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top