You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ABP框架下基于抽象类的Step应用服务设计优化咨询

ABP框架下多步骤类型单AppService优化方案

问题背景

基于ABP框架开发应用时,定义了抽象实体类BaseStep,其派生类型包括:

  • Introduction
  • Regular
  • Navigation
  • Finish

由于各类型高度相似,采用EF Core鉴别器实现单表存储。当前使用单个StepAppService处理所有步骤实体,但创建时ABP无法自动识别具体实体类型,只能通过if-else分支判断StepType,将抽象的CreateUpdateBaseStepDto映射为对应具体创建Dto后调用Base的CreateAsync方法。这种实现方式冗余且扩展性差,需要更优雅的设计方案,同时保留单个ApplicationService的便利性。

当前核心实现代码:

public class StepAppService :
    CrudAppService<
        BaseStep,                        //抽象步骤实体
        BaseStepDto,                     //抽象传输Dto
        Guid,                            //实体主键
        PagedAndSortedResultRequestDto,  //分页排序请求Dto
        CreateUpdateBaseStepDto>,        //抽象创建/更新Dto
    IBaseStepAppService {

    // 其他代码.....
    
    public async Task<BaseStepDto> CreateByTypeAsync(CreateUpdateBaseStepDto input)
    {
        // 尝试给仓储传递明确的类型
        if(input.StepType == StepType.Introduction)
        {
            return await base.CreateAsync(ObjectMapper.Map<CreateUpdateBaseStepDto, CreateUpdateIntroductionStepDto>(input));
        }
        else if (input.StepType == StepType.Navigation)
        {
            return await base.CreateAsync(ObjectMapper.Map<CreateUpdateBaseStepDto, CreateUpdateNavigationStepDto>(input));
        }
        else if (input.StepType == StepType.Regular)
        {
            return await base.CreateAsync(ObjectMapper.Map<CreateUpdateBaseStepDto, CreateUpdateRegularStepDto>(input));
        }
        else if (input.StepType == StepType.Finish)
        {
            return await base.CreateAsync(ObjectMapper.Map<CreateUpdateBaseStepDto, CreateUpdateFinishStepDto>(input));
        }
        else
        {
            return await base.CreateAsync(ObjectMapper.Map<CreateUpdateBaseStepDto, CreateUpdateNavigationStepDto>(input));
        }
    }
}

优化方案

方案1:策略模式封装创建逻辑

将不同步骤类型的创建逻辑封装为独立策略类,消除分支判断,同时提升扩展性。

1. 定义策略接口

public interface IStepCreationStrategy
{
    StepType TargetType { get; }
    Task<BaseStepDto> CreateAsync(CreateUpdateBaseStepDto input, StepAppService appService);
}

2. 实现各类型策略类

以Introduction类型为例,其他类型同理实现:

public class IntroductionStepCreationStrategy : IStepCreationStrategy
{
    public StepType TargetType => StepType.Introduction;

    public async Task<BaseStepDto> CreateAsync(CreateUpdateBaseStepDto input, StepAppService appService)
    {
        var concreteDto = appService.ObjectMapper.Map<CreateUpdateBaseStepDto, CreateUpdateIntroductionStepDto>(input);
        return await appService.CreateAsync(concreteDto);
    }
}

3. 在AppService中注入并使用策略

public class StepAppService : CrudAppService<BaseStep, BaseStepDto, Guid, PagedAndSortedResultRequestDto, CreateUpdateBaseStepDto>, IBaseStepAppService
{
    private readonly Dictionary<StepType, IStepCreationStrategy> _creationStrategyMap;

    public StepAppService(
        IRepository<BaseStep, Guid> repository,
        IEnumerable<IStepCreationStrategy> creationStrategies) 
        : base(repository)
    {
        _creationStrategyMap = creationStrategies.ToDictionary(s => s.TargetType);
    }

    public async Task<BaseStepDto> CreateByTypeAsync(CreateUpdateBaseStepDto input)
    {
        if (_creationStrategyMap.TryGetValue(input.StepType, out var strategy))
        {
            return await strategy.CreateAsync(input, this);
        }
        // 默认处理逻辑
        var defaultDto = ObjectMapper.Map<CreateUpdateBaseStepDto, CreateUpdateNavigationStepDto>(input);
        return await CreateAsync(defaultDto);
    }
}

ABP会自动扫描并注册所有实现IStepCreationStrategy的类,无需手动注册。

方案2:反射+AutoMapper简化映射

如果不想引入过多策略类,可结合反射和AutoMapper直接创建具体Dto实例,简化分支逻辑:

public async Task<BaseStepDto> CreateByTypeAsync(CreateUpdateBaseStepDto input)
{
    // 根据StepType匹配对应Dto类型
    var targetDtoType = input.StepType switch
    {
        StepType.Introduction => typeof(CreateUpdateIntroductionStepDto),
        StepType.Regular => typeof(CreateUpdateRegularStepDto),
        StepType.Navigation => typeof(CreateUpdateNavigationStepDto),
        StepType.Finish => typeof(CreateUpdateFinishStepDto),
        _ => typeof(CreateUpdateNavigationStepDto)
    };

    // 映射为具体Dto
    var concreteDto = ObjectMapper.Map(input, typeof(CreateUpdateBaseStepDto), targetDtoType);
    
    // 反射调用CrudAppService的CreateAsync方法
    var createMethod = typeof(CrudAppService<BaseStep, BaseStepDto, Guid, PagedAndSortedResultRequestDto, CreateUpdateBaseStepDto>)
        .GetMethod(nameof(CreateAsync))!
        .MakeGenericMethod(targetDtoType);
    
    var result = await (Task<BaseStepDto>)createMethod.Invoke(this, new[] { concreteDto })!;
    return result;
}

该方案适合步骤类型数量较少的场景,代码简洁且无需额外类。

方案3:领域工厂下沉创建逻辑(DDD风格)

若希望遵循领域驱动设计,可将实体创建逻辑下沉到领域层,定义工厂类:

1. 定义领域工厂接口与实现

public interface IStepFactory
{
    BaseStep CreateFromDto(CreateUpdateBaseStepDto input);
}

public class StepFactory : IStepFactory
{
    private readonly IObjectMapper _objectMapper;

    public StepFactory(IObjectMapper objectMapper)
    {
        _objectMapper = objectMapper;
    }

    public BaseStep CreateFromDto(CreateUpdateBaseStepDto input)
    {
        return input.StepType switch
        {
            StepType.Introduction => _objectMapper.Map<CreateUpdateIntroductionStepDto, IntroductionStep>(input),
            StepType.Regular => _objectMapper.Map<CreateUpdateRegularStepDto, RegularStep>(input),
            StepType.Navigation => _objectMapper.Map<CreateUpdateNavigationStepDto, NavigationStep>(input),
            StepType.Finish => _objectMapper.Map<CreateUpdateFinishStepDto, FinishStep>(input),
            _ => _objectMapper.Map<CreateUpdateNavigationStepDto, NavigationStep>(input)
        };
    }
}

2. 在AppService中使用工厂

public class StepAppService : CrudAppService<...>, IBaseStepAppService
{
    private readonly IStepFactory _stepFactory;

    public StepAppService(
        IRepository<BaseStep, Guid> repository,
        IStepFactory stepFactory) 
        : base(repository)
    {
        _stepFactory = stepFactory;
    }

    public async Task<BaseStepDto> CreateByTypeAsync(CreateUpdateBaseStepDto input)
    {
        var step = _stepFactory.CreateFromDto(input);
        await Repository.InsertAsync(step);
        await CurrentUnitOfWork.SaveChangesAsync();
        return ObjectMapper.Map<BaseStep, BaseStepDto>(step);
    }
}

该方案绕过了CrudAppService的CreateAsync方法,直接操作仓储,更贴合DDD思想,同时避免了对泛型方法的依赖。


内容的提问来源于stack exchange,提问作者Sebbas

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 00:52:11