Runtime: `ServiceDescriptor` for generic types does not work.

Created on 17 Sep 2020  路  10Comments  路  Source: dotnet/runtime

I am registering all of my services to ServiceCollection using reflection and that's why I am using ServiceDescriptor. Although it is working fine for Concrete type but not working for Generic type.

For example:

Generic Service Type:

public interface IRepository<T> 
    where T: class
{
    string Welcome(string name);
}

Generic Implementaion Type:

public class EfCoreRepository<T> : IRepository<T>
    where T : class
{
    public string Welcome(string name)
    {
        return $"Welcome! {name}";
    }
}

Adding to ServiceCollection as follows:

Type serviceType = Getting Type of IRepository<T> using reflection.
Type implementationType = Getting Type of  EfCoreRepository<T> using reflection.
serviceCollection.Add(new ServiceDescriptor(serviceType, implementationType , ServiceLifetime.Scoped));

Throwing the following exception:

ArgumentException: Cannot instantiate implementation type 'Demo.Implementations.EfCoreRepository1[T]' for service type 'Demo.GenericServices.IRepository1[T]'.

If I remove the generics it works fine.

Please help me in this regard. Thank you.

untriaged

Most helpful comment

The GetInterfaces() method for a type with IsGenericTypeDefinition == true returns the list of types with IsGenericTypeDefinition == false, so the typeof(IType<T>) != typeof(Type<T>).GetInterfaces()[0]

Accordingly, you need to normalize the interface type by calling the serviceType.GetGenericTypeDefinition() here https://github.com/TanvirArjel/AspNetCore.ServiceRegistration.Dynamic/blob/master/src/AspNetCore.ServiceRegistration.Dynamic/ServiceCollectionExtensions.cs#L73 like this, for example

var isGenericTypeDefinition = implementation.IsGenericType && implementation.IsGenericTypeDefinition;
var service = isGenericTypeDefinition
    && serviceType.IsGenericType
    && serviceType.IsGenericTypeDefinition == false
    && serviceType.ContainsGenericParameters
        ? serviceType.GetGenericTypeDefinition()
        : serviceType;
serviceCollection.Add(new ServiceDescriptor(service, implementation, lifetime));

image

All 10 comments

@Tratcher should this be transferred to the dotnet/runtime repo?

I couldn't figure out the best area label to add to this issue. If you have write-permissions please help me learn by adding exactly one area label.

What types did you pass to ServiceDescriptor? Specifically, the real code for this:

C# Type serviceType = Getting Type of IRepository<T> using reflection. Type implementationType = Getting Type of EfCoreRepository<T> using reflection. serviceCollection.Add(new ServiceDescriptor(serviceType, implementationType , ServiceLifetime.Scoped));

You need to either pass a closed generic or an open generic type

@davidfowl

What types did you pass to ServiceDescriptor?

The service type is typeof(IRepository<T>) and the implementation type is typeof(EfCoreRepository<T>) which is getting using reflection at runtime.

Please look at the error message:

ArgumentException: Cannot instantiate implementation type 'Demo.Implementations.EfCoreRepository1[T]' for service type 'Demo.GenericServices.IRepository1[T]'.

What is the T? Can you show all of the code? It is it in a generic class, or a generic method?

@davidfowl Please read the original question thoroughly. I have explained everything clearly with real examples where it is also said that what is T. By the way, 'T' is the generic type with class constraint.

@davidfowl Please read the original question thoroughly. I have explained everything clearly with real examples where it is also said that what is T. By the way, 'T' is the generic type with class constraint.

Sorta, you're missing some code. I'd like something runnable.

If that isn't possible I can guess what you want:

C# serviceCollection.Add(new ServiceDescriptor(typeof(IRepository<>), typeof(EfCoreRepository<>) , ServiceLifetime.Scoped));

The GetInterfaces() method for a type with IsGenericTypeDefinition == true returns the list of types with IsGenericTypeDefinition == false, so the typeof(IType<T>) != typeof(Type<T>).GetInterfaces()[0]

Accordingly, you need to normalize the interface type by calling the serviceType.GetGenericTypeDefinition() here https://github.com/TanvirArjel/AspNetCore.ServiceRegistration.Dynamic/blob/master/src/AspNetCore.ServiceRegistration.Dynamic/ServiceCollectionExtensions.cs#L73 like this, for example

var isGenericTypeDefinition = implementation.IsGenericType && implementation.IsGenericTypeDefinition;
var service = isGenericTypeDefinition
    && serviceType.IsGenericType
    && serviceType.IsGenericTypeDefinition == false
    && serviceType.ContainsGenericParameters
        ? serviceType.GetGenericTypeDefinition()
        : serviceType;
serviceCollection.Add(new ServiceDescriptor(service, implementation, lifetime));

image

@rameel Thank you so much from the bottom of my heart for your contribution. My issue has been solved.

Was this page helpful?
0 / 5 - 0 ratings