簡體   English   中英

LINQ 無法翻譯表達式(EF Core 中的深度查詢)

[英]LINQ Expression could not be translated (Deep Query in EF Core)

我的錯誤信息如下:


LINQ 表達式

DbSet<WorkItemEntity>
  .Where(w => w.Company.Name.ToLower() != null && "com" != null && 
              w.Company.Name.ToLower().StartsWith("com"))

無法翻譯。 以可以翻譯的形式重寫查詢,或者通過插入對 AsEnumerable()、AsAsyncEnumerable()、ToList() 或 ToListAsync() 的調用來顯式切換到客戶端評估。 有關詳細信息,請參閱https://go.microsoft.com/fwlink/?linkid=2101038


我不確定是哪一部分導致的,我認為 ToLower/StartsWith 都受支持?

查詢由表達式生成。 重要的部分發生在這里:

public static IQueryable<T> ApplyFilters<T, TOut>(this IQueryable<T> query, Dictionary<Expression<Func<T, TOut>>, Filter> parameters)
{
    foreach (var filterPair in parameters)
    {
        try
        {
            var parameterExpression = Expression.Parameter(typeof(T), "w");
            MemberExpression propertyExpression = filterPair.Key.Body as MemberExpression;

            var body = GetBody(filterPair, propertyExpression);

            if (body != null)
            {
                query = query.Where(Expression.Lambda<Func<T, bool>>(body, parameterExpression));
            }
        }
        catch (Exception e1)
        {
            // Do not throw for invalid expressions
        }
    }
    return query;
}

這是我的“身體”

var stringModel = filterPair.Value.Value as StringModel;
comparerExpression = Expression.Constant(stringModel.Value.ToLower());
string methodName = Enum.GetName(typeof(StringFilterType), filterType);
MethodInfo methodInfo = typeof(string).GetMethod(methodName, new Type[] { typeof(string) });
return Expression.Call(ToLowerMember(propertyExpression), methodInfo, comparerExpression);

除了查詢的實現之外的一切似乎都有效? 有任何想法嗎?

當我重寫自己的查詢(錯誤)時,它工作正常。 所以我想這與反射有關?

query = query.Where(w => w.Company.Name.ToLower().StartsWith("com"));

因此,這僅涵蓋頂級屬性的屬性過濾(例如,無關系)。 但是,如果您執行類似 select(x => new xModel() { Name = x.Partner.Name}) 之類的操作,您將能夠通過在 Deep 屬性的 Select 之后進行過濾來訪問“Name”。

我有一個使用深層屬性表達式的解決方案,但最終它會更方便地首先 Select 然后通過數組在一行中應用過濾器。

您也可以在其中看到我是如何解決字符串過濾的。

謝謝大家的幫助: :)

using System;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using Shared.Models;
using Shared.Models.Filter;

namespace App.Extensions
{
    public static class FilterExtensions
    {
        /// <summary>
        /// FilterModel
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="TFilterType"></typeparam>
        /// <param name="query"></param>
        /// <param name="filter"></param>
        /// <returns></returns>
        public static IQueryable<T> ApplyFilter<T, TFilterType>(this IQueryable<T> query, FilterModel<TFilterType> filter)
        {
            (Expression Body, ParameterExpression Parameter) value;

            if (filter.FilterType is NumberFilterType numberFilterType)
            {
                value = CreateNumberFilter<T, TFilterType>(filter, numberFilterType);
            }
            else if(filter.FilterType is DateTimeFilterType dateTimeFilterType)
            {
                value = CreateDateTimeFilter<T, TFilterType>(filter, dateTimeFilterType);
            }
            else if(filter.FilterType is StringFilterType stringFilterType)
            {
                value = CreateStringFilter<T, TFilterType>(filter, stringFilterType);
            }
            else if (filter.FilterType is BoolFilterType)
            {
                value = CreateBoolFilter<T, TFilterType>(filter);
            }
            else if (filter.FilterType is GuidFilterType)
            {
                value = CreateGuidFilter<T, TFilterType>(filter);
            }
            else
            {
                throw new NotImplementedException(filter.FilterType.ToString());
            }

            return query.Where(Expression.Lambda<Func<T, bool>>(value.Body, value.Parameter));

        }

        public static IQueryable<T> ApplyFilters<T>(this IQueryable<T> query, FilterPagingParameters parameters)
        {
            query = query.ApplyFilters(parameters.NumberFilters);
            query = query.ApplyFilters(parameters.DateTimeFilters);
            query = query.ApplyFilters(parameters.StringFilters);
            query = query.ApplyFilters(parameters.BoolFilters);
            query = query.ApplyFilters(parameters.GuidFilters);

            return query;
        }

        private static IQueryable<T> ApplyFilters<T, TFilterType>(this IQueryable<T> query, FilterModel<TFilterType>[] filters)
        {
            if (filters != null && filters.Any())
            {
                foreach (var filter in filters)
                {
                    query = query.ApplyFilter(filter);
                }
            }

            return query;
        }

