This project is read-only.

Partially open generic interface implementation

Jun 14, 2013 at 4:21 PM
Hoping someone can help me figure out how to register this dependency with Simple Injector.
public interface IParticipantSearchQueryHandler<TQuery> : IQueryHandler<TQuery, List<ParticipantInfo>> where TQuery : IQuery<List<ParticipantInfo>>
Looking to register IParticipantSearchQueryHandler<> as an implementation for IQueryHandler<TQuery, List<ParticipantInfo>>

Been struggling on this one and any help would be appreciated!
Jun 14, 2013 at 4:35 PM
It's unclear to me what the problem is. Can you be more specific?
Jun 14, 2013 at 5:09 PM
Edited Jun 14, 2013 at 5:31 PM
I've got the following two Interfaces:
public interface IQueryHandler<TQuery, TResult> where TQuery : IQuery<TResult>
public interface IParticipantSearchQueryHandler<TQuery> : IQueryHandler<TQuery, List<ParticipantInfo>>
Basically, I just want to use IParticipantSearchQueryHandler in places instead of IQueryHandler to limit number and type of IQueryHandler generic arguments.

So instead of:
public class ExampleHandler : IQueryHandler<SomeQuery, List<ParticipantInfo>>
I can use:
public class ExampleHandler : IParticipantSearchQueryHandler<SomeQuery>
I think it comes down to trying to register
 IParticipantSearchQueryHandler<TQuery>
as an implementation for
IQueryHandler<TQuery, List<ParticipantInfo>>
They're both open generics but IQueryHandler is kind of "partially open" as one of its parameters is specified.

I've tried:
container.RegisterOpenGeneric(typeof(IQueryHandler<,>), typeof(IParticipantSearchQueryHandler<>));
Which results in a runtime error of:
The given type IParticipantSearchQueryHandler<TQuery> is not a concrete type. Please use one of the other overloads to register this type.
I've also tried:
container.RegisterOpenGeneric(typeof(IQueryHandler<,List<ParticipantInfo>>), typeof(IParticipantSearchQueryHandler<>));
but that won't compile.
Jun 14, 2013 at 5:56 PM
I think I get it. The IParticipantSearchQueryHandler<TQuery> is just an helper interface to make programming easier and the code more readable. But you probably also want to let consumers depend on that specific interface instead of depending directly onto IQueryable<TQuery, TResult>.

You're indeed on the right track with the use of RegisterOpenGeneric, but you'll need to implement a concrete type:
public class ParticipantSearchQueryHandler<TQuery> 
    : IParticipantSearchQueryHandler<TQuery>
{
    private IQueryHandler<TQuery, List<ParticipantInfo>> handler;

    public ParticipantSearchQueryHandler(
        IQueryHandler<TQuery, List<ParticipantInfo>> handler)
    {
        this.handler = handler;
    }
    
    public List<ParticipantInfo> Handle(TQuery query)
    {
        return this.handler.Handle(query);
    }
}
And this is a type you can register using RegisterOpenGeneric:
container.RegisterOpenGeneric(
    typeof(IParticipantSearchQueryHandler<>),
    typeof(ParticipantSearchQueryHandler<>));
And ofcourse you'll still need the normal batch registration:
container.RegisterManyForOpenGeneric(typeof(IQueryHandler<,>), myAssemblies);
So, what happened here? Since we batch registered all query handlers by their IQueryHandler<,> interface, but didn't register the IParticipantSearchQueryHandler<> interface, the container doesn't know how to resolve that interface, so we need a mapping. With the given RegisterOpenGeneric implementation, the container will resolve the registered concrete ParticipantSearchQueryHandler<> implementation, and since that type depends again on an (actually registered) IQueryHandler<,>, we can close the loop.

You might be tempted to do the following (but don't do that):
container.Register<IParticipantSearchQueryHandler<SomeQuery>, ExampleHandler>();
container.Register<IParticipantSearchQueryHandler<AnotherQuery>, AnotherHandler>();
This seems valid, and could even be automated by using the RegisterManyForOpenGeneric that takes in a delegate, but when you do this, any decorators you defined for IQueryHandler<,> won't be wrapped. The following however, would be valid:
container.Register<IParticipantSearchQueryHandler<SomeQuery>>(
    () => container.GetInstance<IQueryHandler<SomeQuery, List<ParticipantInfo>>>());
container.Register<IParticipantSearchQueryHandler<AnotherQuery>, AnotherHandler>();
    () => container.GetInstance<IQueryHandler<AnotherQuery, List<ParticipantInfo>>>());
