Skip to content

Commit d4ccb94

Browse files
authored
feat: ✨ migrate customers-service to use mapperly for mapping (#236)
1 parent 75c7fa9 commit d4ccb94

File tree

70 files changed

+494
-540
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

70 files changed

+494
-540
lines changed

readme.md

+1-2
Original file line numberDiff line numberDiff line change
@@ -106,8 +106,7 @@ Thanks a bunch for supporting me!
106106
- ✔️ **[`Microsoft.AspNetCore.Authentication.JwtBearer`](https://www.nuget.org/packages/Microsoft.AspNetCore.Authentication.JwtBearer)** - Handling Jwt Authentication and authorization in .Net Core
107107
- ✔️ **[`NSubstitute`](https://github.com/nsubstitute/NSubstitute)** - A friendly substitute for .NET mocking libraries.
108108
- ✔️ **[`StyleCopAnalyzers`](https://github.com/DotNetAnalyzers/StyleCopAnalyzers)** - An implementation of StyleCop rules using the .NET Compiler Platform
109-
- ✔️ **[`AutoMapper`](https://github.com/AutoMapper/AutoMapper)** - Convention-based object-object mapper in .NET.
110-
- ✔️ **[`Hellang.Middleware.ProblemDetails`](https://github.com/khellang/Middleware/tree/master/src/ProblemDetails)** - A middleware for handling exception in .Net Core
109+
- ✔️ **[`Mapperly`](https://github.com/riok/mapperly)** - A .NET source generator for generating object mappings, No runtime reflection.
111110
- ✔️ **[`IdGen`](https://github.com/RobThree/IdGen)** - Twitter Snowflake-alike ID generator for .Net
112111

113112
## Setup

src/BuildingBlocks/BuildingBlocks.Abstractions/Persistence/IRepository.cs

+9
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,15 @@ Task<IPageList<TResult>> GetByPageFilter<TResult, TSortKey>(
4646
CancellationToken cancellationToken = default
4747
)
4848
where TResult : class;
49+
50+
Task<IPageList<TResult>> GetByPageFilter<TResult, TSortKey>(
51+
IPageRequest pageRequest,
52+
Func<IQueryable<TEntity>, IQueryable<TResult>> projectionFunc,
53+
Expression<Func<TEntity, TSortKey>> sortExpression,
54+
Expression<Func<TEntity, bool>>? predicate = null,
55+
CancellationToken cancellationToken = default
56+
)
57+
where TResult : class;
4958
}
5059

5160
public interface IWriteRepository<TEntity, in TId>

src/BuildingBlocks/BuildingBlocks.Abstractions/Web/MinimalApi/IHttpCommand.cs

-2
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,12 @@ public interface IHttpCommand<TRequest>
1313
TRequest Request { get; init; }
1414
HttpContext HttpContext { get; init; }
1515
ICommandBus CommandBus { get; init; }
16-
IMapper Mapper { get; init; }
1716
CancellationToken CancellationToken { get; init; }
1817
}
1918

2019
public interface IHttpCommand
2120
{
2221
HttpContext HttpContext { get; init; }
2322
ICommandBus CommandBus { get; init; }
24-
IMapper Mapper { get; init; }
2523
CancellationToken CancellationToken { get; init; }
2624
}

src/BuildingBlocks/BuildingBlocks.Abstractions/Web/MinimalApi/IHttpQuery.cs

-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,5 @@ public interface IHttpQuery
1212
{
1313
HttpContext HttpContext { get; init; }
1414
IQueryBus QueryBus { get; init; }
15-
IMapper Mapper { get; init; }
1615
CancellationToken CancellationToken { get; init; }
1716
}

src/BuildingBlocks/BuildingBlocks.Core/Extensions/QueryableExtensions.cs

+67-7
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
namespace BuildingBlocks.Core.Extensions;
1010

11-
// we should not operation related to Ef or Mongo here and we should design as general with IQueryable to work with any providers
11+
// we should not relate to Ef or Mongo here, and we should design as general with IQueryable to work with any providers
1212
public static class QueryableExtensions
1313
{
1414
public static async Task<IPageList<TEntity>> ApplyPagingAsync<TEntity>(
@@ -30,7 +30,7 @@ CancellationToken cancellationToken
3030
// https://github.com/Biarity/Sieve/issues/34#issuecomment-403817573
3131
var result = sieveProcessor.Apply(sieveModel, queryable, applyPagination: false);
3232
var total = result.Count();
33-
result = sieveProcessor.Apply(sieveModel, queryable, applyFiltering: false, applySorting: false); // Only
33+
result = sieveProcessor.Apply(sieveModel, queryable, applyFiltering: false, applySorting: false);
3434

3535
var items = await result.ToAsyncEnumerable().ToListAsync(cancellationToken: cancellationToken);
3636

@@ -40,8 +40,38 @@ CancellationToken cancellationToken
4040
public static async Task<IPageList<TResult>> ApplyPagingAsync<TEntity, TResult>(
4141
this IQueryable<TEntity> queryable,
4242
IPageRequest pageRequest,
43+
ISieveProcessor sieveProcessor,
4344
IConfigurationProvider configurationProvider,
45+
CancellationToken cancellationToken
46+
)
47+
where TEntity : class
48+
where TResult : class
49+
{
50+
var sieveModel = new SieveModel
51+
{
52+
PageSize = pageRequest.PageSize,
53+
Page = pageRequest.PageNumber,
54+
Sorts = pageRequest.SortOrder,
55+
Filters = pageRequest.Filters
56+
};
57+
58+
// https://github.com/Biarity/Sieve/issues/34#issuecomment-403817573
59+
var result = sieveProcessor.Apply(sieveModel, queryable, applyPagination: false);
60+
var total = result.Count();
61+
result = sieveProcessor.Apply(sieveModel, queryable, applyFiltering: false, applySorting: false); // Only applies pagination
62+
63+
var projectedQuery = result.ProjectTo<TResult>(configurationProvider);
64+
65+
var items = await projectedQuery.ToAsyncEnumerable().ToListAsync(cancellationToken: cancellationToken);
66+
67+
return PageList<TResult>.Create(items.AsReadOnly(), pageRequest.PageNumber, pageRequest.PageSize, total);
68+
}
69+
70+
public static async Task<IPageList<TResult>> ApplyPagingAsync<TEntity, TResult>(
71+
this IQueryable<TEntity> queryable,
72+
IPageRequest pageRequest,
4473
ISieveProcessor sieveProcessor,
74+
Func<IQueryable<TEntity>, IQueryable<TResult>> projectionFunc,
4575
CancellationToken cancellationToken
4676
)
4777
where TEntity : class
@@ -60,14 +90,44 @@ CancellationToken cancellationToken
6090
var total = result.Count();
6191
result = sieveProcessor.Apply(sieveModel, queryable, applyFiltering: false, applySorting: false); // Only applies pagination
6292

63-
var items = await result
64-
.ProjectTo<TResult>(configurationProvider)
65-
.ToAsyncEnumerable()
66-
.ToListAsync(cancellationToken: cancellationToken);
93+
var projectedQuery = projectionFunc(result);
94+
95+
var items = await projectedQuery.ToAsyncEnumerable().ToListAsync(cancellationToken: cancellationToken);
6796

6897
return PageList<TResult>.Create(items.AsReadOnly(), pageRequest.PageNumber, pageRequest.PageSize, total);
6998
}
7099

100+
public static async Task<IPageList<TResult>> ApplyPagingAsync<TEntity, TResult, TSortKey>(
101+
this IQueryable<TEntity> collection,
102+
IPageRequest pageRequest,
103+
ISieveProcessor sieveProcessor,
104+
Func<IQueryable<TEntity>, IQueryable<TResult>> projectionFunc,
105+
Expression<Func<TEntity, bool>>? predicate = null,
106+
Expression<Func<TEntity, TSortKey>>? sortExpression = null,
107+
CancellationToken cancellationToken = default
108+
)
109+
where TEntity : class
110+
where TResult : class
111+
{
112+
IQueryable<TEntity> query = collection;
113+
if (predicate is not null)
114+
{
115+
query = query.Where(predicate);
116+
}
117+
118+
if (sortExpression is not null)
119+
{
120+
query = query.OrderByDescending(sortExpression);
121+
}
122+
123+
return await query.ApplyPagingAsync<TEntity, TResult>(
124+
pageRequest,
125+
sieveProcessor,
126+
projectionFunc,
127+
cancellationToken
128+
);
129+
}
130+
71131
public static async Task<IPageList<TResult>> ApplyPagingAsync<TEntity, TResult>(
72132
this IQueryable<TEntity> queryable,
73133
IPageRequest pageRequest,
@@ -124,8 +184,8 @@ public static async Task<IPageList<TResult>> ApplyPagingAsync<TEntity, TResult,
124184

125185
return await query.ApplyPagingAsync<TEntity, TResult>(
126186
pageRequest,
127-
configuration,
128187
sieveProcessor,
188+
configuration,
129189
cancellationToken
130190
);
131191
}

src/BuildingBlocks/BuildingBlocks.Core/Persistence/EfCore/EfRepositoryBase.cs

+19
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,25 @@ public async Task<IPageList<TResult>> GetByPageFilter<TResult, TSortKey>(
105105
);
106106
}
107107

108+
public async Task<IPageList<TResult>> GetByPageFilter<TResult, TSortKey>(
109+
IPageRequest pageRequest,
110+
Func<IQueryable<TEntity>, IQueryable<TResult>> projectionFunc,
111+
Expression<Func<TEntity, TSortKey>> sortExpression,
112+
Expression<Func<TEntity, bool>>? predicate = null,
113+
CancellationToken cancellationToken = default
114+
)
115+
where TResult : class
116+
{
117+
return await DbSet.ApplyPagingAsync<TEntity, TResult, TSortKey>(
118+
pageRequest,
119+
sieveProcessor,
120+
projectionFunc,
121+
predicate,
122+
sortExpression,
123+
cancellationToken
124+
);
125+
}
126+
108127
public async Task<TEntity> AddAsync(TEntity entity, CancellationToken cancellationToken = default)
109128
{
110129
entity.NotBeNull();

src/BuildingBlocks/BuildingBlocks.Persistence.Mongo/MongoRepositoryBase.cs

+21
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,27 @@ public async Task<IPageList<TResult>> GetByPageFilter<TResult, TSortKey>(
123123
);
124124
}
125125

126+
public async Task<IPageList<TResult>> GetByPageFilter<TResult, TSortKey>(
127+
IPageRequest pageRequest,
128+
Func<IQueryable<TEntity>, IQueryable<TResult>> projectionFunc,
129+
Expression<Func<TEntity, TSortKey>> sortExpression,
130+
Expression<Func<TEntity, bool>>? predicate = null,
131+
CancellationToken cancellationToken = default
132+
)
133+
where TResult : class
134+
{
135+
return await DbSet
136+
.AsQueryable()
137+
.ApplyPagingAsync<TEntity, TResult, TSortKey>(
138+
pageRequest,
139+
_sieveProcessor,
140+
projectionFunc,
141+
predicate,
142+
sortExpression,
143+
cancellationToken
144+
);
145+
}
146+
126147
public Task<TEntity> AddAsync(TEntity entity, CancellationToken cancellationToken = default)
127148
{
128149
Context.AddCommand(async () =>

src/BuildingBlocks/BuildingBlocks.Web/Minimal/Extensions/EndpointRouteBuilderExtensions.cs

+4-11
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
using BuildingBlocks.Abstractions.Commands;
22
using BuildingBlocks.Abstractions.Queries;
33
using BuildingBlocks.Abstractions.Web.MinimalApi;
4-
using BuildingBlocks.Web.Problem.HttpResults;
54
using Humanizer;
6-
using MediatR;
75
using Microsoft.AspNetCore.Builder;
86
using Microsoft.AspNetCore.Http;
97
using Microsoft.AspNetCore.Http.HttpResults;
@@ -97,8 +95,8 @@ async Task<IResult> Handle([AsParameters] HttpCommand<TRequest> requestParameter
9795
public static RouteHandlerBuilder MapQueryEndpoint<TRequestParameters, TResponse, TQuery, TQueryResult>(
9896
this IEndpointRouteBuilder builder,
9997
string pattern,
100-
Func<TRequestParameters, TQuery>? mapRequestToQuery = null,
101-
Func<TQueryResult, TResponse>? mapQueryResultToResponse = null
98+
Func<TRequestParameters, TQuery> mapRequestToQuery,
99+
Func<TQueryResult, TResponse> mapQueryResultToResponse
102100
)
103101
where TRequestParameters : IHttpQuery
104102
where TResponse : class
@@ -115,18 +113,13 @@ public static RouteHandlerBuilder MapQueryEndpoint<TRequestParameters, TResponse
115113
async Task<Ok<TResponse>> Handle([AsParameters] TRequestParameters requestParameters)
116114
{
117115
var queryProcessor = requestParameters.QueryBus;
118-
var mapper = requestParameters.Mapper;
119116
var cancellationToken = requestParameters.CancellationToken;
120117

121-
var query = mapRequestToQuery is not null
122-
? mapRequestToQuery(requestParameters)
123-
: mapper.Map<TQuery>(requestParameters);
118+
var query = mapRequestToQuery(requestParameters);
124119

125120
var res = await queryProcessor.SendAsync(query, cancellationToken);
126121

127-
var response = mapQueryResultToResponse is not null
128-
? mapQueryResultToResponse(res)
129-
: mapper.Map<TResponse>(res);
122+
var response = mapQueryResultToResponse(res);
130123

131124
// https://learn.microsoft.com/en-us/aspnet/core/fundamentals/minimal-apis/responses
132125
// https://learn.microsoft.com/en-us/aspnet/core/fundamentals/minimal-apis/openapi?view=aspnetcore-7.0#multiple-response-types

src/Services/Catalogs/FoodDelivery.Services.Catalogs/Products/Features/GettingProducts/v1/GetProducts.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,8 @@ public async Task<GetProductsResult> Handle(GetProducts request, CancellationTok
6262
.AsNoTracking()
6363
.ApplyPagingAsync<Product, ProductReadModel>(
6464
request,
65-
mapper.ConfigurationProvider,
6665
sieveProcessor,
66+
mapper.ConfigurationProvider,
6767
cancellationToken: cancellationToken
6868
);
6969

src/Services/Customers/FoodDelivery.Services.Customers/Customers/CustomersMapping.cs

-119
This file was deleted.

0 commit comments

Comments
 (0)