        private static (Expression Body, ParameterExpression Parameter) CreateNumberFilter<T, TFilterType>(FilterModel<TFilterType> filter, NumberFilterType filterType)
        {
            var propertyExpression = GetExpression<T, decimal>(filter.Name);
            var parameter = propertyExpression.Parameters[0];
            var value1 = Expression.Constant(Convert.ToDecimal(filter.Value.Value));
            Expression body;

            if (filterType == NumberFilterType.Between)
            {
                var value2 = Expression.Constant(Convert.ToDecimal(filter.Value.Value2));
                var bodyMin = Expression.GreaterThanOrEqual(propertyExpression.Body, value1);
                var bodyMax = Expression.LessThanOrEqual(propertyExpression.Body, value2);

                body = Expression.AndAlso(bodyMin, bodyMax);

            }
            else if (filterType == NumberFilterType.Equals)
            {
                body = Expression.Equal(propertyExpression.Body, value1);
            }
            else if (filterType == NumberFilterType.GreaterThan)
            {
                body = Expression.GreaterThan(propertyExpression.Body, value1);
            }
            else if (filterType == NumberFilterType.GreaterThanOrEqual)
            {
                body = Expression.GreaterThanOrEqual(propertyExpression.Body, value1);
            }
            else if (filterType == NumberFilterType.LessThan)
            {
                body = Expression.LessThan(propertyExpression.Body, value1);
            }
            else if (filterType == NumberFilterType.LessThanOrEqual)
            {
                body = Expression.LessThanOrEqual(propertyExpression.Body, value1);
            }
            else
            {
                throw new NotImplementedException(filterType.ToString());
            }

            return (body, parameter);
        }

        private static (Expression Body, ParameterExpression Parameter) CreateDateTimeFilter<T, TFilterType>(FilterModel<TFilterType> filter, DateTimeFilterType filterType)
        {
            var propertyExpression = GetExpression<T, DateTime>(filter.Name);
            var parameter = propertyExpression.Parameters[0];
            var value1 = Expression.Constant(Convert.ToDateTime(filter.Value.Value)); // TODO: ToDate Required?
            Expression body;

            if (filterType == DateTimeFilterType.Between)
            {
                var value2 = Expression.Constant(Convert.ToDateTime(filter.Value.Value2));
                var bodyMin = Expression.GreaterThanOrEqual(propertyExpression.Body, value1);
                var bodyMax = Expression.LessThanOrEqual(propertyExpression.Body, value2);

                body = Expression.AndAlso(bodyMin, bodyMax);

            }
            else if (filterType == DateTimeFilterType.Equals)
            {
                body = Expression.Equal(propertyExpression.Body, value1);
            }
            else if (filterType == DateTimeFilterType.GreaterThan)
            {
                body = Expression.GreaterThan(propertyExpression.Body, value1);
            }
            else if (filterType == DateTimeFilterType.GreaterThanOrEqual)
            {
                body = Expression.GreaterThanOrEqual(propertyExpression.Body, value1);
            }
            else if (filterType == DateTimeFilterType.LessThan)
            {
                body = Expression.LessThan(propertyExpression.Body, value1);
            }
            else if (filterType == DateTimeFilterType.LessThanOrEqual)
            {
                body = Expression.LessThanOrEqual(propertyExpression.Body, value1);
            }
            else
            {
                throw new NotImplementedException(filterType.ToString());
            }

            return (body, parameter);
        }

        private static (Expression Body, ParameterExpression Parameter) CreateStringFilter<T, TFilterType>(FilterModel<TFilterType> filter, StringFilterType filterType)
        {
            // TODO: Try to make it the same way everywhere
            string methodName = Enum.GetName(typeof(StringFilterType), filterType);
            MethodInfo methodInfo = typeof(string).GetMethod(methodName, new Type[] { typeof(string) });

            if(methodInfo == null)
            {
                throw new NotImplementedException(filterType.ToString());
            }

            var propertyExpression = GetExpression<T, string>(filter.Name);
            var parameter = propertyExpression.Parameters[0];
            var value = Expression.Constant(filter.Value.Value as string);
            Expression body = Expression.Call(propertyExpression.Body, methodInfo, value);

            return (body, parameter);
        }

        private static (Expression Body, ParameterExpression Parameter) CreateGuidFilter<T, TFilterType>(FilterModel<TFilterType> filter)
        {
            var propertyExpression = GetExpression<T, Guid>(filter.Name);
            var parameter = propertyExpression.Parameters[0];
            var value = Expression.Constant(Guid.Parse(filter.Value.Value as string));
            Expression body = Expression.Equal(propertyExpression.Body, value);
            return (body, parameter);
        }

        private static (Expression Body, ParameterExpression Parameter) CreateBoolFilter<T, TFilterType>(FilterModel<TFilterType> filter)
        {
            var propertyExpression = GetExpression<T, bool>(filter.Name);
            var parameter = propertyExpression.Parameters[0];
            var value = Expression.Constant((bool)filter.Value.Value);
            Expression body = Expression.Equal(propertyExpression.Body, value);

            return (body, parameter);
        }

        private static Expression<Func<T, TProperty>> GetExpression<T, TProperty>(string propertyName)
        {
            // x =>
            var parameter = Expression.Parameter(typeof(T));
            // x.Name
            var mapProperty = Expression.Property(parameter, propertyName);
            // (object)x.Name
            var convertedExpression = Expression.Convert(mapProperty, typeof(TProperty));
            // x => (object)x.Name
            return Expression.Lambda<Func<T, TProperty>>(convertedExpression, parameter);
        }
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM