如何获取OData Queryable Web API端点过滤器并从DTO对象映射它? [英] How to take OData Queryable Web API endpoint filter and map it from DTO object?

查看:51
本文介绍了如何获取OData Queryable Web API端点过滤器并从DTO对象映射它?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个简单的Web API端点,可以接受传入的OData查询:

I have a simple Web API endpoint which can accept incoming OData queries:

public IActionResult GetProducts(ODataQueryOptions<ProductDTO> options)
{
    var results = DomainLayer.GetProducts(options);
    return Ok(results);
}

我特别希望能够针对 ProductDTO 对象进行查询,并能够针对DTO表示形式的属性进行过滤或排序.

I specifically want to be able to query against ProductDTO objects and to be able to filter or sort against the properties of the DTO representation.

我的设计问题是我想利用OData库的过滤器解析/应用逻辑,但又不想将数据库绑定的 ProductEntity 对象公开给Web API AND 我不想从我的 DataAccessLayer 中返回一个 IQueryable ,而仅返回 IEnumerable .

My design issue is that I want to take advantage of the filter parsing/applying logic of the OData library but I don't want to expose my database-bound ProductEntity objects to my Web API AND I do not want to return an IQueryable from my DataAccessLayer, only IEnumerables.

然后我要做的是从传入的 ODataQueryOptions FilterQueryOption 属性中提取 Expression ,以便可以使用AutoMapper的Expression映射功能可将表达式从 Expression< Func< ProductDTO,bool> 映射到 Expression< Func< Product,bool> ,然后最终映射到 Expression< Func< ProductEntity,bool>>> ,然后将其传递到我的 Table< ProductEntity> 上的 .Where()调用中>在哪里(希望)将过滤器应用到我的SQL数据库中(通过Linq-2-SQL),然后我将其完全转换回DTO对象.

What I am trying to do then is to extract the Expression from the FilterQueryOption property of the incoming ODataQueryOptions so I can use AutoMapper's Expression Mapping feature to map the expression from a Expression<Func<ProductDTO, bool>> to a Expression<Func<Product, bool>> then finally to a Expression<Func<ProductEntity, bool>> where I will then pass it into a .Where() call on my Table<ProductEntity> where (hopefully) the filter is applied in my SQL database (via Linq-2-SQL) and then I just convert it all the way back to a DTO object after.

我遇到的最大问题是 queryable.Expression 返回的是 MethodCallExpression ,而不是 Expression< Func< ProductDTO,bool>> 就像我期望的那样,这意味着我无法像我计划的那样使用AutoMapper映射表达式...

The big showstopper I came across is that queryable.Expression is returning a MethodCallExpression rather than a Expression<Func<ProductDTO, bool>> like I expected, which means I can't map the expression with AutoMapper like I had planned...

我该如何解决?

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);
        }
    }
}

参考:

推荐答案

请注意,表达式包含的内容更像是 SOTests.Customer [].Where($ it => conditional-expression).因此,您可能必须从lambda中提取该条件表达式.

Notice that the expression contains looks more like this, SOTests.Customer[].Where($it => conditional-expression). So, you might have to extract that conditional expression from the lambda.

您得到的 MethodCallExpression 就是-对" Queryable.Where< ProductDTO> "和lambda表达式 Expression< Func< ProductDTO的调用",bool>> 是第二个参数(请记住 Queryable.Where static 扩展方法,因此第一个参数表示 IQueryable< ProductDTO> ),并用

The MethodCallExpression you are getting is exactly that - a "call" to Queryable.Where<ProductDTO>, and the lambda expression Expression<Func<ProductDTO, bool>> you need is the second argument (remember the Queryable.Where is a static extension method, so the first argument is representing the IQueryable<ProductDTO>), wrapped with Expression.Quote.

因此,您所需要做的就是使用以下内容提取lambda表达式:

So all you need is to extract the lambda expression with something like this:

public static class ODataQueryOptionsExtensions
{
    public static Expression<Func<T, bool>> GetFilter<T>(this ODataQueryOptions<T> options)
    {
        // The same trick as in the linked post
        IQueryable query = Enumerable.Empty<T>().AsQueryable();
        query = options.Filter.ApplyTo(query, new ODataQuerySettings());
        // Extract the predicate from `Queryable.Where` call
        var call = query.Expression as MethodCallExpression;
        if (call != null && call.Method.Name == nameof(Queryable.Where) && call.Method.DeclaringType == typeof(Queryable))
        {
            var predicate = ((UnaryExpression)call.Arguments[1]).Operand;
            return (Expression<Func<T, bool>>)predicate;
        }
        return null;
    }
}

并像这样使用它:

public class DomainLayer
{
    public IEnumerable<ProductDTO> GetProductsByEntityOptions(ODataQueryOptions<ProductDTO> options)
    {
         var filter = options.GetFilter();
         // Here the type of filter variable is Expression<Func<ProductDTO, bool>> as desired
         // The rest ...
    }
}

这篇关于如何获取OData Queryable Web API端点过滤器并从DTO对象映射它?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