- Designing the Query Engine
- Encapsulating common behavior (template pattern)
- Implementing Queries
- Creating the Query Resolver
Finally in this chapter I will show you the result of the Query Engine and in the next post I will show a way to decouple the query handler from your code.
So let’s start creating queries.
Querying By ID
The first Query I want to create is this one, just a simple query by ID
A table/view can have a primary key composed of just one field or by several fields, with this approach you can express the intent of the query explicitly and specify any number of ID’s to query your objects. Furthermore, sometimes your tables/views could have unique indexes additional to the primary key, you can use this approach to also find each unique items
So let’s see the code:
We need an object implementing the IQueryID
mark interface to specify it as the ID we want to search for.
public sealed class MovieID : IQueryID
{
public MovieID(Guid id)
{
this.ID = id;
}
public Guid ID { get; private set; }
}
That’s it, we are explicitly specifying the ID used by our Movie entity.
What we’re actually doing is: we are taking an implicit hidden domain concept (the Movie Identifier) and we are making it explicit
The next step is to create the query:
public class FindMovieByIDQueryHandler : QueryByIDHandler<MovieID, Movie>
{
private IMoviesQueryRepository moviesQueryRepository;
public FindMovieByIDQueryHandler(IMoviesQueryRepository moviesQueryRepository)
{
this.moviesQueryRepository = moviesQueryRepository;
}
protected override IQueryable<Movie> InitialItems
{
get { return this.moviesQueryRepository.GetItems(); }
}
protected override Movie FindItem(MovieID queryID, IQueryable<Movie> items)
{
return items.FirstOrDefault(x => x.ID.Equals(queryID.ID));
}
}
That’s it. You created your first query completely isolated so that it can be safely unit tested. And since this query is isolated, creating new queries won’t interfere in any way with any other queries created, so there’s no risk to break existing queries =), you won’t require to change any interfaces nor existing implementations in order to add a new query, you could even create this query in a new assembly, drop it to your application’s bin folder and use reflection to load it into your IoC container when the application starts.
But we are missing something, first let me remember you the definition of the QueryByIDHandler
class:
public abstract class QueryByIDHandler<TQueryID, TQueryResult> : IQueryByIDHandler<TQueryID, TQueryResult>
where TQueryID : IQueryID
The class implements IQueryByIDHandler<TQueryID, TQueryResult>
, so with this code, you would have to inject this dependency (interface) in the object you want to use your query, so let’s imagine that you want to use it in a MVC application, your code would look like:
Note: I know that you would never use a handler in this way, I’m just using it as an example. And I want to emphasize, the following code is not meant to be used in this way because you would be tight coupling your code with this specific Query Handler
public partial class MoviesController : Controller
{
private IQueryByIDHandler<MovieID, Movie> queryByIdHandler;
public MoviesController(IQueryByIDHandler<MovieID, Movie> queryByIdHandler)
{
this.queryByIdHandler = queryByIdHandler;
}
}
Please pay special attention to:
IQueryByIDHandler<MovieID, Movie> queryByIdHandler
This doesn’t look right to me. The nature of the IQueryByIDHandler
is to be generic so that we can use it to create custom queries, but using it like this we are hiding the intention of the query.
So what’s the intention? Well in this case the intention is to simply provide a way to query the Movies collection to find a specific Movie by ID
To me the correct naming would be something like:
IFindMovieByIDQueryHandler
In this case we are making the concept and the intention of the query explicit in the application. The readability of the code is dramatically increased when you read:
IFindMovieByIDQueryHandler
vs
IQueryByIDHandler<MovieID, Movie>
In order to create this interface we need code like this:
public interface IFindMovieByIDQueryHandler : IQueryByIDHandler<MovieID, Movie>
{
}
And then use it to mark our query:
public class FindMovieByIDQueryHandler : QueryByIDHandler<MovieID, Movie>, IFindMovieByIDQueryHandler
{
private IMoviesQueryRepository moviesQueryRepository;
public FindMovieByIDQueryHandler(IMoviesQueryRepository moviesQueryRepository)
{
this.moviesQueryRepository = moviesQueryRepository;
}
protected override IQueryable<Movie> InitialItems
{
get { return this.moviesQueryRepository.GetItems(); }
}
protected override Movie FindItem(MovieID queryID, IQueryable<Movie> items)
{
return items.FirstOrDefault(x => x.ID.Equals(queryID.ID));
}
}
And finally the updated controller would look like (Again just for demonstration purpose):
public partial class MoviesController : Controller
{
private IFindMovieByIDQueryHandler findMovieByIDHandler;
public MoviesController(IFindMovieByIDQueryHandler findMovieByIDHandler)
{
this.findMovieByIDHandler = findMovieByIDHandler;
}
}
If you want to learn more about Making Roles Explicit, please see this Uddi Dahan video
Querying all items in the collection
In order to query all items (the typical FindAll
method) we will create the interface to expose the query concept
public interface IFindAllMoviesQueryHandler : IQueryHandler<Movie>
{
}
Then the implementation of the query
public class FindAllMoviesQueryHandler : QueryHandler<Movie>, IFindAllMoviesQueryHandler
{
private IMoviesQueryRepository moviesQueryRepository;
public FindAllMoviesQueryHandler(IMoviesQueryRepository moviesQueryRepository)
{
this.moviesQueryRepository = moviesQueryRepository;
}
protected override IQueryable<Movie> ApplyDefaultOrder(IQueryable<Movie> items)
{
return items.OrderByDescending(x => x.ID);
}
protected override IQueryable<Movie> InitialItems
{
get { return this.moviesQueryRepository.GetItems(); }
}
}
The code is actually straightforward, so it doesn’t require explanation
Applying specific queries
This is the most interesting part of the Query Engine, and also the most used in RL applications.
Here we will define all the specific queries needed in the application but only when they are required, and like I explained before, adding new queries won’t affect in any way to any existing query
So let’s start by making the query concept explicit, in order to do it, we need an object implementing the IQuery
interface, something like this:
public sealed class FindMoviesByTitleQuery : IQuery
{
public FindMoviesByTitleQuery(string title)
{
this.Title = title;
}
public string Title { get; private set; }
}
In this query class you will define all fields needed in order to apply the query, in this case I just need to query by title
Then, let’s create the mark of the query handler related to this query:
public interface IFindMoviesByTitleQueryHandler : IQueryHandler<FindMoviesByTitleQuery, Movie>
{
}
And finally the implementation:
public class FindMoviesByTitleQueryHandler : QueryHandler<FindMoviesByTitleQuery, Movie>, IFindMoviesByTitleQueryHandler
{
private IMoviesQueryRepository moviesQueryRepository;
public FindMoviesByTitleQueryHandler(IMoviesQueryRepository moviesQueryRepository)
{
this.moviesQueryRepository = moviesQueryRepository;
}
protected override IQueryable<Movie> ApplyDefaultOrder(IQueryable<Movie> items)
{
return items.OrderBy(x => x.Title);
}
protected override IQueryable<Movie> InitialItems
{
get { return this.moviesQueryRepository.GetItems(); }
}
protected override IQueryable<Movie> ApplyQuery(FindMoviesByTitleQuery query, IQueryable<Movie> items)
{
var customQuery = items;
if (!string.IsNullOrWhiteSpace(query.Title))
{
customQuery = customQuery.Where(x => x.Title.ToLower().Contains(query.Title.ToLower()));
}
return customQuery;
}
}
Again, the code is straightforward and self explanatory therefore it doesn’t require any explanation
At this point, you would be able to use the query handlers in your application by exposing them as dependencies of your objects, using DI, and by adding them to your IoC container, however, like I said before, if you use them this way, you would be tight coupling your handlers with your code
As an exercise for the reader, go ahead and do it, create a simple application and use the handlers directly by injecting them in your objects, just to play with them
In the next post I will show you a way to completely decouple the handlers from your code.
An article diagram is a record www.uk-essay.net of all needed qualified information that you arrangement to incorporate in your article
ReplyDelete