Расширение DbFunctions в Entity Framework Core? - PullRequest
0 голосов
/ 04 февраля 2020

С. NET Core 3.1.1 и Entity Framework Core 3.1.1, у меня есть:

var query = from user in context.Users
            join userRole in userRoleView on user.Id equals userRole.UserId into gj
            from p in gj.DefaultIfEmpty()
            select new
                   {
                        user.Id,
                        user.UserName,
                        RoleName = p.Rolename,
                        user.CreatedUtc,
                        user.ModifiedUtc,
                  };

if (!String.IsNullOrWhiteSpace(conditions.Keyword))
{
    query = query.Where(d => EF.Functions.Like(d.UserName, "%" + conditions.Keyword + "%"));
}

Это работает хорошо, и тогда я хотел бы иметь EF.Functions.Contains(d.UserName, conditions.Keyword), поэтому я написал расширение:

public static class DbFunctionsExtensions
{
    public static bool Contains(this DbFunctions _, string matchExpression, string keyword)
    {
        return _.Like(matchExpression, "%" + keyword + "%");
    }
}

Однако при запуске

query.Where(d => EF.Functions.Contains(d.UserName, conditions.Keyword))

я получаю следующее исключение:

System.InvalidOperationException ... не удалось перевести ,

Либо переписать запрос в форме, которую можно перевести, либо явным образом переключиться на оценку клиента, вставив вызов либо AsEnumerable(), AsAsyncEnumerable(), ToList(), либо ToListAsync(). См. https://go.microsoft.com/fwlink/?linkid=2101038 для получения дополнительной информации.

Source = Microsoft.EntityFrameworkCore

StackTrace: at Microsoft.EntityFrameworkCore.Query.QueryableMethodTranslatingExpressionVisitor.g__CheckTranslated, 8_00 (переведено | 8_0) > c__DisplayClass8_0 &)
в Microsoft.EntityFrameworkCore.Query.QueryableMethodTranslatingExpressionVisitor.VisitMethodCall (methodCallExpression methodCallExpression) * * на тысячу двадцать восемь Microsoft.EntityFrameworkCore.Query.RelationalQueryableMethodTranslatingExpressionVisitor.VisitMethodCall (methodCallExpression methodCallExpression)
в System.Linq.Expressions.MethodCallExpression.Accept (Посетитель ExpressionVisitor)
в System.Linq.Expressions.ExpressionVisitor.Visit (узел Expression)
в Microsoft.EntityFrameworkCore.Query.QueryableMethodTranslatingExpressionVisitor.VisitMethodCetExhoeryQalworkPlayPlayPlayPlayPlayPlayPlayPlayPlayPlayPlayPlayPlayPlayPlayPlayPlayPlayPlayPlayPlayPlayPlayPlayPlayPlayPlayPlayPlayPlayPlayPlayPlayPlayPlayPlayPlayPlayPlayPlayPlayPlayPlayPlayPlayPlayPlayPlayPlayPlayPlayPlayPlayPlayPlayPlayPlayer_Ru_C_W_C_W_C_W_P_C_P_C_W_W_W_W_W_W_P_C_W_W_W_P_C_W_P_C_W_P_C_W_T_W_T_W_T_W_T_W_T_T_. VI sitMethodCall (MethodCallExpression methodCallExpression) в System.Linq.Expressions.MethodCallExpression.Accept (посетитель ExpressionVisitor) в System.Linq.Expressions.ExpressionVisitor.Visit (узел выражения) в методе Microsoft.EntityFrameworkCore.QliteExchange.VExExCress.ExceptionExCression. EntityFrameworkCore.Query.RelationalQueryableMethodTranslatingExpressionVisitor.VisitMethodCall (methodCallExpression methodCallExpression) при System.Linq.Expressions.MethodCallExpression.Accept (ExpressionVisitor посетителя) при System.Linq.Expressions.ExpressionVisitor.Visit (Выражение узла) в Microsoft.EntityFrameworkCore.Query.QueryCompilationContext.CreateQueryExecutor [ TResult] (запрос выражения) в Microsoft.EntityFrameworkCore.Storage.Database.CompileQuery [TResult] (запрос выражения, логический асин c) в Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.CompileQueryCore [идентификатор базы данных запроса TResult] ( IMO del модель, логическое asyn c) в Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler. <> c__DisplayClass9_0 1.<Execute>b__0() at Microsoft.EntityFrameworkCore.Query.Internal.CompiledQueryCache.GetOrAddQueryCore[TFunc](Object cacheKey, Func 1) в Microsoft.EntityFrameworkCore.Query.Internal.CompiledQueryCache.GeryOrAQ 1049 *1 compiler) at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.Execute[TResult](Expression query) at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryProvider.Execute[TResult](Expression expression) at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable 1.GetEnumerator () в System.Collections.Generi c .LargeArrayBuilder 1.AddRange(IEnumerable 1 элементов) в System.Collections.Generi c .EnumerableHelpers.ToArray [T] (IEnumerable 1 source) at System.Linq.Enumerable.ToArray[TSource](IEnumerable 1 источник ) в APS.WebPos.DAL.SearchOperations.GetActivePeopleByKeyword (ключевое слово String) в C: \ VSProjects \ ApsCloudTrunk \ APS.WebPos.DALCore \ SearchOperations.cs: строка 96 в APS.WebPos.WebApi.ControleyEp. Строковое ключевое слово) в C: \ VSProjects \ ApsCloudTrunk \ APS.WebPos.WebApiCore \ Controllers \ SearchController.cs: строка 25 в Microsoft.Extensions.Internal.ObjectMethodExecutor.Execute (объектная цель, параметры Object []) в Microsoft.AspNetCore . Mvc .Infrastructure.ActionMethodExecutor.SyncObjectResultExecutor.Execute (IActionResultTypeMapper m apper, исполнитель ObjectMethodExecutor, контроллер объекта, аргументы Object [] в Microsoft.AspNetCore. Mvc .Infrastructure.ControllerActionInvoker.InvokeActionMethodAsyn c () в Microsoft.AspNetCore. Mvc .Infrastructure.ControllerAxtInoker Область и область действия, объект и состояние, логическое значение и isCompleted) в Microsoft.AspNetCore. Mvc .Infrastructure.ControllerActionInvoker.InvokeNextActionFilterAsyn c ()

