ipjohnson / Grace

Grace is a feature rich dependency injection container library
MIT License
336 stars 33 forks source link

Bulk registration of open generics (part 2) #153

Closed davidkeaveny closed 6 years ago

davidkeaveny commented 6 years ago

Continuing from https://github.com/ipjohnson/Grace/issues/148, I've got a slightly different issue; previously, I was registering all implementations of the following:

public interface IQueryHandler<in TQuery, out TResult> where TQuery : IQuery<TResult>
{
    TResult Handle(TQuery query);
}

using the following code in a configuration module:

configure.Export(GetType().Assembly.ExportedTypes)
    .BasedOn(typeof(IQueryHandler<,>))
    .ByInterfaces();

then I would resolve them thusly:

protected TResult Query<TQuery, TResult>(TQuery query) where TQuery : IQuery<TResult>
{
    var handler = _container.Locate<IQueryHandler<TQuery, TResult>>();
    return handler.Handle(query);
}

and there was much rejoicing.

Now however, I want to change my resolution of the type to be like this:

protected TResult Query<TResult>(IQuery<TResult> query)
{
    var handler = _container.Locate<IQueryHandler<IQuery<TResult>, TResult>>();
    return handler.Handle(query);
}

but I am getting a LocateException. I tried explicitly registering a query handler like this:

configure.Export<SomeConcreteQueryHandler>()
    .As<IQueryHandler<IQuery<ConcreteResult>, ConcreteResult>>();

but that results in a RecursiveLocateException instead.

So I don't know if the problem is how I am registering my types, or how I am resolving them! Any ideas?

ipjohnson commented 6 years ago

@davidkeaveny that's a very interesting problem I would have assumed they all work but generics can be tricky. I'm heading out on vacation tomorrow so I don't think I'll have time to look at this till next week.

Would it be possible to put together a simple solution showing this and send it along?

davidkeaveny commented 6 years ago

Repro attached: Cqrs.zip

I tried replacing Grace with StructureMap and got something similar, although SM gave me a compile-time error when I tried to explicitly register the type.

ipjohnson commented 6 years ago

@davidkeaveny I took a look at the example and I can see why you can't do what you want to do. Ultimately it boils down to IQueryHandler<IQuery<ConcreteResult>, ConcreteResult> is not the same interface as IQueryHandler<in TQuery, out TResult> where TQuery : IQuery<TResult> and casting them as such is a runtime exception.

Or put it another way in the implementation example IQueryHandler<TestQuery,TestModel> is not equivalent to IQueryHandler<IQuery<TestModel>,TestModel>.

davidkeaveny commented 6 years ago

Looks like you're right; in the end, I took the approach used by Mediator, which has a wrapper which is able to make the necessary conversion from IQueryHandler<TestQuery, TestModel> to IQueryHandler<IQuery<TestModel>, TestModel>.