我有一个简单的Web API端点,可以接受传入的OData查询:
public IActionResult GetProducts(ODataQueryOptions<ProductDTO> options)
{
var results = DomainLayer.GetProducts(options);
return Ok(results);
}
我希望能够针对
ProductDTO
对象查询并根据 DTO 表示的属性进行过滤或排序。我的设计问题在于,我想利用 OData 库的过滤器解析/应用逻辑,但我不想将我的数据库绑定的 ProductEntity
对象公开到 Web API,也不想从我的 DataAccessLayer
返回一个 IQueryable
,仅返回 IEnumerable
。因此,我正在尝试从传入的 ODataQueryOptions
的 FilterQueryOption
属性中提取 Expression
,以便可以使用 AutoMapper 的表达式映射功能将表达式从 Expression<Func<ProductDTO, bool>>
映射到 Expression<Func<Product, bool>>
,最后映射到Expression<Func<ProductEntity, bool>>
,然后将其传递到 Table<ProductEntity>
的 .Where()
调用中, 通过 Linq-2-SQL 在 SQL 数据库中应用筛选器(希望如此),最终将其转换回 DTO 对象。我遇到的大障碍是
queryable.Expression
返回的是 MethodCallExpression
而不是我预期的 Expression<Func<ProductDTO, bool>>
,这意味着我不能像计划的那样使用 AutoMapper 映射表达式...如何解决这个问题?using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using Microsoft.AspNet.OData.Query;
using AutoMapper.Extensions.ExpressionMapping;
using AutoMapper.QueryableExtensions;
namespace ProductApp
{
public class DomainLayer
{
public IEnumerable<ProductDTO> GetProductsByEntityOptions(ODataQueryOptions<ProductDTO> options)
{
var mapper = MyMapper.GetMapper();
// This is the trick to get the expression out of the FilterQueryOption...
IQueryable queryable = Enumerable.Empty<ProductDTO>().AsQueryable();
queryable = options.Filter.ApplyTo(queryable, new ODataQuerySettings());
var exp = (MethodCallExpression) queryable.Expression; // <-- This comes back as a MethodCallExpression...
// Map the expression to my intermediate Product object type
var mappedExp = mapper.Map<Expression<Func<Product, bool>>>(exp); // <-- But I want it as a Expression<Func<ProductDTO, bool>> so I can map it...
IEnumerable<Product> results = _dataAccessLayer.GetProducts(mappedExp);
return mapper.Map<IEnumerable<ProductDTO>>(results);
}
}
public class DataAccessLayer
{
public IEnumerable<Product> GetProducts(Expression<Func<Product, bool>> exp)
{
var mapper = MyMapper.GetMapper();
var mappedExp = mapper.Map<Expression<Func<ProductEntity, bool>>>(exp);
IEnumerable<ProductEntity> result = _dataContext.GetTable<ProductEntity>().Where(mappedExpression).ToList();
return mapper.Map<IEnumerable<Product>>(result);
}
}
}
参考资料:
- 获取过滤器中表达式的技巧来源: https://dev59.com/znHYa4cB1Zd3GeqPQ-C6#16447514
- 相关的 GitHub 问题链接: https://github.com/OData/WebApi/issues/33