Skip to content

Commit

Permalink
Merge pull request #468 from neozhu/removeAutoFilterer
Browse files Browse the repository at this point in the history
🍒 Improve Specification Query
  • Loading branch information
neozhu authored Aug 9, 2023
2 parents 7d65c16 + cd0a8c5 commit 3773c43
Show file tree
Hide file tree
Showing 21 changed files with 69 additions and 55 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using CleanArchitecture.Blazor.Application.Common.ExceptionHandlers;
using CleanArchitecture.Blazor.Application.Common.Interfaces.Identity;
using CleanArchitecture.Blazor.Application.Common.Security;

Expand Down
42 changes: 38 additions & 4 deletions src/Application/Common/Extensions/QueryableExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,50 @@
// The .NET Foundation licenses this file to you under the MIT license.

using Ardalis.Specification.EntityFrameworkCore;
using Ardalis.Specification;
using CleanArchitecture.Blazor.Domain.Common;
using DocumentFormat.OpenXml.Bibliography;

namespace CleanArchitecture.Blazor.Application.Common.Extensions;

public static class QueryableExtensions
{
public static IQueryable<T> Specify<T>(this IQueryable<T> query, ISpecification<T> spec, bool evaluateCriteriaOnly=false) where T : class, IEntity
/// <summary>
/// Extension method to provided query by specification.
/// </summary>
/// <remarks>
/// This method uses the SpecificationEvaluator to evaluate and modify the provided query based on the given specification.
/// </remarks>
/// <typeparam name="T">Type of the entities in the query</typeparam>
/// <param name="query">The original query to which the specification should be applied</param>
/// <param name="spec">The specification to apply to the query</param>
/// <param name="evaluateCriteriaOnly">Optional flag to determine if only the criteria should be evaluated or other configurations as well</param>
/// <returns>The modified query with the specification applied</returns>
public static IQueryable<T> ApplySpecification<T>(this IQueryable<T> query, ISpecification<T> spec, bool evaluateCriteriaOnly=false) where T : class, IEntity
{
return new SpecificationEvaluator().GetQuery(query, spec, evaluateCriteriaOnly);
return SpecificationEvaluator.Default.GetQuery(query, spec, evaluateCriteriaOnly);
}
/// <summary>
/// Extension method to provided ordered queryable data to a paginated result set.
/// </summary>
/// <remarks>
/// This method will apply the given specification to the query, paginate the results, and project them to the desired result type.
/// </remarks>
/// <typeparam name="T">Source type of the entities in the query</typeparam>
/// <typeparam name="TResult">Destination type to which the entities should be projected</typeparam>
/// <param name="query">The original ordered query to project and paginate</param>
/// <param name="spec">The specification to apply to the query before projection and pagination</param>
/// <param name="pageNumber">The desired page number of the paginated results</param>
/// <param name="pageSize">The number of items per page in the paginated results</param>
/// <param name="configuration">Configuration for the projection</param>
/// <param name="cancellationToken">Optional cancellation token to cancel the operation</param>
/// <returns>The paginated and projected data</returns>
public static async Task<PaginatedData<TResult>> ProjectToPaginatedDataAsync<T,TResult>(this IOrderedQueryable<T> query, ISpecification<T> spec,int pageNumber,int pageSize, IConfigurationProvider configuration, CancellationToken cancellationToken = default) where T : class, IEntity
{

var specificationEvaluator = SpecificationEvaluator.Default;
var count =await specificationEvaluator.GetQuery(query,spec).CountAsync();
var data = await specificationEvaluator.GetQuery(query.AsNoTracking(), spec).Skip((pageNumber-1)* pageSize).Take(pageSize)
.ProjectTo<TResult>(configuration)
.ToListAsync(cancellationToken);
return new PaginatedData<TResult>(data, count, pageNumber, pageSize);
}
}
2 changes: 0 additions & 2 deletions src/Application/Common/Mappings/MappingExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Ardalis.Specification;

