在实体框架核心中扩展DbFunction?

时间:2020-02-04 01:28:58

标签: c# .net linq asp.net-core entity-framework-core

有了.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()或{{1}的调用来显式切换到客户端评估}。有关更多信息,请参见https://go.microsoft.com/fwlink/?linkid=2101038

Source = Microsoft.EntityFrameworkCore

StackTrace: 在Microsoft.EntityFrameworkCore.Query.QueryableMethodTranslatingExpressionVisitor.g__CheckTranslated | 8_0(ShapedQueryExpression翻译,<> 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(表达式节点)上
在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.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.QueryCompilationContext.CreateQueryExecutor [TResult](表达式查询) 在Microsoft.EntityFrameworkCore.Storage.Database.CompileQuery [TResult](表达式查询,布尔异步) 在Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.CompileQueryCore [TResult](IDatabase数据库,表达式查询,IModel模型,布尔异步) 在Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler。<> c__DisplayClass9_0 ToListAsync() 1编译器处) 在Microsoft.EntityFrameworkCore.Query.Internal.CompiledQueryCache.GetOrAddQuery [TResult](对象cacheKey,Func 1.<Execute>b__0() at Microsoft.EntityFrameworkCore.Query.Internal.CompiledQueryCache.GetOrAddQueryCore[TFunc](Object cacheKey, Func 1.GetEnumerator() 在System.Collections.Generic.LargeArrayBuilder 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个项目中) 在System.Collections.Generic.EnumerableHelpers.ToArray [T](IEnumerable 1.AddRange(IEnumerable 1源)处 在C:\ VSProjects \ ApsCloudTrunk \ APS.WebPos.DALCore \ SearchOperations.cs:line 96中的APS.WebPos.DAL.SearchOperations.GetActivePeopleByKeyword(String关键字) 在C:\ VSProjects \ ApsCloudTrunk \ APS.WebPos.WebApiCore \ Controllers \ SearchController.cs:第25行的APS.WebPos.WebApi.Controllers.SearchController.GetActivePeopleByKeyword(String关键字)处 在Microsoft.Extensions.Internal.ObjectMethodExecutor.Execute(Object target,Object []参数) 在Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.SyncObjectResultExecutor.Execute(IActionResultTypeMapper映射器,ObjectMethodExecutor执行器,对象控制器,Object []参数) 在Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeActionMethodAsync() 在Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(状态和下一个,范围和范围,对象和状态,布尔值和isCompleted)处 在Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeNextActionFilterAsync()

是否可以在具有Entity Framework Core的应用程序中扩展DbFunction,并在LINQ中使用它?怎么样?

备注:

1 source) at System.Linq.Enumerable.ToArray[TSource](IEnumerable在EF Core查询中区分大小写,尽管在将EF转换为SQL的LIKE中不区分大小写。

2 个答案:

答案 0 :(得分:0)

您可以使用简单的一个-> context.Users.Where(x =>(conditions.Keyword == null || x.UserName.Contains(conditions.Keyword)))。如果condition.Keyword为null,则跳过过滤器。

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

答案 1 :(得分:0)

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

P.S。问题的标题方式看起来与Scalar function mapping相似,但不适用于LIKE子句。