abpframework / abpframework/abp
REPR Pattern | CreateAppService, UpdateAppService etc. instead of CrudAppService
@maliming is already working on this.
Since Sep 17, 2026.
- Dominant language
- C#
- Stars
- 14.4k
- Forks
- 3.7k
- Avg merge
- 15h 32m
- Merged PRs (30d)
- 106
Description
Is there an existing issue for this?
- I have searched the existing issues
Is your feature request related to a problem? Please describe the problem.
I had the need to create a no logic CRUD APIs, and I normally used CrudAppService.
But I had the case to just make a single Api out of them, but I found out it was not flexable, it's CRUD or non
Describe the solution you'd like
I suggest we separate it through (Create/Update/Delete/Get/GetList/GetPaged) service, that would give the dev the flexability to make 1 no logic Create API, maybe there is no Delete API
Solution would be making the chain of AbstractKeyCrudAppService->CrudAppService use composition to composite the needed apis to not break any changes so it stays the same but each api has its separate class,
so users can easily do the described flow
Additional context
using Volo.Abp.Application.Services;
using Volo.Abp.Domain.Entities;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.MultiTenancy;
public class CreateAppService<TEntity, TEntityDto>(
IRepository<TEntity> repository)
: CreateAppService<TEntity, TEntityDto, TEntityDto>(repository)
where TEntity : class, IEntity;
public class CreateAppService<TEntity, TInput, TOutput>(
IRepository<TEntity> repository)
: ApplicationService, ICreateAppService<TOutput, TInput>
where TEntity : class, IEntity
{
protected IRepository<TEntity> Repository { get; } = repository;
protected virtual string? CreatePolicyName { get; set; }
public async Task<TOutput> CreateAsync(TInput input)
{
await CheckCreatePolicyAsync();
var entity = await MapToEntityAsync(input);
SetIdForGuids(entity);
TryToSetTenantId(entity);
await PersistAsync(entity);
return await MapToOutputAsync(entity);
}
protected virtual Task CheckCreatePolicyAsync()
{
return CheckPolicyAsync(CreatePolicyName);
}
protected virtual Task<TEntity> MapToEntityAsync(TInput input)
{
return Task.FromResult(MapToEntity(input));
}
protected virtual TEntity MapToEntity(TInput input)
{
return ObjectMapper.Map<TInput, TEntity>(input);
}
protected virtual void SetIdForGuids(TEntity entity)
{
if (entity is IEntity<Guid> entityWithGuidId &&
entityWithGuidId.Id == Guid.Empty)
{
EntityHelper.TrySetId(
entityWithGuidId,
GuidGenerator.Create,
true);
}
}
protected virtual void TryToSetTenantId(TEntity entity)
{
if (entity is not IMultiTenant)
{
return;
}
var tenantId = CurrentTenant.Id;
if (!tenantId.HasValue)
{
return;
}
var propertyInfo = entity
.GetType()
.GetProperty(nameof(IMultiTenant.TenantId));
if (propertyInfo?.GetSetMethod(true) == null)
{
return;
}
propertyInfo.SetValue(entity, tenantId);
}
protected virtual Task PersistAsync(TEntity entity)
{
return Repository.InsertAsync(entity, autoSave: true);
}
protected virtual Task<TOutput> MapToOutputAsync(TEntity entity)
{
return Task.FromResult(MapToOutput(entity));
}
protected virtual TOutput MapToOutput(TEntity entity)
{
return ObjectMapper.Map<TEntity, TOutput>(entity);
}
}
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.