Browse Source

Assert the projected SQL and fix the Mapperly sample in the docs

* RequiredMappingStrategy.Target avoids an RMG020 warning per unmapped entity property
pull/26016/head
maliming 12 hours ago
parent
commit
f00d7df56c
No known key found for this signature in database GPG Key ID: A646B9CB645ECEA4
  1. 4
      docs/en/framework/architecture/domain-driven-design/application-services.md
  2. 22
      framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/Applications/QueryProjection_Tests.cs
  3. 2
      framework/test/Volo.Abp.Mapperly.Tests/Volo/Abp/Mapperly/SampleClasses/MyEntityQueryProjector.cs

4
docs/en/framework/architecture/domain-driven-design/application-services.md

@ -485,13 +485,15 @@ public class BookProjector : IQueryProjectionMapper<Book, BookDto>
[Mapperly](https://mapperly.riok.app/) can generate that method for you:
````csharp
[Mapper]
[Mapper(RequiredMappingStrategy = RequiredMappingStrategy.Target)]
public partial class BookProjector : IQueryProjectionMapper<Book, BookDto>
{
public partial IQueryable<BookDto> ProjectTo(IQueryable<Book> source);
}
````
> `RequiredMappingStrategy.Target` tells Mapperly to only require the DTO members to be mapped. Without it, it reports a warning for every entity property that the DTO doesn't have.
ABP registers the projection mappers by convention, you don't need to configure anything else. Filters (like soft delete and multi-tenancy), sorting and paging are still applied to the query before the projection.
> The projection replaces the entity based extension points. `GetAsync` doesn't use `GetEntityByIdAsync` and `MapToGetOutputDtoAsync`, `GetListAsync` doesn't use `MapToGetListOutputDtosAsync` anymore. If an application service needs to keep using them, override the `GetProjectionMapper` or `GetListProjectionMapper` property and return `null`:

22
framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/Applications/QueryProjection_Tests.cs

@ -1,9 +1,12 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Shouldly;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Domain.Entities;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.ObjectMapping;
using Volo.Abp.TestApp;
using Volo.Abp.TestApp.Domain;
using Xunit;
@ -63,4 +66,23 @@ public class QueryProjection_Tests : EntityFrameworkCoreTestBase
result.Items.ShouldNotContain(x => x.Id == TestDataBuilder.UserJohnDeletedId);
}
[Fact]
public async Task Should_Only_Select_The_Projected_Columns()
{
await WithUnitOfWorkAsync(async () =>
{
var repository = GetRequiredService<IReadOnlyRepository<Person, Guid>>();
var projector = GetRequiredService<IQueryProjectionMapper<Person, PersonProjectionDto>>();
var sql = projector.ProjectTo(await repository.GetQueryableAsync()).ToQueryString();
sql.ShouldContain("\"Name\"");
sql.ShouldNotContain("\"Birthday\"");
sql.ShouldNotContain("\"ExtraProperties\"");
//the data filters are still a part of the query
sql.ShouldContain("Is_Deleted");
});
}
}

2
framework/test/Volo.Abp.Mapperly.Tests/Volo/Abp/Mapperly/SampleClasses/MyEntityQueryProjector.cs

@ -4,7 +4,7 @@ using Volo.Abp.ObjectMapping;
namespace Volo.Abp.Mapperly.SampleClasses;
[Mapper]
[Mapper(RequiredMappingStrategy = RequiredMappingStrategy.Target)]
public partial class MyEntityQueryProjector : IQueryProjectionMapper<MyEntity, MyEntityDto>
{
public partial IQueryable<MyEntityDto> ProjectTo(IQueryable<MyEntity> source);

Loading…
Cancel
Save