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

如何将ODataQueryOptions<ProductDTO>转换为ODataQueryOptions<Product>查询仓储?

解决ODataQueryOptions到实体的查询转换问题

直接转换ODataQueryOptions<ProductDTO>为ODataQueryOptions<Product>并不现实,因为ODataQueryOptions依赖请求上下文和EDM模型绑定。更合理的方案是拆解ODataQueryOptions中的各个查询组件,分别映射后应用到仓储返回的IQueryable<Product>上,以此支持Filter、Select、Top、Skip、OrderBy等所有OData操作。

步骤1:调整仓储接口,返回IQueryable

先修改仓储接口,让Get方法返回IQueryable<Product>,而非直接接收ODataQueryOptions,这样能在控制器层统一处理查询转换:

public interface IRepository<T>
{
    IQueryable<T> Get();
    // 其他仓储方法...
}

步骤2:拆解并映射OData查询组件

在控制器中逐个处理ODataQueryOptions的核心组件:

1. 过滤(Filter)映射

利用AutoMapper的ExpressionMapping扩展,将针对DTO的过滤表达式转换为实体的过滤表达式:

// 提取DTO的过滤表达式
var dtoFilter = queryOptions.Filter?.ToExpression<Func<ProductDTO, bool>>();
// 映射为实体的过滤表达式
Expression<Func<Product, bool>> entityFilter = null;
if (dtoFilter != null)
{
    entityFilter = _mapper.Map<Expression<Func<Product, bool>>>(dtoFilter);
}

2. 排序(OrderBy)映射

OData的OrderBy表达式需要手动映射字段名(从DTO字段到实体字段),可借助AutoMapper配置获取字段映射关系:

IQueryable<Product> query = _repository.Get();

if (queryOptions.OrderBy != null)
{
    var orderByClause = queryOptions.OrderBy.ToString();
    // 从AutoMapper配置中获取DTO与实体的字段映射
    var nameMap = _mapper.ConfigurationProvider.FindTypeMapFor<Product, ProductDTO>()
        .GetPropertyMaps()
        .ToDictionary(p => p.DestinationMember.Name, p => p.SourceMember.Name);

    // 替换OrderBy中的字段名为实体对应字段
    foreach (var map in nameMap)
    {
        orderByClause = orderByClause.Replace(map.Key, map.Value);
    }

    // 动态应用排序
    query = query.OrderBy(orderByClause);
}

3. 分页(Top/Skip)

直接提取Top和Skip的值,应用到查询上:

if (queryOptions.Skip != null)
{
    query = query.Skip(queryOptions.Skip.Value);
}
if (queryOptions.Top != null)
{
    query = query.Take(queryOptions.Top.Value);
}

4. 投影(Select)

不直接使用OData的Select表达式,而是用AutoMapper的ProjectTo方法将实体查询直接投影为DTO,同时自动遵循OData Select指定的字段(需确保AutoMapper配置正确):

// 先应用过滤、排序、分页,再投影为DTO
var projectedQuery = query.ProjectTo<ProductDTO>(_mapper.ConfigurationProvider);

// 若需严格遵循OData的Select规则,可结合OData的ApplyTo方法:
// projectedQuery = queryOptions.ApplyTo(projectedQuery) as IQueryable<ProductDTO>;

完整控制器代码

整合以上步骤后的控制器代码如下:

[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
    private readonly IRepository<Product> _repository;
    private readonly IMapper _mapper;

    public ProductsController(IRepository<Product> repository, IMapper mapper)
    {
        _repository = repository;
        _mapper = mapper;
    }

    [HttpGet]
    public async Task<ActionResult<IList<ProductDTO>>> Get(ODataQueryOptions<ProductDTO> queryOptions)
    {
        IQueryable<Product> query = _repository.Get();

        // 处理过滤
        if (queryOptions.Filter != null)
        {
            var dtoFilter = queryOptions.Filter.ToExpression<Func<ProductDTO, bool>>();
            var entityFilter = _mapper.Map<Expression<Func<Product, bool>>>(dtoFilter);
            query = query.Where(entityFilter);
        }

        // 处理排序
        if (queryOptions.OrderBy != null)
        {
            var orderByClause = queryOptions.OrderBy.ToString();
            var propertyMaps = _mapper.ConfigurationProvider.FindTypeMapFor<Product, ProductDTO>()
                .GetPropertyMaps();
            foreach (var map in propertyMaps)
            {
                orderByClause = orderByClause.Replace(map.DestinationMember.Name, map.SourceMember.Name);
            }
            query = query.OrderBy(orderByClause);
        }

        // 处理分页
        if (queryOptions.Skip != null)
            query = query.Skip(queryOptions.Skip.Value);
        if (queryOptions.Top != null)
            query = query.Take(queryOptions.Top.Value);

        // 投影为DTO并执行查询
        var result = await query.ProjectTo<ProductDTO>(_mapper.ConfigurationProvider).ToListAsync();

        return Ok(result);
    }
}

关键注意事项

  • 确保AutoMapper的ExpressionMapping已正确注册(你已在配置中添加cfg.AddExpressionMapping())
  • 若DTO与实体字段名完全一致,排序和过滤的映射可简化,无需手动替换字段名
  • 对于复杂OData操作(如Expand),需额外处理实体间的关联映射,确保投影正确
  • 可封装通用的OData查询映射工具类,避免多控制器重复代码

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 22:10:24