namespace CleanArchitecture.Blazor.Application.Common.Mappings;

public static class MappingExtensions
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@

using CleanArchitecture.Blazor.Application.Features.AuditTrails.Caching;
using CleanArchitecture.Blazor.Application.Features.AuditTrails.DTOs;
using CleanArchitecture.Blazor.Application.Features.Documents.Queries.PaginationQuery;
using CleanArchitecture.Blazor.Application.Features.Products.Queries.Specification;
using CleanArchitecture.Blazor.Domain.Enums;

namespace CleanArchitecture.Blazor.Application.Features.AuditTrails.Queries.PaginationQuery;
Expand Down Expand Up @@ -44,10 +42,8 @@ IMapper mapper
public async Task<PaginatedData<AuditTrailDto>> Handle(AuditTrailsWithPaginationQuery request,
CancellationToken cancellationToken)
{
var data = await _context.AuditTrails.Specify(request.Specification)
.OrderBy($"{request.OrderBy} {request.SortDirection}")
.ProjectTo<AuditTrailDto>(_mapper.ConfigurationProvider)
.PaginatedDataAsync(request.PageNumber, request.PageSize);
var data = await _context.AuditTrails.OrderBy($"{request.OrderBy} {request.SortDirection}")
.ProjectToPaginatedDataAsync<AuditTrail, AuditTrailDto>(request.Specification, request.PageNumber, request.PageSize, _mapper.ConfigurationProvider, cancellationToken);

return data;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.ComponentModel;
using CleanArchitecture.Blazor.Application.Features.Customers.DTOs;
using CleanArchitecture.Blazor.Application.Features.Customers.Caching;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.ComponentModel;
using CleanArchitecture.Blazor.Application.Features.Customers.DTOs;
using CleanArchitecture.Blazor.Application.Features.Customers.Caching;

Expand Down
1 change: 0 additions & 1 deletion src/Application/Features/Customers/DTOs/CustomerDto.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.ComponentModel;
namespace CleanArchitecture.Blazor.Application.Features.Customers.DTOs;

[Description("Customers")]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ IStringLocalizer<ExportCustomersQueryHandler> localizer
public async Task<Result<byte[]>> Handle(ExportCustomersQuery request, CancellationToken cancellationToken)
{
// TODO: Implement ExportCustomersQueryHandler method
var data = await _context.Customers.Specify(request.Specification)
var data = await _context.Customers.ApplySpecification(request.Specification)
.OrderBy($"{request.OrderBy} {request.SortDirection}")
.ProjectTo<CustomerDto>(_mapper.ConfigurationProvider)
.AsNoTracking()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,9 @@ IStringLocalizer<CustomersWithPaginationQueryHandler> localizer
public async Task<PaginatedData<CustomerDto>> Handle(CustomersWithPaginationQuery request, CancellationToken cancellationToken)
{
// TODO: Implement CustomersWithPaginationQueryHandler method
var data = await _context.Customers.Specify(request.Specification)
.OrderBy($"{request.OrderBy} {request.SortDirection}")
.ProjectTo<CustomerDto>(_mapper.ConfigurationProvider)
.PaginatedDataAsync(request.PageNumber, request.PageSize);
return data;
var data = await _context.Customers.OrderBy($"{request.OrderBy} {request.SortDirection}")
.ProjectToPaginatedDataAsync<Customer, CustomerDto>(request.Specification, request.PageNumber, request.PageSize, _mapper.ConfigurationProvider, cancellationToken);
return data;
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using CleanArchitecture.Blazor.Application.Common.ExceptionHandlers;
using CleanArchitecture.Blazor.Application.Features.Documents.Caching;
using CleanArchitecture.Blazor.Application.Features.Documents.DTOs;
using CleanArchitecture.Blazor.Domain.Enums;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,8 @@ IMapper mapper
public async Task<PaginatedData<DocumentDto>> Handle(DocumentsWithPaginationQuery request,
CancellationToken cancellationToken)
{
var data = await _context.Documents.Specify(request.Specification)
.OrderBy($"{request.OrderBy} {request.SortDirection}")
.ProjectTo<DocumentDto>(_mapper.ConfigurationProvider)
.PaginatedDataAsync(request.PageNumber,request.PageSize);
var data = await _context.Documents.OrderBy($"{request.OrderBy} {request.SortDirection}")
.ProjectToPaginatedDataAsync<Document, DocumentDto>(request.Specification, request.PageNumber, request.PageSize, _mapper.ConfigurationProvider, cancellationToken);

return data;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using CleanArchitecture.Blazor.Application.Common.ExceptionHandlers;
using CleanArchitecture.Blazor.Application.Features.KeyValues.Caching;
using CleanArchitecture.Blazor.Application.Features.KeyValues.DTOs;
using CleanArchitecture.Blazor.Domain.Enums;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using CleanArchitecture.Blazor.Application.Features.Documents.DTOs;
using CleanArchitecture.Blazor.Application.Features.Documents.Queries.PaginationQuery;
using CleanArchitecture.Blazor.Application.Features.KeyValues.Caching;
using CleanArchitecture.Blazor.Application.Features.KeyValues.DTOs;
using CleanArchitecture.Blazor.Domain.Enums;
Expand Down Expand Up @@ -38,9 +36,8 @@ IMapper mapper
public async Task<PaginatedData<KeyValueDto>> Handle(KeyValuesWithPaginationQuery request,
CancellationToken cancellationToken)
{
var data = await _context.KeyValues.Specify(request.Specification)
.ProjectTo<KeyValueDto>(_mapper.ConfigurationProvider)
.PaginatedDataAsync(request.PageNumber, request.PageSize);
var data = await _context.KeyValues.OrderBy($"{request.OrderBy} {request.SortDirection}")
.ProjectToPaginatedDataAsync<KeyValue, KeyValueDto>(request.Specification, request.PageNumber, request.PageSize, _mapper.ConfigurationProvider, cancellationToken);

return data;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using CleanArchitecture.Blazor.Application.Features.Documents.Queries.PaginationQuery;
using CleanArchitecture.Blazor.Application.Features.Loggers.Caching;
using CleanArchitecture.Blazor.Application.Features.Loggers.DTOs;
using CleanArchitecture.Blazor.Application.Features.Products.Queries.Specification;
using CleanArchitecture.Blazor.Domain.Entities.Logger;

namespace CleanArchitecture.Blazor.Application.Features.Loggers.Queries.PaginationQuery;
Expand Down Expand Up @@ -39,10 +37,8 @@ IMapper mapper
public async Task<PaginatedData<LogDto>> Handle(LogsWithPaginationQuery request,
CancellationToken cancellationToken)
{
var data = await _context.Loggers.Specify(request.Specification)
.ProjectTo<LogDto>(_mapper.ConfigurationProvider)
.PaginatedDataAsync(request.PageNumber, request.PageSize);

var data = await _context.Loggers.OrderBy($"{request.OrderBy} {request.SortDirection}")
.ProjectToPaginatedDataAsync<Logger, LogDto>(request.Specification, request.PageNumber, request.PageSize, _mapper.ConfigurationProvider, cancellationToken);
return data;
}
}
Expand Down
3 changes: 0 additions & 3 deletions src/Application/Features/Products/Caching/ProductCacheKey.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.


using DocumentFormat.OpenXml.Spreadsheet;

namespace CleanArchitecture.Blazor.Application.Features.Products.Caching;

public static class ProductCacheKey
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
// The .NET Foundation licenses this file to you under the MIT license.


using CleanArchitecture.Blazor.Application.Common.ExceptionHandlers;
using CleanArchitecture.Blazor.Application.Features.Products.Caching;
using CleanArchitecture.Blazor.Application.Features.Products.DTOs;
using Microsoft.AspNetCore.Components.Forms;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,9 @@ IStringLocalizer<ExportProductsQueryHandler> localizer

public async Task<Result<byte[]>> Handle(ExportProductsQuery request, CancellationToken cancellationToken)
{
var data = await _context.Products.Specify(request.Specification)
.ProjectTo<ProductDto>(_mapper.ConfigurationProvider)
var data = await _context.Products.ApplySpecification(request.Specification)
.AsNoTracking()
.ProjectTo<ProductDto>(_mapper.ConfigurationProvider)
.ToListAsync(cancellationToken);


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,9 @@ IStringLocalizer<ProductsWithPaginationQueryHandler> localizer
public async Task<PaginatedData<ProductDto>> Handle(ProductsWithPaginationQuery request,
CancellationToken cancellationToken)
{

var data = await _context.Products.Specify(request.Specification)
.OrderBy($"{request.OrderBy} {request.SortDirection}")
.ProjectTo<ProductDto>(_mapper.ConfigurationProvider)
.PaginatedDataAsync(request.PageNumber, request.PageSize);

var data = await _context.Products.OrderBy($"{request.OrderBy} {request.SortDirection}")
.ProjectToPaginatedDataAsync<Product,ProductDto>(request.Specification, request.PageNumber, request.PageSize, _mapper.ConfigurationProvider, cancellationToken);
return data;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,7 @@ public SearchProductSpecification(ProductsWithPaginationQuery query)
.Where(product => product.CreatedBy == query.CurrentUser.UserId, query.ListView == ProductListView.My)
.Where(product => product.Created >= start && product.Created <= end, query.ListView == ProductListView.CreatedToday)
.Where(product => product.Created >= last30day, query.ListView == ProductListView.Created30Days);


}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ public override string ToString()
{
return $"Search:{Keyword},OrderBy:{OrderBy} {SortDirection},{PageNumber},{PageSize}";
}
public TenantsPaginationSpecification Specification => new TenantsPaginationSpecification(this);
}

public class TenantsWithPaginationQueryHandler :
Expand All @@ -38,9 +39,17 @@ IStringLocalizer<TenantsWithPaginationQueryHandler> localizer
public async Task<PaginatedData<TenantDto>> Handle(TenantsWithPaginationQuery request,
CancellationToken cancellationToken)
{
var data = await _context.Tenants.Where(x=>x.Name.Contains(request.Keyword) || x.Description.Contains(request.Keyword))
.ProjectTo<TenantDto>(_mapper.ConfigurationProvider)
.PaginatedDataAsync(request.PageNumber, request.PageSize);
var data = await _context.Tenants.OrderBy($"{request.OrderBy} {request.SortDirection}")
.ProjectToPaginatedDataAsync<Tenant, TenantDto>(request.Specification, request.PageNumber, request.PageSize, _mapper.ConfigurationProvider, cancellationToken);
return data;
}
}

public class TenantsPaginationSpecification : Specification<Tenant>
{
public TenantsPaginationSpecification(TenantsWithPaginationQuery query)
{
Query.Where(q => q.Name != null)
.Where(q => q.Name.Contains(query.Keyword) || q.Description.Contains(query.Keyword), !string.IsNullOrEmpty(query.Keyword));
}
}
2 changes: 0 additions & 2 deletions src/Application/_Imports.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,8 @@
global using Microsoft.Extensions.Logging;
global using Microsoft.Extensions.Primitives;
global using Microsoft.Extensions.Caching.Memory;
global using CleanArchitecture.Blazor.Domain;
global using CleanArchitecture.Blazor.Domain.Entities;
global using CleanArchitecture.Blazor.Domain.Events;
global using CleanArchitecture.Blazor.Application.Common.Mappings;
global using CleanArchitecture.Blazor.Application.Common.Models;
global using CleanArchitecture.Blazor.Application.Common.Extensions;
global using CleanArchitecture.Blazor.Application.Common.Interfaces;
Expand Down

0 comments on commit 3773c43

Please sign in to comment.