In an ASP.NET Core 1.1.2 web project, which depends on Microsoft.Extensions.DependencyInjection 1.1.1, I'm trying to register a generic FluentValidation validator both with its implementation and with its interface. At runtime, during host building, I got following exception:
An unhandled exception of type 'System.ArgumentException' occurred in Microsoft.Extensions.DependencyInjection.dll
Cannot instantiate implementation type 'MyProj.Shared.Api.WithUserCommandValidator`1[T]' for service type 'FluentValidation.IValidator`1[MyProj.Shared.Model.WithUser`1[T]]'.
at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceTable..ctor(IEnumerable`1 descriptors)
at Microsoft.Extensions.DependencyInjection.ServiceProvider..ctor(IEnumerable`1 serviceDescriptors, Boolean validateScopes)
at Microsoft.Extensions.DependencyInjection.ServiceCollectionContainerBuilderExtensions.BuildServiceProvider(IServiceCollection services)
at Microsoft.AspNetCore.Hosting.Internal.WebHost.BuildApplication()
at Microsoft.AspNetCore.Hosting.WebHostBuilder.Build()
at MyProj.Program.Main(String[] args) in X:\MySolution\src\MyProj\Program.cs:line 31
This is the (generic, non-abstract) validator class:
public class WithUserCommandValidator<TCommand> : AbstractValidator<WithUser<TCommand>>
where TCommand : ICommand
{
public WithUserCommandValidator(IValidator<TCommand> commandValidator)
{
RuleFor(cmd => cmd).NotNull();
RuleFor(cmd => cmd.UserId).NotEqual(Guid.Empty);
RuleFor(cmd => cmd.Content).NotNull()
.SetValidator(commandValidator);
}
}
(not sure is important to show WithUser<T> class, it just holds a Guid UserId and a T Content).
Registration happens like so:
someInterface = typeof(FluentValidation.IValidator<WithUser<>>);
someImplementation = typeof(Shared.Api.WithUserCommandValidator<>);
// this is for top-level command validators, injected by interface
services.AddScoped(someInterface, someImplementation);
// this is for nested validators, injected by implementation
services.AddScoped(someImplementation);
and at runtime those variables hold:
+ someInterface {FluentValidation.IValidator`1[MyProj.Shared.Model.WithUser`1[TCommand]]} System.Type {System.RuntimeType}
+ someImplementation {MyProj.Shared.Api.WithUserCommandValidator`1[TCommand]} System.Type {System.RuntimeType}
which seems correct.
I've looked around for similar problems and questions on SO as well, but they're not really related, as they were about generic implementation, while this is a concrete one.
I've stepped through DependencyInjection module code, and exception is thrown here. For some reason, serviceTypeInfo.IsGenericTypeDefinition returns false (shouldn't that be true, as the interface type is generic?) and so the else-branch is taken. There, instead, the implementation type return true to implementationTypeInfo.IsGenericTypeDefinition, so there you have the exception.
Runtime values are:
+ serviceTypeInfo {FluentValidation.IValidator`1[MyProj.Shared.Model.WithUser`1[TCommand]]} System.Reflection.TypeInfo {System.RuntimeType}
+ implementationTypeInfo {MyProj.Shared.Api.WithUserCommandValidator`1[TCommand]} System.Reflection.TypeInfo {System.RuntimeType}
While fiddling, I tried registering only the implementation, without also registering interface+implementation, and that exception goes away. But I'd like to inject the interface, if possible, not the implementation.
What am I doing wrong? TA