Sieve icon indicating copy to clipboard operation
Sieve copied to clipboard

CQRS + MediatR query filtering, sorting and paging with Sieve package

Open Lukash88 opened this issue 2 years ago • 0 comments

Welcome everyone,

According to the title I want to add Sieve package to my small portfolio project which is using IMediatR and CQRS.

How and where should I apply this to implement this in correct way and keep the clean architecture? In controller, handler, command (I know that is for add, remove and update, but I saw somewhere that someone uses HttpPost for retrieving the data to avoid exceeding to long query if there is to many filters applied) or query?

Registration in Startup.cs

public void ConfigureServices(IServiceCollection services)
       {
           services.AddScoped<ISieveProcessor, ApplicationSieveProcessor>();
       }

Implementation of SieveProcessor :

 public class ApplicationSieveProcessor : SieveProcessor
    {
        public ApplicationSieveProcessor(
            IOptions<SieveOptions> options,
            ISieveCustomSortMethods customSortMethods,
            ISieveCustomFilterMethods customFilterMethods)
            : base(options, customSortMethods, customFilterMethods)
            {
            }

        protected override SievePropertyMapper MapProperties(SievePropertyMapper mapper)
        {
            return mapper.ApplyConfigurationsFromAssembly(typeof(ApplicationSieveProcessor).Assembly);
        }
    }

Implementation of ISieveConfiguration:

public class SieveConfigurationForProduct : ISieveConfiguration
    {
        public void Configure(SievePropertyMapper mapper)
        {
            mapper.Property<Product>(p => p.Name)
                .CanSort()
                .CanFilter();

            mapper.Property<Product>(p => p.Price)
                .CanSort()
                .CanFilter();

            mapper.Property<Product>(p => p.Category)
                .CanSort()
                .CanFilter();

            return mapper;
        }
    }

This is my ProductsCotroller:

public class ProductsController : ApiControllerBase
    {
        public ProductsController(IMediator mediator, ILogger<ProductsController> logger) : base(mediator)
        {
               logger.LogInformation("We are in Products");
        }
 
    [HttpGet]
    [Route("")]
    public async Task<IActionResult> GetAllProducts([FromQuery] GetProductsRequest request) =>
        await this.HandleRequest<GetProductsRequest, GetProductsResponse>(request);

    [HttpGet]
    [Route("{productId}")]
    public async Task<IActionResult> GetProductById([FromRoute] int productId)
    {
        var request = new GetProductByIdRequest()
        {
            ProductId = productId
        };

        return await this.HandleRequest<GetProductByIdRequest, GetProductByIdResponse>(request);
    }

GetProductsRequest :

 public class GetProductsRequest : IRequest<GetProductsResponse>
    {
        public string Name { get; init; }
        public SieveModel SieveModel { get; }

        public GetProductsRequest(SieveModel sieveModel)
        {
            SieveModel = sieveModel;
        }
    }

GetProductsRequest :

 public class GetProductsRequest : IRequest<GetProductsResponse>
    {
        public string Name { get; init; }
        public SieveModel SieveModel { get; }

        public GetProductsRequest(SieveModel sieveModel)
        {
            SieveModel = sieveModel;
        }
    }

This is my GetProductsHandler:

 public class GetProductsHandler : IRequestHandler<GetProductsRequest, GetProductsResponse>
{
    private readonly IMapper mapper;
    private readonly IQueryExecutor queryExecutor;

    public GetProductsHandler(IMapper mapper, IQueryExecutor queryExecutor)
    {
        this.mapper = mapper;
        this.queryExecutor = queryExecutor;
    }

    public async Task<GetProductsResponse> Handle(GetProductsRequest request, CancellationToken cancellationToken)
    {
        var query = new GetProductsQuery()
        {
            Name = request.Name
        };
        var products = await this.queryExecutor.Execute(query);
        if (products == null)
        {
            return new GetProductsResponse()
            {
                Error = new ErrorModel(ErrorType.NotFound)
            };
        }

        var mappedProducts = this.mapper.Map<List<Domain.Models.ProductDTO>>(products);
        var response = new GetProductsResponse()
        {
            Data = mappedProducts
        };

        return response;
    }
}

Here is my GetProductsQuery:

public class GetProductsQuery : QueryBase<List<Product>>
   {
       public string Name { get; init; }

       public async override Task<List<Product>> Execute(FlowerShopStorageContext context)
       {
           var productsFilteredByName = !string.IsNullOrEmpty(Name) ?
               await context.Products.AsNoTracking().OrderBy(p => p.Name).ToListAsync() : await context.Products.ToListAsync();

           return productsFilteredByName;
       }
   }

Thank you in advance for help or advice.

Regards, Lukas

Lukash88 avatar Jun 28 '22 20:06 Lukash88