patterncsharpMinor
Generic queries and handlers without reflection
Viewed 0 times
genericwithoutreflectionhandlersandqueries
Problem
I'm trying to implement generic queries and handlers so that I can make requests with simple syntax, like this:
This post explains a brilliant technique for accomplishing this, but it uses reflection and
The central idea is the IQuery:
One such implementation could be:
The TSelf type argument is the key difference between my technique and the other post's technique. It allows us to avoid reflection in the HandlerResolver:
You would register handlers at the composition root. Here's the interface for a handler, and an implementation for HelloQuery:
And finally, the actual query processor:
```
public interface IQueryProcessor
{
TResult Process(IQuery request)
where TQuery : IQuery;
}
public class QueryProcessor : IQue
var query = new HelloQuery("hi");
var result = processor.Process(query);This post explains a brilliant technique for accomplishing this, but it uses reflection and
dynamic to resolve handlers for queries. I came up with an alternative using generics.The central idea is the IQuery:
public interface IQuery
where TSelf : IQuery
{
}One such implementation could be:
public class HelloQuery : IQuery
{
public HelloQuery(string message)
{
this.message = message;
}
private readonly string message;
public string Message
{
get { return this.message; }
}
}The TSelf type argument is the key difference between my technique and the other post's technique. It allows us to avoid reflection in the HandlerResolver:
public interface IHandlerResolver
{
IQueryHandler GetHandlerFor()
where TQuery: IQuery;
}
public class HandlerResolver : IHandlerResolver
{
private readonly IContainer container;
public HandlerResolver(IContainer container)
{
this.container = container;
}
public IQueryHandler GetHandlerFor()
where TQuery : IQuery
{
var handler = this.container.Get>();
return handler;
}
}You would register handlers at the composition root. Here's the interface for a handler, and an implementation for HelloQuery:
public interface IQueryHandler
where TQuery : IQuery
{
TResult Handle(TQuery request);
}
public class HelloQueryHandler : IQueryHandler
{
public string Handle(HelloQuery request)
{
var response = string.Format("Well \"{0}\" yourself!", request.Message);
return response;
}
}And finally, the actual query processor:
```
public interface IQueryProcessor
{
TResult Process(IQuery request)
where TQuery : IQuery;
}
public class QueryProcessor : IQue
Solution
One objection I could raise is that your generic type constraint doesn't guarantee what you think it might guarantee. Eg. you could have:
In other words,
However, as long as you're not a masochist and don't declare nonsensical types like this, there is nothing wrong with this. This is an established design pattern known as the curiously recurring template pattern.
// TSelf is actually not T of Self
public class HelloQuery : IQuery
{
}
// This one is fine
public class GoodbyeQuery: IQuery
{
}In other words,
TSelf doesn't actually have to be the T of the class itself. This is not horrible, especially since you seem to only be using the interface as metadata, but it can still lead to confusing results during dependency injection if you've accidentally screwed up a class definition.However, as long as you're not a masochist and don't declare nonsensical types like this, there is nothing wrong with this. This is an established design pattern known as the curiously recurring template pattern.
Code Snippets
// TSelf is actually not T of Self
public class HelloQuery : IQuery<GoodbyeQuery, string>
{
}
// This one is fine
public class GoodbyeQuery: IQuery<GoodbyeQuery, string>
{
}Context
StackExchange Code Review Q#79394, answer score: 6
Revisions (0)
No revisions yet.