Возможно ли расширить DbFunctions в приложении с Entity Framework Core и использовать его в LINQ? Как?

Примечания:

String.Contains() чувствителен к регистру в запросе EF Core, хотя он нечувствителен к регистру в EF, переводимом в LIKE в SQL.

Ответы [ 2 ]

0 голосов
/ 06 февраля 2020

Параметр метода Where - это Expression, и его тело не имеет значения, когда запрос переводится в SQL. Вот почему вы получаете исключение.

Чтобы оно работало, вам нужно динамически построить выражение.

public static Expression<Func<T, bool>> Like<T>(Expression<Func<T, string>> prop, string keyword)
{
    var concatMethod = typeof(string).GetMethod(nameof(string.Concat), new[] { typeof(string), typeof(string) });
    return Expression.Lambda<Func<T, bool>>(
        Expression.Call(
            typeof(DbFunctionsExtensions),
            nameof(DbFunctionsExtensions.Like),
            null,
            Expression.Constant(EF.Functions),
            prop.Body,
            Expression.Add(
                Expression.Add(
                    Expression.Constant("%"),
                    Expression.Constant(keyword),
                    concatMethod),
                Expression.Constant("%"),
                concatMethod)),
        prop.Parameters);
}

И затем использовать его в своем запросе

if (!String.IsNullOrWhiteSpace(conditions.Keyword))
{
    query = query.Where(Like<User>(d => d.UserName, conditions.Keyword));
}

PS То, как вопрос озаглавлен, выглядит близко к тому, что Отображение скалярной функции , но это не применимо в случае предложения LIKE.

0 голосов
/ 04 февраля 2020

Вы можете использовать простой -> context.Users.Where (x => (условие.Keyword == null || x.UserName.Contains (condition.Keyword))). Если условие. Ключевое слово равно нулю, фильтр пропускается.

var query = from user in context.Users.Where(x => (conditions.Keyword == null || x.UserName.Contains(conditions.Keyword)))
                        join userRole in userRoleView
                        on user.Id equals userRole.UserId into gj
                        from p in gj.DefaultIfEmpty()
                        select new
                        {
                            user.Id,
                            user.UserName,
                            RoleName = p.Rolename,
                            user.CreatedUtc,
                            user.ModifiedUtc,
                        };
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...