ABP 应用服务源码解读:ApplicationService 基类内置了哪些能力

ApplicationService 的声明

csharp 复制代码
// framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/ApplicationService.cs
public abstract class ApplicationService :
    IApplicationService,
    IAvoidDuplicateCrossCuttingConcerns,
    IValidationEnabled,           // ← 自动验证
    IUnitOfWorkEnabled,           // ← 自动事务
    IAuditingEnabled,             // ← 自动审计
    IGlobalFeatureCheckingEnabled, // ← 自动特性检查
    ITransientDependency          // ← 自动注册

继承 ApplicationService 的类自动获得 5 个横切关注点。

横切关注点的拦截实现

这些接口不是装饰器,而是被 Castle DynamicProxy(AOP 框架)拦截的。ABP 为每个实现了这些标记接口的类创建代理,在方法调用前后插入横切逻辑。

csharp 复制代码
// 以验证为例,执行顺序:
1. UnitOfWorkInterceptor    → 开启事务
2. AuditingInterceptor      → 记录审计日志
3. AuthorizeInterceptor     → 检查权限
4. ValidationInterceptor    → 验证参数
5. → 执行业务方法 ←
6. UnitOfWorkInterceptor    → 提交事务

内置的快捷属性

csharp 复制代码
// 以下属性可以直接使用,不需要手动注入
public abstract class ApplicationService
{
    protected IGuidGenerator GuidGenerator { get; }      // ID 生成
    protected ICurrentUser CurrentUser { get; }           // 当前用户
    protected ICurrentTenant CurrentTenant { get; }       // 当前租户
    protected IObjectMapper ObjectMapper { get; }         // 对象映射
    protected IUnitOfWorkManager UnitOfWorkManager { get; } // 事务管理
    protected IAuthorizationService AuthorizationService { get; } // 授权
    protected IClock Clock { get; }                       // 时间
    protected IStringLocalizer L { get; }                 // 本地化
    protected ILogger Logger { get; }                     // 日志
    protected ISettingProvider SettingProvider { get; }   // 设置
    protected IFeatureChecker FeatureChecker { get; }     // 特性开关
    protected IDataFilter DataFilter { get; }             // 数据过滤
    protected IAsyncQueryableExecuter AsyncExecuter { get; } // 异步查询
}

这些属性都是通过 LazyServiceProvider 延迟加载的,只有在使用时才从 DI 容器解析,不会增加构造开销。

CrudAppService 源码解读

CrudAppService 位于 framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/CrudAppService.cs,它把 CRUD 操作模板化了。

GetAsync 为例:

csharp 复制代码
// 抽象基类中的实现
protected override async Task<TEntity> GetEntityByIdAsync(TKey id)
{
    return await Repository.GetAsync(id);  // 委托给仓储
}

// 然后由 CrudAppService 的 MapToGetOutputDtoAsync 映射为 DTO
protected override Task<TGetOutputDto> MapToGetOutputDtoAsync(TEntity entity)
{
    return Task.FromResult(ObjectMapper.Map<TEntity, TGetOutputDto>(entity));
}

CrudAppService 暴露了多个 virtual 方法,开发者可以按需覆写:

csharp 复制代码
public class BookAppService : CrudAppService<Book, BookDto, Guid>
{
    // 覆写创建时的额外逻辑
    protected override async Task<Book> MapToEntityAsync(CreateBookDto createInput)
    {
        var book = await base.MapToEntityAsync(createInput);
        book.Status = BookStatus.Draft;  // 设置默认值
        return book;
    }

    // 覆写查询过滤
    protected override async Task<IQueryable<Book>> CreateFilteredQueryAsync(PagedAndSortedResultRequestDto input)
    {
        return (await Repository.GetQueryableAsync())
            .Where(b => !b.IsHidden);  // 过滤掉隐藏的
    }
}

实践:应用服务的设计原则

csharp 复制代码
// 推荐:每个方法只做一件事,使用 CrudAppService 减少模板代码
public class BookAppService :
    CrudAppService<Book, BookDto, Guid, BookListDto, CreateBookDto, UpdateBookDto>
{
    public BookAppService(IRepository<Book, Guid> repo) : base(repo) { }
}

// 复杂业务:使用 ApplicationService 基类提供的快捷属性
public class OrderAppService : ApplicationService, IOrderAppService
{
    public async Task<OrderDto> CreateAsync(CreateOrderDto input)
    {
        var order = ObjectMapper.Map<CreateOrderDto, Order>(input);
        order.Id = GuidGenerator.Create();
        order.TenantId = CurrentTenant.Id;

        Logger.LogInformation("用户 {UserId} 创建订单 {OrderId}", CurrentUser.Id, order.Id);

        await Repository.InsertAsync(order);
        return ObjectMapper.Map<Order, OrderDto>(order);
    }
}
相关推荐
硅基喵1 天前
ABP 仓储模式源码解读:自动过滤是如何实现的
dotnet
硅基喵2 天前
ABP DDD 实体源码解读:7 层基类的设计逻辑和适用场景
dotnet
硅基喵4 天前
ABP 模块系统源码学习:启动时模块是如何加载和排序的
dotnet
硅基喵3 个月前
C# 也能像 Python 一样写脚本 | .NET 10 构建基于文件的应用
dotnet
硅基喵3 个月前
.NET 10 使用 Microsoft.AspNetCore.OpenApi 实现 API 版本管理
dotnet
硅基喵4 个月前
ASP.NET Core 内存缓存实战:一篇搞懂该怎么配、怎么避坑
dotnet
ChaITSimpleLove5 个月前
aiagent-webapi 命令的详细使用说明
dotnet·webapi·ai agent·agent framework·maf·projecttemp
TeamDev5 个月前
使用 Docker 部署 DotNetBrowser 应用程序
运维·ui·docker·容器·桌面应用·dotnet·dotnetbrowser
CSharp精选营5 个月前
.NET命名之谜:它与C#纠缠20年的关系揭秘
c#·.net·dotnet·csharp