Browse Source

feat: add IQueryable DTO projection support to read-only application services

pull/26016/head
Mohamed Nazem 24 hours ago
committed by GitHub
parent
commit
a07484570f
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 50
      framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/AbstractKeyReadOnlyAppService.cs
  2. 11
      framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/ReadOnlyAppService.cs
  3. 8
      framework/src/Volo.Abp.ObjectMapping/Volo/Abp/ObjectMapping/IQueryProjectionMapper.cs
  4. 7
      framework/src/Volo.Abp.ObjectMapping/Volo/Abp/ObjectMapping/QueryProjectionMapper.cs

50
framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/AbstractKeyReadOnlyAppService.cs

@ -44,6 +44,18 @@ public abstract class AbstractKeyReadOnlyAppService<TEntity, TGetOutputDto, TGet
protected virtual string? GetListPolicyName { get; set; }
protected virtual IQueryProjectionMapper<TEntity, TGetOutputDto>? ObjectProjectionMapper =>
LazyServiceProvider.LazyGetService<IQueryProjectionMapper<TEntity, TGetOutputDto>>();
protected virtual IQueryProjectionMapper<TEntity, TGetListOutputDto>? ListProjectionMapper =>
LazyServiceProvider.LazyGetService<IQueryProjectionMapper<TEntity, TGetListOutputDto>>();
protected virtual bool UseObjectProjectionMapper =>
ObjectProjectionMapper != null;
protected virtual bool UseListProjectionMapper =>
ListProjectionMapper != null;
protected AbstractKeyReadOnlyAppService(IReadOnlyRepository<TEntity> repository)
{
ReadOnlyRepository = repository;
@ -53,6 +65,19 @@ public abstract class AbstractKeyReadOnlyAppService<TEntity, TGetOutputDto, TGet
{
await CheckGetPolicyAsync();
var projectionMapper = ObjectProjectionMapper;
if (UseObjectProjectionMapper && projectionMapper != null)
{
var query = await GetEntityByIdQueryAsync(id);
var dto =
await AsyncExecuter.FirstOrDefaultAsync(projectionMapper.ProjectTo(query))
?? throw new EntityNotFoundException(typeof(TEntity), id);
return dto;
}
var entity = await GetEntityByIdAsync(id);
return await MapToGetOutputDtoAsync(entity);
@ -65,7 +90,6 @@ public abstract class AbstractKeyReadOnlyAppService<TEntity, TGetOutputDto, TGet
var query = await CreateFilteredQueryAsync(input);
var totalCount = await AsyncExecuter.CountAsync(query);
var entities = new List<TEntity>();
var entityDtos = new List<TGetListOutputDto>();
if (totalCount > 0)
@ -73,8 +97,19 @@ public abstract class AbstractKeyReadOnlyAppService<TEntity, TGetOutputDto, TGet
query = ApplySorting(query, input);
query = ApplyPaging(query, input);
entities = await AsyncExecuter.ToListAsync(query);
entityDtos = await MapToGetListOutputDtosAsync(entities);
var projectionMapper = ListProjectionMapper;
if (UseListProjectionMapper && projectionMapper != null)
{
entityDtos = await AsyncExecuter.ToListAsync(
projectionMapper.ProjectTo(query)
);
}
else
{
var entities = await AsyncExecuter.ToListAsync(query);
entityDtos = await MapToGetListOutputDtosAsync(entities);
}
}
return new PagedResultDto<TGetListOutputDto>(
@ -85,6 +120,13 @@ public abstract class AbstractKeyReadOnlyAppService<TEntity, TGetOutputDto, TGet
protected abstract Task<TEntity> GetEntityByIdAsync(TKey id);
protected virtual Task<IQueryable<TEntity>> GetEntityByIdQueryAsync(TKey id)
{
throw new NotImplementedException(
"Override this method to create the query used for getting an entity by id."
);
}
protected virtual async Task CheckGetPolicyAsync()
{
await CheckPolicyAsync(GetPolicyName);
@ -227,4 +269,4 @@ public abstract class AbstractKeyReadOnlyAppService<TEntity, TGetOutputDto, TGet
{
return ObjectMapper.Map<TEntity, TGetListOutputDto>(entity);
}
}
}

11
framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/ReadOnlyAppService.cs

@ -37,7 +37,7 @@ public abstract class ReadOnlyAppService<TEntity, TGetOutputDto, TGetListOutputD
protected IReadOnlyRepository<TEntity, TKey> Repository { get; }
protected ReadOnlyAppService(IReadOnlyRepository<TEntity, TKey> repository)
: base(repository)
: base(repository)
{
Repository = repository;
}
@ -47,6 +47,13 @@ public abstract class ReadOnlyAppService<TEntity, TGetOutputDto, TGetListOutputD
return await Repository.GetAsync(id);
}
protected override async Task<IQueryable<TEntity>> GetEntityByIdQueryAsync(TKey id)
{
var query = await Repository.GetQueryableAsync();
return query.Where(e => e.Id.Equals(id));
}
protected override IQueryable<TEntity> ApplyDefaultSorting(IQueryable<TEntity> query)
{
if (typeof(TEntity).IsAssignableTo<ICreationAuditedObject>())
@ -58,4 +65,4 @@ public abstract class ReadOnlyAppService<TEntity, TGetOutputDto, TGetListOutputD
return query.OrderByDescending(e => e.Id);
}
}
}
}

8
framework/src/Volo.Abp.ObjectMapping/Volo/Abp/ObjectMapping/IQueryProjectionMapper.cs

@ -0,0 +1,8 @@
using System.Linq;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.ObjectMapping;
public interface IQueryProjectionMapper<TSource, TDestination> : ITransientDependency
{
IQueryable<TDestination> ProjectTo(IQueryable<TSource> source);
}

7
framework/src/Volo.Abp.ObjectMapping/Volo/Abp/ObjectMapping/QueryProjectionMapper.cs

@ -0,0 +1,7 @@
using System.Linq;
namespace Volo.Abp.ObjectMapping;
public abstract class QueryProjectionMapper<TSource, TDestination> : IQueryProjectionMapper<TSource, TDestination>
{
public abstract IQueryable<TDestination> ProjectTo(IQueryable<TSource> source);
}
Loading…
Cancel
Save