Skip to content

Commit

Permalink
Implemented rudimentary readonly appservices
Browse files Browse the repository at this point in the history
  • Loading branch information
NecatiMeral committed Jun 14, 2020
1 parent db2da83 commit 10c00be
Show file tree
Hide file tree
Showing 2 changed files with 230 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Dynamic.Core;
using System.Text;
using System.Threading.Tasks;

using Volo.Abp.Application.Dtos;
using Volo.Abp.Auditing;
using Volo.Abp.Domain.Entities;
using Volo.Abp.Domain.Repositories;

namespace Volo.Abp.Application.Services
{
public abstract class AbstractKeyReadOnlyAppService<TEntity, TEntityDto, TKey>
: AbstractKeyReadOnlyAppService<TEntity, TEntityDto, TEntityDto, TKey, PagedAndSortedResultRequestDto>
where TEntity : class, IEntity
{
protected AbstractKeyReadOnlyAppService(IRepository<TEntity> repository)
: base(repository)
{
}
}

public abstract class AbstractKeyReadOnlyAppService<TEntity, TEntityDto, TKey, TGetListInput>
: AbstractKeyReadOnlyAppService<TEntity, TEntityDto, TEntityDto, TKey, TGetListInput>
where TEntity : class, IEntity
{
protected AbstractKeyReadOnlyAppService(IRepository<TEntity> repository)
: base(repository)
{
}
}

public abstract class AbstractKeyReadOnlyAppService<TEntity, TGetOutputDto, TGetListOutputDto, TKey, TGetListInput>
: ApplicationService
, IReadOnlyAppService<TGetOutputDto, TGetListOutputDto, TKey, TGetListInput>
where TEntity : class, IEntity
{
protected IRepository<TEntity> Repository { get; }

protected virtual string GetPolicyName { get; set; }

protected virtual string GetListPolicyName { get; set; }

protected AbstractKeyReadOnlyAppService(IRepository<TEntity> repository)
{
Repository = repository;
}

public virtual async Task<TGetOutputDto> GetAsync(TKey id)
{
await CheckGetPolicyAsync();

var entity = await GetEntityByIdAsync(id);
return MapToGetOutputDto(entity);
}

public virtual async Task<PagedResultDto<TGetListOutputDto>> GetListAsync(TGetListInput input)
{
await CheckGetListPolicyAsync();

var query = CreateFilteredQuery(input);

var totalCount = await AsyncExecuter.CountAsync(query);

query = ApplySorting(query, input);
query = ApplyPaging(query, input);

var entities = await AsyncExecuter.ToListAsync(query);

return new PagedResultDto<TGetListOutputDto>(
totalCount,
entities.Select(MapToGetListOutputDto).ToList()
);
}

protected abstract Task<TEntity> GetEntityByIdAsync(TKey id);

protected virtual async Task CheckGetPolicyAsync()
{
await CheckPolicyAsync(GetPolicyName);
}

protected virtual async Task CheckGetListPolicyAsync()
{
await CheckPolicyAsync(GetListPolicyName);
}

/// <summary>
/// Should apply sorting if needed.
/// </summary>
/// <param name="query">The query.</param>
/// <param name="input">The input.</param>
protected virtual IQueryable<TEntity> ApplySorting(IQueryable<TEntity> query, TGetListInput input)
{
//Try to sort query if available
if (input is ISortedResultRequest sortInput)
{
if (!sortInput.Sorting.IsNullOrWhiteSpace())
{
return query.OrderBy(sortInput.Sorting);
}
}

//IQueryable.Task requires sorting, so we should sort if Take will be used.
if (input is ILimitedResultRequest)
{
return ApplyDefaultSorting(query);
}

//No sorting
return query;
}

/// <summary>
/// Applies sorting if no sorting specified but a limited result requested.
/// </summary>
/// <param name="query">The query.</param>
protected virtual IQueryable<TEntity> ApplyDefaultSorting(IQueryable<TEntity> query)
{
if (typeof(TEntity).IsAssignableTo<ICreationAuditedObject>())
{
return query.OrderByDescending(e => ((ICreationAuditedObject)e).CreationTime);
}

throw new AbpException("No sorting specified but this query requires sorting. Override the ApplyDefaultSorting method for your application service derived from AbstractKeyCrudAppService!");
}

/// <summary>
/// Should apply paging if needed.
/// </summary>
/// <param name="query">The query.</param>
/// <param name="input">The input.</param>
protected virtual IQueryable<TEntity> ApplyPaging(IQueryable<TEntity> query, TGetListInput input)
{
//Try to use paging if available
if (input is IPagedResultRequest pagedInput)
{
return query.PageBy(pagedInput);
}

//Try to limit query result if available
if (input is ILimitedResultRequest limitedInput)
{
return query.Take(limitedInput.MaxResultCount);
}

//No paging
return query;
}

/// <summary>
/// This method should create <see cref="IQueryable{TEntity}"/> based on given input.
/// It should filter query if needed, but should not do sorting or paging.
/// Sorting should be done in <see cref="ApplySorting"/> and paging should be done in <see cref="ApplyPaging"/>
/// methods.
/// </summary>
/// <param name="input">The input.</param>
protected virtual IQueryable<TEntity> CreateFilteredQuery(TGetListInput input)
{
return Repository;
}

/// <summary>
/// Maps <see cref="TEntity"/> to <see cref="TGetOutputDto"/>.
/// It uses <see cref="IObjectMapper"/> by default.
/// It can be overriden for custom mapping.
/// </summary>
protected virtual TGetOutputDto MapToGetOutputDto(TEntity entity)
{
return ObjectMapper.Map<TEntity, TGetOutputDto>(entity);
}

/// <summary>
/// Maps <see cref="TEntity"/> to <see cref="TGetListOutputDto"/>.
/// It uses <see cref="IObjectMapper"/> by default.
/// It can be overriden for custom mapping.
/// </summary>
protected virtual TGetListOutputDto MapToGetListOutputDto(TEntity entity)
{
return ObjectMapper.Map<TEntity, TGetListOutputDto>(entity);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Volo.Abp.Application.Dtos;
using Volo.Abp.Auditing;
using Volo.Abp.Domain.Entities;
using Volo.Abp.Domain.Repositories;

namespace Volo.Abp.Application.Services
{
public abstract class ReadOnlyAppService<TEntity, TGetOutputDto, TGetListOutputDto, TKey, TGetListInput>
: AbstractKeyReadOnlyAppService<TEntity, TGetOutputDto, TGetListOutputDto, TKey, TGetListInput>
where TEntity : class, IEntity<TKey>
where TGetOutputDto : IEntityDto<TKey>
where TGetListOutputDto : IEntityDto<TKey>
{
protected new IRepository<TEntity, TKey> Repository { get; }

protected ReadOnlyAppService(IRepository<TEntity, TKey> repository)
: base(repository)
{
Repository = repository;
}

protected override async Task<TEntity> GetEntityByIdAsync(TKey id)
{
return await Repository.GetAsync(id);
}

protected override IQueryable<TEntity> ApplyDefaultSorting(IQueryable<TEntity> query)
{
if (typeof(TEntity).IsAssignableTo<ICreationAuditedObject>())
{
return query.OrderByDescending(e => ((ICreationAuditedObject)e).CreationTime);
}
else
{
return query.OrderByDescending(e => e.Id);
}
}
}
}

0 comments on commit 10c00be

Please sign in to comment.