Of course this would be rather awkward. You can automate this by using an overload of RegisterManyForOpenGeneric that accepts a callback delegate:

Which can be automated as follows:
container.RegisterManyForOpenGeneric(typeof(IQueryHandler<,>), 
    (service, impls) =>
    {
        var implementation = impls.Single();
        
        container.Register(service, implementation);
        
        var participantInterface = (
            from face in implementation.GetInterfaces()
            where face.IsGenericType
            where face.GetGenericTypeDefinition() == typeof(IParticipantSearchQueryHandler<>)
            select face)
            .SingleOrDefault();
            
        if (participantInterface != null)
        {
            container.Register(participantInterface, () => container.GetInstance(service));
        }
    },
    myAssemblies);
I think these are basically your options.
Marked as answer by dot_NET_Junkie on 3/2/2014 at 10:43 AM
Jun 14, 2013 at 6:43 PM
Edited Jun 14, 2013 at 6:58 PM
Thank you for that fantastically detailed explanation. Your understanding of the issue is dead on except currently I don't have any classes dependent on IParticipantSearchQueryHandler<TQuery> other than those implementing the interface. I am actually utilizing your WCF query handler from the CQRS examples in your blog so an incoming Query type is analyzed dynamically and a corresponding QueryHandler is searched for using conatiner.GetInstance(Type type).

Using the concrete implementation of ParticipantSearchQueryHandler<TQuery> as suggested limits the Handler to one type of Query. In my situation (where the Handler is actually a plug-in developed around communicating with a 3rd party) it would be handy to do something like the following:
public class ExampleParticipantSearchQueryHandler: 
        IParticipantSearchQueryHandler<ParticipantSearchByAccountNumberQuery>,
        IParticipantSearchQueryHandler<ParticipantSearchByCriteriaQuery>,
        IParticipantSearchQueryHandler<ParticipantSearchByLocationQuery>
Is there any way you can think of to accomplish this?
Jun 14, 2013 at 11:26 PM
Implementing multiple closed-generic versions of the same open-generic interface is supported out of the box, so I'm not sure what the problem is. If you do this:
container.RegisterManyForOpenGeneric(typeof(IQueryHandler<,>), myAssemblies);
You'll get three registrations for the same ExampleParticipantSearchQueryHandler, one for each IQueryHandler<,>.

Since you are sending query messages over the wire, there is no need for an mapping of IParticipantSearchQueryHandler<> to ParticipantSearchQueryHandler<> using RegisterOpenGeneric. Such mapping is only needed when there are actually consumers that depend on IParticipantSearchQueryHandler<> or when you request IParticipantSearchQueryHandler<> directly from the container, but this never seems the case in your application, since the WCF service always directly asks for an IQueryHandler<,>.

Or did I misunderstand?
Jun 15, 2013 at 9:07 PM
Edited Jun 15, 2013 at 9:08 PM
That is exactly the info I was looking for! I guess I just assumed that SimpleInjector would need some sort of hint to realize that ExampleParticipantSearchQueryHandler was actually an implementation of IQueryHandler<,>.

In my case the ParticipantSearchQueryHandler<TQuery> implementations are plug-ins so they exist in separate assemblies (not referenced by the main app). If not for that I might have just stumbled onto the fact that it worked using the registration you suggested:
container.RegisterManyForOpenGeneric(typeof(IQueryHandler<,>), myAssemblies);
which I already had in my Bootstrapper.

Sound like I'll just have to create a separate list of plug-in assemblies to pass to RegisterManyForOpenGeneric.

Thank you so much for the amazing support, not only did you help me better understand SimpleInjector but you also gave me a great solution should I want to use IParticipantSearchQueryHandler<> as a dependency in the future!
Jun 16, 2013 at 10:03 AM
You're more than welcome.