Search code examples
c#dependency-injectiongeneric-interface

Inject an IEnumerable of generic interface in Unity Dependency Injection c#


I am working on a web API 2 application and using Unity dependency injection.

I have multiple types of filters: Name, Brand, Types ...

I want to make an interface called: IFilterService and force every other class to implement it and then I call an IEnumerable of this interface and inject it with the correct type.

the interface is:

public interface IFilterService<T>
{
    bool CanHandle(FilterType type);

    Task<ServiceResult<T>> FilterAsync(T entity);
}

and the classes are like:

public class NameService : IFilterService<Name>
{
    public bool CanHandle(FacetType type)
    {
        return type == FacetType.Name;
    }

    public async Task<ServiceResult<Name>> FilterAsync(Name entity)
    {
      // Code
    }
}

the controller is like:

public class FilterController
{
    private readonly IEnumerable<IFilterService> filters;

    public MediaController(IEnumerable<IFilterService> filters)
    {
        this.filters = filters;
    }

     public async Task<HttpResponseMessage> FilterAsync(FilterType type, Name entity)
     {
        foreach(var filter in this.filters.Where(x => x.CanHandle(type)))
        {
            filter.FilterAsync(entity); 
        }
        ....
    }
}

everything is working correctly: the only problem is with registering the interface and the classes in the Unity Dependency Injection.

container.RegisterType<IEnumerable<IFilterService>, IFilterService[] >(
                new ContainerControlledLifetimeManager());
container.RegisterType<IFilterService, NameService>("Name Service",
                new ContainerControlledLifetimeManager());

I am receiving this error:

Error CS0305 Using the generic type 'IFilterService' requires 1 type arguments

The same code I've tried it but with a non-generic interface and works fine.

how to fix the error? and a little explanation could be very useful. thank you.


Solution

  • You have two options, the first is to register the specific filter type

    container.RegisterType<IEnumerable<IFilterService<Name>>, IFilterService<Name>[] >(
                    new ContainerControlledLifetimeManager());
    container.RegisterType<IFilterService<Name>, NameService>("Name Service",
                    new ContainerControlledLifetimeManager());
    

    Used like

    public class FilterController
    {
        private readonly IEnumerable<IFilterService<Name>> filters;
    
        public MediaController(IEnumerable<IFilterService<Name>> filters)
        {
            this.filters = filters;
        }
    
         public async Task<HttpResponseMessage> FilterAsync(FilterType type, Name entity)
         {
            foreach(var filter in this.filters.Where(x => x.CanHandle(type)))
            {
                filter.FilterAsync(entity); 
            }
            ....
        }
    }
    

    The second option is to make your interface non generic, but you keep the function generic

    public interface IFilterService
    {
        bool CanHandle(FilterType type);
    
        Task<ServiceResult<T>> FilterAsync<T>(T entity);
    }
    
    public class NameService : IFilterService
    {
        public bool CanHandle(FacetType type)
        {
            return type == FacetType.Name;
        }
    
        public Task<ServiceResult<T>> FilterAsync<T>(T entity)
        {
          if(!entity is Name)
              throw new NotSupportedException("The parameter 'entity' is not assignable to the type 'Name'");
    
            return FilterAsyncInternal((Name)entity);
        }
    
        private async Task<ServiceResult<Name>> FilterAsyncInternal(Name entity)
        {
            //code
        }
    }