Skip to content

Queries

Martin Havlišta edited this page Jul 11, 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.

Clone this wiki locally