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
相关产品推荐
相关产品推荐

