Is it possible to invoke a generic method in a strongly-typed fashion while maintaining the ability to infer types at compile-time?

StackOverflow https://stackoverflow.com/questions/21610038

Question

I have a couple of interfaces as follows, used to mediate query objects and query handlers:-

public interface IQuery<TResponse> { }

public interface IQueryBus
{
  public TResponse Resolve<TResponse>(IQuery<TResponse> query);
}

It's supposed to be invoked in the controller layer as follows:-

FooQuery query = ... // comes from model binding
FooResponse response = this.bus.Resolve(query);

The bus has the responsibility for locating a handler for the query:-

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

The handlers are wired up using a dependency resolver (I'm using Ninject, but it shouldn't matter terribly).

My current implementation is along the following lines:-

public TResponse Resolve(IQuery<TResponse> query)
{
  var queryType = query.GetType();
  var handlerType = typeof(IQueryHandler<,>)
    .MakeGenericType(queryType, typeof(TResponse));

  var handleMethod = handlerType.GetMethod("Handle", ...);

  var handler = kernel.Get(handlerType);

  return (TResponse)handleMethod.Invoke(handler, new[] { query });
}

My problem is that I'd like to avoid the call to MethodInfo.Invoke for both stylistic and performance reasons.

If I change the signature of IQueryBus.Resolve to:-

public TResponse Resolve<TQuery, TResponse>(TQuery query)
  where TQuery : IQuery<TResponse>

Then it's pretty simply to change the implementation:-

var handler = kernel.GetType<IQueryHandler<TQuery, TResponse>>();
return handler.Handle(query);

but then of course the generic type parameters cannot be inferred at the point of use:-

FooResponse response = this.bus.Resolve<FooQuery, FooResponse>(query);

Can I have my cake and eat it? Is there a solution that satisfies the following:-

  • Doesn't change the signature of IQueryBus.Resolve (i.e. allows its generic type parameters to be inferred)
  • Allows for compile-time type-safety of the Query, the Response, and the invocation of the Handler (i.e. no reflection or dynamic dispatching)
  • Doesn't change the interface of IQueryHandler (i.e. doesn't expose the handlers to the implementation constraints of the bus)

I have control over the rest of the code, I can introduce new types and entirely replace the implementation of IQueryBus. I can cache work done by the dependency resolver on app start (which is the most promising lead I have atm).

If the worst comes to the worst, then I guess asking the caller to specify the generic type parameters isn't the end of the world, but I'm hoping for something a bit more elegant that minimises the opportunity for future mistakes.

There's a pretty similar implementation at ShortBus on github if you're looking for a more complete real-world example.

Was it helpful?

Solution

If there is no possibility to have multiple Queries using same TResult, then something like this is possible.

public interface IQueryHandler<out TResponse>
{
    TResponse Handle(object query);
}

public abstract class QueryHandler<TQuery, TResponse> : IQueryHandler<TResponse>
{
    TResponse IQueryHandler<TResponse>.Handle(object query)
    {
        return Handle((TQuery)query);
    }

    protected abstract TResponse Handle(TQuery query);
}

public TResponse Resolve<TResponse>(IQuery<TResponse> query)
{
    var handlerType = typeof(IQueryHandler<TResponse>);

    IQueryHandler<TResponse> handler = kernel.Get(handlerType);

    return handler.Handle(query);
}

You might loose typing a little in the middle, but the specific queries and handler are all properly typed just like before. And only performance impact I see is possibility of boxing when TResponse is a value type. But that would get overshadowed by IoC looking for the concrete handler.

OTHER TIPS

Posted by request:-

interface IQueryHandlerAdapter<TResponse>
{
  TResponse Handle(IQuery<TResponse> query);
}

class QueryHandlerAdapter<TQuery, TResponse>
  where TQuery : class, IQuery<TResponse>
{
  private readonly IQueryHandler<TQuery, TResponse> handler;

  public QueryHandlerAdapter(IQueryHandler<TQuery, TResponse> handler)
  {
    this.handler = handler;
  }

  public TResponse Handle(IQuery<TResponse> query)
  {
    var q = query as TQuery;

    if (q == null)
    {
      throw new InvalidOperationException("...");
    }

    return this.handler.Handle(q);
  }
}

class SynchronousQueryBus
{
  private readonly IKernel kernel;

  public SynchronousQueryBus(IKernel kernel)
  {
    this.kernel = kernel;
  }

  public TResponse Resolve<TResponse>(IQuery<TResponse> query)
  {
    if (query == null)
    {
      throw new ArgumentNullException("query");
    }

    var handler = this.kernel.Get<IQueryHandlerAdapter<TResponse>>();

    return handler.Handle(query);
  }
}

Then all I need to do is wire up all the Query/Response pairs to the respective concrete adapter types at app start. I'm doing that by hand for the moment, but it's not terribly complicated to loop through types implementing IQuery and bind them like that so I'll refactor once I'm done testing.

That way the mess gets contained and whoever implements the query objects and handlers has the minimum possible opportunity to get things wrong.

Basically the same Implementation but here's ours.

public interface Query<TResult> { }
public interface QueryProcessor
{
        TResult Process<TResult>(Query<TResult> query);
}
public interface QueryHandler<in TQuery, out TResult> where TQuery : Query<TResult>
{
        TResult Handle(TQuery query);
}

//GetSomethingByIdQuery Implements : Query<Something>
//so Something will be the return value 
queryProcessor.Process(new GetSomethingByIdQuery());    

//Query Processor
public TResult Process<TResult>(Query<TResult> query)
{
       var handlerType = typeof(QueryHandler<,>)
       .MakeGenericType(query.GetType(), typeof(TResult));

   dynamic handler = _container.Resolve(handlerType);


   return handler.Handle((dynamic)query);
}

Here's the Handler

public class GetSomethingByIdQueryHandler : QueryHandler<GetSomethingByIdQuery, Something>
{   
    public Something Handle(GetSomethingByIdQuery query)
    {
       using (var session = _store.OpenSession())
      {
        return session.Load<Something >(query.Id)   
      }
    }
} 
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top