Skip to content

Queries

Martin Havlišta edited this page Jul 12, 2018 · 39 revisions

CoreDdd encourages CQRS and application queries to be explicitly implemented as a query and a query handler instead of querying data directly in the code. A CoreDdd query implements IQuery interface, and a query handler implements IQueryHandler<TQuery> interface, or is derived from BaseQueryOverHandler for NHibernate QueryOver SQL queries, or from BaseAdoNetQueryHandler for ADO.NET SQL queries.

CoreDdd queries should return data in a DTO form. The recommended way is to map DTO objects into database views, where the database view contains SQL which queries domain entity tables. The advantage of this approach is that a developer can fine tune the database view SQL code instead of relying on the generated SQL code when using NHibernate QueryOver/Criteria with domain entities and their associations.

Before creating a new query, first persist the entities the query will use. Then create a new query, for instance get ships by name query:

    public class GetShipsByNameQuery : IQuery
    {
        public string ShipName { get; set; }
    }

The query will return ship data in a ShipDto:

    public class ShipDto
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }

ShipDto class will be automatically mapped into a database view ShipDto by FluentNHibernate. This database view does not exist, a needs to be created manually by executing the following SQL script, SQLite example:

drop view if exists ShipDto;

create view ShipDto
as
select 
    Id
    , Name
from Ship 

Now add a new query handler:

    public class GetShipsByNameQueryHandler : BaseQueryOverHandler<GetShipsByNameQuery>
    {
        public GetShipsByNameQueryHandler(NhibernateUnitOfWork unitOfWork) 
            : base(unitOfWork)
        {
        }

        protected override IQueryOver GetQueryOver<TResult>(GetShipsByNameQuery query)
        {
            return Session.QueryOver<ShipDto>()
                          .WhereRestrictionOn(x => x.Name)
                          .IsLike($"%{query.ShipName}%");
        }
    }

Execute the query using the following code:

            using (var unitOfWork = new NhibernateUnitOfWork(nhibernateConfigurator))
            {
                unitOfWork.BeginTransaction();

                var shipRepository = new NhibernateRepository<Ship>(unitOfWork);
                
                try
                {
                    var ship = new Ship("lady starlight", tonnage: 10m);
                    await shipRepository.SaveAsync(ship);

                    unitOfWork.Flush();

                    var getShipByNameQuery = new GetShipsByNameQuery {ShipName = "lady"};
                    var getShipByNameQueryHandler = new GetShipsByNameQueryHandler(unitOfWork);

                    var shipDtos = await getShipByNameQueryHandler.ExecuteAsync<ShipDto>(getShipByNameQuery);

                    Console.WriteLine($"Ship by name query was executed. Number of ships queried: {shipDtos.Count()}");

                    unitOfWork.Commit();
                }
                catch
                {
                    unitOfWork.Rollback();
                    throw;
                }
            }

The code sample above is available here as .NET Core console app.

Another way how to execute a query is to use CoreDdd QueryExecutor. Instead of instantiating and executing the query handler directly, QueryExecutor can be used to instantiate and execute the query handler based on the query type:

            using (var unitOfWork = new NhibernateUnitOfWork(nhibernateConfigurator))
            {
                unitOfWork.BeginTransaction();

                var shipRepository = new NhibernateRepository<Ship>(unitOfWork);
                var queryExecutor = new QueryExecutor(new FakeQueryHandlerFactory(unitOfWork));

                try
                {
                    var ship = new Ship("lady starlight", tonnage: 10m);
                    await shipRepository.SaveAsync(ship);

                    unitOfWork.Flush();

                    var getShipByNameQuery = new GetShipsByNameQuery {ShipName = "lady"};
                    var shipDtos = await queryExecutor.ExecuteAsync<GetShipsByNameQuery, ShipDto>(getShipByNameQuery);

                    Console.WriteLine($"Ship by name query was executed by query executor. Number of ships queried: {shipDtos.Count()}");

                    unitOfWork.Commit();
                }
                catch
                {
                    unitOfWork.Rollback();
                    throw;
                }
            }

The code above uses FakeQueryHandlerFactory which is a query handler abstract factory implementation, which creates an instance of GetShipsByNameQueryHandler based on the GetShipsByNameQuery type.

The code sample above is available here as .NET Core console app.

QueryExecutor is not supposed to be used directly, instead IQueryExecutor interface should be used together with an IoC container to execute queries. Most IoC containers (e.g. Castle Windsor or Ninject) are able to provide abstract factory implementation out of the box, so no manual implementation like FakeQueryHandlerFactory is needed.

To start using QueryExecutor with IoC container, first register components into the IoC container, Castle Windsor example:

            var ioCContainer = new WindsorContainer();
            ioCContainer.AddFacility<TypedFactoryFacility>();

            ioCContainer.Register(
                Component.For<IQueryHandlerFactory>().AsFactory(),
                Component.For<IQueryExecutor>() // register query executor
                    .ImplementedBy<QueryExecutor>()
                    .LifeStyle.Transient,
                Classes
                    .FromAssemblyContaining<GetShipsByNameQuery>() // register all query handlers in this assembly
                    .BasedOn(typeof(IQueryHandler<>))
                    .WithService.FirstInterface()
                    .Configure(x => x.LifestyleTransient()),
                Component.For<INhibernateConfigurator>() // register nhibernate configurator
                    .ImplementedBy<CoreDddSampleNhibernateConfigurator>()
                    .LifeStyle.Singleton,
                Component.For<NhibernateUnitOfWork>() // register nhibernate unit of work
                    .LifeStyle.PerThread
            );

And then resolve IQueryExecutor and execute the query:

    var queryExecutor = ioCContainer.Resolve<IQueryExecutor>();
    var getShipByNameQuery = new GetShipsByNameQuery { ShipName = "lady" };
    var shipDtos = await queryExecutor.ExecuteAsync<GetShipsByNameQuery, ShipDto>(getShipByNameQuery);

The code sample above is available here as .NET Core console app.

Clone this wiki locally