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

如何在不通过Controller的情况下让OData在.NET Core 6中生效?

无Controller场景下OData 8 + .NET 6的实现方案

核心思路

跳过Controller直接在服务端业务类中处理OData查询,核心是手动解析OData查询参数,通过ODataQueryOptions.ApplyTo()方法将查询规则应用到EF Core的IQueryable,最终封装成兼容客户端的ODataServiceResult返回。


步骤1:基础配置(Program.cs)

即使不用Controller,仍需配置OData核心服务和EDM模型:

// 注册HttpContext访问器(用于获取请求查询参数)
builder.Services.AddHttpContextAccessor();

// 配置OData服务
builder.Services.AddControllers().AddOData(options =>
{
    // 启用所需的OData查询功能
    options.Select().Filter().OrderBy().Expand().Count().SetMaxTop(100);
    // 注册EDM模型
    options.AddRouteComponents("odata", GetEdmModel());
});

// 定义EDM模型(映射PersonDto结构)
private static IEdmModel GetEdmModel()
{
    ODataConventionModelBuilder modelBuilder = new();
    modelBuilder.EntitySet<PersonDto>("Persons");
    return modelBuilder.GetEdmModel();
}

步骤2:改造服务端PersonService

注入必要依赖,实现OData查询解析与应用:

public class ServerPersonService
{
    private readonly AppDbContext _context;
    private readonly IHttpContextAccessor _httpContextAccessor;
    private readonly IEdmModel _edmModel;
    private readonly ODataQuerySettings _querySettings;

    public ServerPersonService(AppDbContext context, IHttpContextAccessor httpContextAccessor, IEdmModel edmModel)
    {
        _context = context;
        _httpContextAccessor = httpContextAccessor;
        _edmModel = edmModel;
        // 配置OData查询全局设置
        _querySettings = new ODataQuerySettings { PageSize = 100 };
    }

    // 兼容客户端参数+Http请求两种调用方式
    public async Task<ODataServiceResult<PersonDto>> GetPeopleAsync(
        string filter = null, int? top = null, int? skip = null, 
        string orderby = null, string expand = null, string select = null, bool? count = null)
    {
        HttpRequest request = _httpContextAccessor.HttpContext?.Request;
        ODataQueryOptions<PersonDto> queryOptions;

        if (request != null && !string.IsNullOrEmpty(request.QueryString.Value))
        {
            // 从Http请求中解析OData查询参数
            queryOptions = new ODataQueryOptions<PersonDto>(_edmModel, request);
        }
        else
        {
            // 从传入参数构建模拟查询字符串
            var queryParams = new List<string>();
            if (!string.IsNullOrEmpty(filter)) queryParams.Add($"$filter={Uri.EscapeDataString(filter)}");
            if (top.HasValue) queryParams.Add($"$top={top.Value}");
            if (skip.HasValue) queryParams.Add($"$skip={skip.Value}");
            if (!string.IsNullOrEmpty(orderby)) queryParams.Add($"$orderby={Uri.EscapeDataString(orderby)}");
            if (!string.IsNullOrEmpty(expand)) queryParams.Add($"$expand={Uri.EscapeDataString(expand)}");
            if (!string.IsNullOrEmpty(select)) queryParams.Add($"$select={Uri.EscapeDataString(select)}");
            if (count.HasValue) queryParams.Add($"$count={count.Value.ToString().ToLower()}");

            var mockRequest = new HttpRequest(new DefaultHttpContext())
            {
                QueryString = new QueryString($"?{string.Join("&", queryParams)}")
            };
            queryOptions = new ODataQueryOptions<PersonDto>(_edmModel, mockRequest);
        }

        // 从数据库获取原始查询
        var baseQuery = _context.Persons.ConvertToDto().AsQueryable();

        // 将OData查询规则应用到IQueryable(EF Core会自动转成SQL)
        var queriedData = queryOptions.ApplyTo(baseQuery, _querySettings) as IQueryable<PersonDto>;

        // 处理计数请求(需考虑过滤条件)
        long? totalCount = null;
        if (queryOptions.Count != null && queryOptions.Count.Value)
        {
            var countQuery = queryOptions.Filter != null 
                ? queryOptions.Filter.ApplyTo(baseQuery, _querySettings) as IQueryable<PersonDto> 
                : baseQuery;
            totalCount = await countQuery.LongCountAsync();
        }

        // 执行查询获取数据
        var resultData = await queriedData.ToListAsync();

        // 封装成客户端兼容的结果格式
        return new ODataServiceResult<PersonDto>
        {
            Value = resultData.AsQueryable(),
            Count = totalCount
        };
    }
}

步骤3:简化控制器调用(可选)

如果仍需要对外暴露API,控制器只需调用服务即可:

[ApiController]
[Route("odata/[controller]")]
public class PersonsController : ControllerBase
{
    private readonly ServerPersonService _personService;

    public PersonsController(ServerPersonService personService)
    {
        _personService = personService;
    }

    [HttpGet]
    public async Task<ActionResult<ODataServiceResult<PersonDto>>> Get()
    {
        var result = await _personService.GetPeopleAsync();
        return Ok(result);
    }
}

关键注意事项

  1. EDM模型必须准确:OData依赖EDM模型解析查询规则,需确保PersonDto的属性映射正确。
  2. 计数逻辑优化:不能直接使用原始表的Count(),需应用过滤条件后再计数,避免数据不准确。
  3. 参数兼容:服务同时支持从Http请求和直接传入参数解析OData查询,适配Blazor预渲染和客户端API调用两种场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 00:24:32