Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

"Cannot instantiate implementation type" even when specifying generic type #2341

Closed
aspnet-hello opened this issue Jan 1, 2018 · 10 comments
Closed

Comments

@aspnet-hello
Copy link

From @thiagomajesk on Thursday, June 1, 2017 3:38:54 PM

Hello, I'm having trouble using the default container with @jbogard's MediatR.
I know that currently there are limitations, but I would appreciated if someone could bring any help.

I have the standard generic command:

public class Command<T> : IRequest<Command<T>> where T : class 
{ 
    public List<T> List { get; set; }
}

and its handler:

public class CommandHandler<T> : IRequestHandler<Command<T>, Command<T>> where T : class
{
    [...]
}

I was searching about it and saw that the user @dannyc02 managed to get this working with Structure Map by specifying the concrete types during registration (jbogard/MediatR#69). I've tried the same thing without success:

services.AddTransient(typeof(IRequestHandler<Command<string>, Command<string>>), typeof(CommandHandler<string>));

If its possible to register the concrete type like the example I know I could write a custom type scan to register everything to me, like this: https://gist.github.com/thiagomajesk/204ca7e602c3a1f1d5cc6f7b4ab42aa9. But right now I only get
Cannot instantiate implementation type.

Copied from original issue: aspnet/DependencyInjection#531

@aspnet-hello
Copy link
Author

From @pakrym on Thursday, June 1, 2017 4:26:09 PM

Can you provide a minimal repro please?

@aspnet-hello
Copy link
Author

From @thiagomajesk on Thursday, June 1, 2017 4:53:58 PM

@pakrym Of course, here it is: container_sample.zip

@aspnet-hello
Copy link
Author

From @pakrym on Monday, June 5, 2017 9:11:55 AM

Exception is by design, services.AddMediatR(); registers open generic service of second order (where open generic part is type argument itself)

{MediatR.IRequestHandler`2[ContainerSample.Handlers.Command`1[T],ContainerSample.Handlers.Command`1[T]]}

and this is not supported by DI container. Only first order open generic registrations are supported.

@aspnet-hello
Copy link
Author

From @thiagomajesk on Monday, June 5, 2017 1:47:43 PM

@pakrym I have to admit I don't quite understand why this scenario isn't supported though. It seems to me a common use case. Is there any recommendations on how to achieve similar behaviour (extending the default container or not) besides replacing it?

@aspnet-hello
Copy link
Author

From @pakrym on Monday, June 5, 2017 3:31:32 PM

@thiagomajesk it creates a lot of complexities in resolution rules, imagine following registrations:

IRequestHandler<ICommand<Action<>>, ICommand<>>
IRequestHandler<ICommand<>, ICommand<string>>

Which one would you use to resolve IRequestHandler<ICommand<Action<string>, ICommand<string>> and why?

The only way to make it work with AspNetCore.DependencyInjection is too define interface ICommandHandler<T>: IRequestHandler<Command<T>, Command<T>> but then you need to inject ICommandHandler<T> that might not be applicable to your case.

@aspnet-hello
Copy link
Author

From @thiagomajesk on Tuesday, June 6, 2017 9:26:48 AM

@pakrym I see your point now, in that example the container doesn't know how to act because it can't tell the difference between ICommand<T> and ICommand<T<U>> because the whole thing inside the first <> IS a generic type. So I'll assume that the default behaviour of the DI is not to cascade scan the generic types (tree?), hence the error. Is that right?

So if I got it right and I still want this behaviour, I'd have to simplify the generic registration through the interface that has just one level of generics, like you've just demonstrated. So, for sake of visualization, something like this:

public class IGenericRequestHandler<T> : IRequestHandler<Command<T>, Command<T>> {}

public class CommandHandler<T> : IGenericRequestHandler<T> {}

public class Command<T> : IRequest<Command<T>> { }

services.AddTransient(typeof(IGenericRequestHandler<string>), typeof(CommandHandler<string>));

@aspnet-hello
Copy link
Author

From @pakrym on Tuesday, June 6, 2017 4:50:53 PM

@thiagomajesk yes, we don't try to deconstruct generic type 'trees'.

In your example you can have services.AddTransient(typeof(IGenericRequestHandler<>), typeof(CommandHandler<>));

@aspnet-hello
Copy link
Author

From @thiagomajesk on Tuesday, June 6, 2017 6:59:51 PM

@pakrym Have you tested this workaround? It seems that even when using the interface approach it doesn't work. It throws the same error on Startup. Even if I try to 'trick' the container making simpler 'sub interfaces' like this:

public interface ICommandHandler<T> : IRequestHandler<Command<T>, Command<T>>
public interface ICommand<T> : IRequest<Command<T>>

public class Command<T> : ICommand<T>
{
    public List<T> List { get; set; }
}
public class CommandHandler<T> : ICommandHandler<T>
{
    public Command<T> Handle(Command<T> message)  { [...]  }
}

Any thoughts?

@aspnet-hello
Copy link
Author

From @tlycken on Monday, August 28, 2017 1:06:00 AM

it creates a lot of complexities in resolution rules, imagine following registrations:

IRequestHandler<ICommand<Action<>>, ICommand<>>
IRequestHandler<ICommand<>, ICommand<string>>

Which one would you use to resolve IRequestHandler<ICommand<Action<string>, ICommand<string>> and why?

This ambiguity situation could actually still be unsupported (and e.g. throw an error on startup, as today), while still supporting having just one of them registered. I stumbled here also from MediatR, and the case I want to get working is having the following interfaces and concrete implementations:

interface IRequest<T> : { }
interface IRequestHandler<TRequest, T> where TRequest : IRequest<T> { }

class Request<T> : IRequest<T> { }
class RequestHandler<T> : IRequestHandler<Request<T>, T> { }

Given an instance of Request<T>, I also know what T is - let's say it's a Request<string> instance I have. Then it's trivial to figure out that I'm looking for an IRequestHandler<Request<string>, string>, and that the corresponding implementation is a RequestHandler<string>. This solves my immediate use case.

Of course, it's possible to come up with situations that are close to this but ambiguous - but even with all its flexibility, the example above is not. (And I'm thinking that the generic type constraint on IRequestHandler<TRequest, T> makes it more difficult...)

@aspnet-hello
Copy link
Author

We periodically close 'discussion' issues that have not been updated in a long period of time.

We apologize if this causes any inconvenience. We ask that if you are still encountering an issue, please log a new issue with updated information and we will investigate.

@aspnet-hello aspnet-hello removed this from the Discussions milestone Jul 11, 2018
@ghost ghost locked as resolved and limited conversation to collaborators Dec 4, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant