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

FastEndpoints:Data类的用法及查询代码编写位置

FastEndpoints Data类用法与数据库查询位置指南

一、Data类的正确用法与示例

FastEndpoints中Endpoint<TRequest, TResponse, TData>里的TData,是用来在端点的生命周期方法间共享临时状态的容器,和数据库查询没有直接关联。

适用场景

  • 需要在PreProcessAsync(前置处理)、HandleAsync(核心逻辑)、PostProcessAsync(后置处理)之间传递数据时
  • 存储端点内的中间计算结果、验证后的上下文数据等,避免重复计算或繁琐的参数传递

示例代码

比如一个获取用户详情的端点,在前置处理中提前验证用户ID并查询基础信息,存入Data供核心逻辑使用:

public class GetUserDetailsEndpoint : Endpoint<GetUserRequest, GetUserResponse, UserContextData>
{
    private readonly AppDbContext _dbContext;

    public GetUserDetailsEndpoint(AppDbContext dbContext)
    {
        _dbContext = dbContext;
    }

    public override void Configure()
    {
        Get("/users/{UserId}");
        AllowAnonymous();
    }

    // 前置处理:验证ID并查询用户基础数据
    public override async Task PreProcessAsync(CancellationToken ct)
    {
        var userId = Route<int>("UserId");
        var user = await _dbContext.Users.FindAsync(userId, ct);

        if (user == null)
        {
            AddError("用户不存在");
            ThrowIfAnyErrors();
        }

        // 将查询结果存入Data,供后续方法调用
        Data.User = user;
        Data.RequestedUserId = userId;
    }

    // 核心逻辑:使用Data中的数据构建响应
    public override async Task HandleAsync(GetUserRequest req, CancellationToken ct)
    {
        Response = new GetUserResponse
        {
            Id = Data.RequestedUserId,
            Name = Data.User.Name,
            Email = Data.User.Email,
            JoinDate = Data.User.JoinDate
        };
    }
}

// 自定义Data类,存储端点内需要共享的状态
public class UserContextData
{
    public User User { get; set; }
    public int RequestedUserId { get; set; }
}

// 请求/响应类
public class GetUserRequest { }
public class GetUserResponse
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Email { get; set; }
    public DateTime JoinDate { get; set; }
}

二、数据库查询的位置选择

1. 简单查询:直接写在Endpoint中

如果查询逻辑简单(单表查询、无复杂关联),且仅当前端点使用,直接写在HandleAsync或PreProcessAsync中完全符合垂直切片架构。垂直切片的核心是围绕业务功能聚合代码,不需要为了“分层”而强行抽象。

示例:

public override async Task HandleAsync(GetUserRequest req, CancellationToken ct)
{
    var user = await _dbContext.Users
        .Where(u => u.Id == req.UserId)
        .Select(u => new GetUserResponse
        {
            Id = u.Id,
            Name = u.Name
        })
        .FirstOrDefaultAsync(ct);

    if (user == null)
        ThrowError("用户不存在");

    Response = user;
}

2. 复杂/复用查询:使用业务切片专属仓储

如果查询逻辑复杂(多表关联、过滤规则复杂),或者多个端点需要复用相同的查询逻辑,不要用通用的IRepository<T>(这种围绕实体的抽象违背垂直切片),而是创建属于当前业务切片的专属仓储。

比如用户模块下多个端点需要查询用户订单信息,就创建UserOrderRepository,仅包含该切片需要的查询方法:

// 专属仓储:仅服务于用户相关的订单查询
public class UserOrderRepository
{
    private readonly AppDbContext _dbContext;

    public UserOrderRepository(AppDbContext dbContext)
    {
        _dbContext = dbContext;
    }

    // 仅实现当前业务需要的查询方法
    public async Task<List<UserOrderDto>> GetUserRecentOrders(int userId, int count, CancellationToken ct)
    {
        return await _dbContext.Orders
            .Where(o => o.UserId == userId && o.CreatedAt >= DateTime.Now.AddDays(-30))
            .OrderByDescending(o => o.CreatedAt)
            .Take(count)
            .Select(o => new UserOrderDto
            {
                OrderId = o.Id,
                TotalAmount = o.TotalAmount,
                CreatedAt = o.CreatedAt
            })
            .ToListAsync(ct);
    }
}

然后在Endpoint中注入使用:

public class GetUserRecentOrdersEndpoint : Endpoint<GetUserOrdersRequest, GetUserOrdersResponse>
{
    private readonly UserOrderRepository _orderRepo;

    public GetUserRecentOrdersEndpoint(UserOrderRepository orderRepo)
    {
        _orderRepo = orderRepo;
    }

    public override void Configure()
    {
        Get("/users/{UserId}/recent-orders");
        AllowAnonymous();
    }

    public override async Task HandleAsync(GetUserOrdersRequest req, CancellationToken ct)
    {
        var orders = await _orderRepo.GetUserRecentOrders(req.UserId, req.Count, ct);
        Response = new GetUserOrdersResponse { Orders = orders };
    }
}

为什么专属仓储不违背垂直切片?

垂直切片禁止的是跨切片的通用抽象层(比如包含所有实体CRUD的通用仓储),而专属仓储是当前业务切片的一部分,它的方法都是为当前切片的业务功能服务的,没有跨切片依赖,完全符合“围绕业务功能聚合代码”的原则。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 06:28:14