У меня есть простая конечная точка Web API, которая может принимать входящие запросы OData:
public IActionResult GetProducts(ODataQueryOptions<ProductDTO> options)
{
var results = DomainLayer.GetProducts(options);
return Ok(results);
}
Я специально хочу иметь возможность запрашивать у ProductDTO
объектов и иметь возможность фильтровать или сортировать по свойствам представления DTO.
Моя проблема с дизайном заключается в том, что я хочу воспользоваться логикой разбора / применения фильтра библиотеки OData, но не хочу выставлять мои объекты ProductEntity
, связанные с базой данных, моему веб-API AND Я не хочу возвращать IQueryable
из моих DataAccessLayer
, только IEnumerable
с.
Затем я пытаюсь извлечь Expression
из свойства FilterQueryOption
входящего ODataQueryOptions
, чтобы я мог использовать функцию сопоставления выражений AutoMapper для сопоставления выражения из Expression<Func<ProductDTO, bool>>
в Expression<Func<Product, bool>>
затем, наконец, Expression<Func<ProductEntity, bool>>
, где я затем передам его в .Where()
вызов на моем Table<ProductEntity>
, где (надеюсь) фильтр применяется в моей базе данных SQL (через Linq-2-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);
}
}
}
Ссылка: