惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Google DeepMind News
Google DeepMind News
U
Unit 42
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
J
Java Code Geeks
D
DataBreaches.Net
B
Blog RSS Feed
D
Docker
L
LangChain Blog
aimingoo的专栏
aimingoo的专栏
F
Fortinet All Blogs
Y
Y Combinator Blog
A
About on SuperTechFans
V
V2EX
罗磊的独立博客
WordPress大学
WordPress大学
宝玉的分享
宝玉的分享
MongoDB | Blog
MongoDB | Blog
博客园 - 【当耐特】
Last Week in AI
Last Week in AI
S
SegmentFault 最新的问题
月光博客
月光博客
Vercel News
Vercel News
H
Hackread – Cybersecurity News, Data Breaches, AI and More
阮一峰的网络日志
阮一峰的网络日志

博客园 - 邓磊Lei

我可以双击 shift 打开 visual studio 的代码搜索 我决定把 gittoy 更名为 git blame for vs 为了可以重新拥抱 Visual Studio, 我做了一个 vs 版的 gittoolbox ABP DTO 与对象映射:AutoMapper Profile 配置与 Mapperly 源生成器 ABP 仓储模式源码解读:自动过滤是如何实现的 ABP DDD 实体源码解读:7 层基类的设计逻辑和适用场景 ABP 模块系统源码学习:启动时模块是如何加载和排序的 为什么 Go 一个 HTTP 服务可以同时处理数万连接 gin怎么处理一次http请求的 我的经验:git提交信息分别用什么icon C# 也能像 Python 一样写脚本 | .NET 10 构建基于文件的应用 .NET 10 使用 Microsoft.AspNetCore.OpenApi 实现 API 版本管理 ASP.NET Core 内存缓存实战:一篇搞懂该怎么配、怎么避坑 Microsoft Agent Framework + Kimi API 实战:控制台应用跑通单次与多轮 Agent 对话 开发实战:asp.net core + ef core 实现动态可扩展的分页方案 聊聊 ASP.NET Core 中间件和过滤器的区别 Python 入门:从“其他语言”到 Pythonic 思维的完整迁移手册 .NET 进阶之路:异步、并发与内存管理的系统性认知 Redis:延迟双删的适用边界与落地细节 Serilog:从结构化日志认知到 .NET 工程落地 EF Core 原生 SQL 实战:FromSql、SqlQuery 与对象映射边界 EF Core 拦截器实战:SaveChangesInterceptor、CommandInterceptor 与审计落地 ASP.NET Core 外部依赖调用治理实战:HttpClientFactory、Polly 与幂等边界 .NET .Result 避坑指南:不同框架下的死锁与线程池饥饿 EF Core 慢查询排查实战:TagWith、OpenTelemetry、执行计划,30 分钟定位性能瓶颈 EF Core 并发冲突实战:乐观锁、RowVersion 与 DbUpdateConcurrencyException 怎么处理 EF Core 写入链路深拆:从 ChangeTracker 到 SQL Batch 的性能诊断与优化 ASP.NET Core 认证鉴权实战:JWT、Policy 与权限边界怎么落地 C# 异步编程深水区:Task、ValueTask、线程池饥饿与背压设计 EF Core 查询性能黑洞:Include、投影与跟踪策略的边界
ABP 应用服务源码解读:ApplicationService 基类内置了哪些能力
DLForge · 2026-08-05 · via 博客园 - 邓磊Lei

ApplicationService 的声明

// 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 为每个实现了这些标记接口的类创建代理,在方法调用前后插入横切逻辑。

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

内置的快捷属性

// 以下属性可以直接使用,不需要手动注入
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 为例:

// 抽象基类中的实现
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 方法,开发者可以按需覆写:

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);  // 过滤掉隐藏的
    }
}

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

// 推荐:每个方法只做一件事,使用 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);
    }
}