Linq 如何在EntityFramework 7(Core)中按某些实体属性动态排序

Linq 如何在EntityFramework 7(Core)中按某些实体属性动态排序,linq,sql-order-by,asp.net-core,dynamic-sql,entity-framework-core,Linq,Sql Order By,Asp.net Core,Dynamic Sql,Entity Framework Core,我有一个项目,其中前端JavaScript指定了一个要排序的列列表 然后在后端我有多层应用程序。典型情景 服务层(服务模型(DTO)属性与客户端想要订购的内容匹配) 域层(它公开存储库接口以访问持久化对象) ORM层(它实现存储库,并使用实体框架7(又称实体框架核心)访问SQL Server数据库) 请注意,DNX Core v5.0或.NET Platform v5.4不支持System.Linq.Dynamic,因此我无法使用该库 我的存储库中有以下实现: public async

我有一个项目,其中前端JavaScript指定了一个要排序的列列表

然后在后端我有多层应用程序。典型情景

  • 服务层(服务模型(DTO)属性与客户端想要订购的内容匹配)
  • 域层(它公开存储库接口以访问持久化对象)
  • ORM层(它实现存储库,并使用实体框架7(又称实体框架核心)访问SQL Server数据库)
  • 请注意,DNX Core v5.0或.NET Platform v5.4不支持
    System.Linq.Dynamic
    ,因此我无法使用该库

    我的
    存储库中有以下实现:

        public async Task<IEnumerable<Thing>> GetThingsAsync(IEnumerable<SortModel> sortModels)
        {
            var query = GetThingsQueryable(sortModels);
            var things = await query.ToListAsync();
            return things;
        }
    
        private IQueryable<Thing> GetThingsQueryable(IEnumerable<SortModel> sortModels)
        {
    
            var thingsQuery = _context.Things
                    .Include(t => t.Other)
                    .Where(t => t.Deleted == false);
    
            // this is the problematic area as it does not return a valid queryable
            string orderBySqlStatement = GetOrderBySqlStatement(sortModels);
            thingsQuery = thingsQuery.FromSql(orderBySqlStatement);
            return thingsQuery ;
        }
    
        /// this returns something like " order by thingy1 asc, thingy2 desc"
        private string GetOrderBySqlStatement(IEnumerable<SortModel> sortModels)
        {
            IEnumerable<string> orderByParams = sortModels.Select(pair => { return pair.PairAsSqlExpression; });
            string orderByParamsConcat = string.Join(", ", orderByParams);
            string sqlStatement = orderByParamsConcat.Length > 1 ? $" order by {orderByParamsConcat}" : string.Empty;
            return sqlStatement;
        }
    
    这种方法尝试将SQL语句与为上一个可查询项创建的任何实体混合。但我得到一个:

    Microsoft.Data.Entity.Query.Internal.SqlServerQueryCompilationContextFactory:Verbose: Compiling query model: 'from Thing t in {value(Microsoft.Data.Entity.Query.Internal.EntityQueryable`1[MyTestProj.Data.Models.Thing]) => AnnotateQuery(Include([t].DeparturePort)) => AnnotateQuery(Include([t].ArrivalPort)) => AnnotateQuery(Include([t].Consignments))} where (([t].CreatorBusinessId == __businessId_0) AndAlso (Convert([t].Direction) == __p_1)) select [t] => AnnotateQuery(QueryAnnotation(FromSql(value(Microsoft.Data.Entity.Query.Internal.EntityQueryable`1[MyTestProj.Data.Models.Thing]), " order by arrivalDate asc, arrivalPortCode asc", []))) => Count()'
    Microsoft.Data.Entity.Query.Internal.SqlServerQueryCompilationContextFactory:Verbose: Optimized query model: 'from Thing t in value(Microsoft.Data.Entity.Query.Internal.EntityQueryable`1[MyTestProj.Data.Models.Thing]) where (([t].CreatorBusinessId == __businessId_0) AndAlso (Convert([t].Direction) == __p_1)) select [t] => Count()'
    Microsoft.Data.Entity.Query.Internal.QueryCompiler:Error: An exception occurred in the database while iterating the results of a query.
    System.InvalidOperationException: The Include operation is not supported when calling a stored procedure.
       at Microsoft.Data.Entity.Query.ExpressionVisitors.RelationalEntityQueryableExpressionVisitor.VisitEntityQueryable(Type elementType)
       at Microsoft.Data.Entity.Query.ExpressionVisitors.EntityQueryableExpressionVisitor.VisitConstant(ConstantExpression constantExpression)
       at System.Linq.Expressions.ConstantExpression.Accept(ExpressionVisitor visitor)
       at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
       at Microsoft.Data.Entity.Query.ExpressionVisitors.ExpressionVisitorBase.Visit(Expression expression)
       at Microsoft.Data.Entity.Query.EntityQueryModelVisitor.ReplaceClauseReferences(Expression expression, IQuerySource querySource, Boolean inProjection)
       at Microsoft.Data.Entity.Query.EntityQueryModelVisitor.CompileMainFromClauseExpression(MainFromClause mainFromClause, QueryModel queryModel)
       at Microsoft.Data.Entity.Query.RelationalQueryModelVisitor.CompileMainFromClauseExpression(MainFromClause mainFromClause, QueryModel queryModel)
       at Microsoft.Data.Entity.Query.EntityQueryModelVisitor.VisitMainFromClause(MainFromClause fromClause, QueryModel queryModel)
       at Remotion.Linq.Clauses.MainFromClause.Accept(IQueryModelVisitor visitor, QueryModel queryModel)
       at Remotion.Linq.QueryModelVisitorBase.VisitQueryModel(QueryModel queryModel)
       at Microsoft.Data.Entity.Query.EntityQueryModelVisitor.VisitQueryModel(QueryModel queryModel)
       at Microsoft.Data.Entity.Query.RelationalQueryModelVisitor.VisitQueryModel(QueryModel queryModel)
       at Microsoft.Data.Entity.Query.Internal.SqlServerQueryModelVisitor.VisitQueryModel(QueryModel queryModel)
       at Microsoft.Data.Entity.Query.EntityQueryModelVisitor.CreateAsyncQueryExecutor[TResult](QueryModel queryModel)
       at Microsoft.Data.Entity.Storage.Database.CompileAsyncQuery[TResult](QueryModel queryModel)
       at Microsoft.Data.Entity.Query.Internal.QueryCompiler.<>c__DisplayClass19_0`1.<CompileAsyncQuery>b__0()
       at Microsoft.Data.Entity.Query.Internal.CompiledQueryCache.GetOrAddAsyncQuery[TResult](Object cacheKey, Func`1 compiler)
       at Microsoft.Data.Entity.Query.Internal.QueryCompiler.CompileAsyncQuery[TResult](Expression query)
       at Microsoft.Data.Entity.Query.Internal.QueryCompiler.ExecuteAsync[TResult](Expression query, CancellationToken cancellationToken)
    Exception thrown: 'System.InvalidOperationException' in EntityFramework.Core.dll
    Exception thrown: 'System.InvalidOperationException' in mscorlib.ni.dll
    Exception thrown: 'System.InvalidOperationException' in mscorlib.ni.dll
    Microsoft.AspNet.Diagnostics.Entity.DatabaseErrorPageMiddleware:Verbose: System.InvalidOperationException occurred, checking if Entity Framework recorded this exception as resulting from a failed database operation.
    Microsoft.AspNet.Diagnostics.Entity.DatabaseErrorPageMiddleware:Verbose: Entity Framework recorded that the current exception was due to a failed database operation. Attempting to show database error page.
    Microsoft.Data.Entity.Storage.Internal.SqlServerConnection:Verbose: Opening connection 'Server=(localdb)\mssqllocaldb;Database=SpeediCargo;Trusted_Connection=True;MultipleActiveResultSets=true'.
    Microsoft.Data.Entity.Storage.Internal.SqlServerConnection:Verbose: Closing connection 'Server=(localdb)\mssqllocaldb;Database=SpeediCargo;Trusted_Connection=True;MultipleActiveResultSets=true'.
    Microsoft.Data.Entity.Storage.Internal.SqlServerConnection:Verbose: Opening connection 'Server=(localdb)\mssqllocaldb;Database=SpeediCargo;Trusted_Connection=True;MultipleActiveResultSets=true'.
    Microsoft.Data.Entity.Storage.Internal.SqlServerConnection:Verbose: Closing connection 'Server=(localdb)\mssqllocaldb;Database=SpeediCargo;Trusted_Connection=True;MultipleActiveResultSets=true'.
    Microsoft.Data.Entity.Storage.Internal.SqlServerConnection:Verbose: Opening connection 'Server=(localdb)\mssqllocaldb;Database=SpeediCargo;Trusted_Connection=True;MultipleActiveResultSets=true'.
    Microsoft.Data.Entity.Storage.Internal.RelationalCommandBuilderFactory:Information: Executed DbCommand (0ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
    SELECT OBJECT_ID(N'__EFMigrationsHistory');
    Microsoft.Data.Entity.Storage.Internal.SqlServerConnection:Verbose: Closing connection 'Server=(localdb)\mssqllocaldb;Database=SpeediCargo;Trusted_Connection=True;MultipleActiveResultSets=true'.
    Microsoft.Data.Entity.Storage.Internal.SqlServerConnection:Verbose: Opening connection 'Server=(localdb)\mssqllocaldb;Database=SpeediCargo;Trusted_Connection=True;MultipleActiveResultSets=true'.
    Microsoft.Data.Entity.Storage.Internal.RelationalCommandBuilderFactory:Information: Executed DbCommand (0ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
    
    Microsoft.Data.Entity.Query.Internal.SqlServerQueryCompilationContextFactory:Verbose:compileding查询模型:{value(Microsoft.Data.Entity.Query.Internal.EntityQueryable`1[MyTestProj.Data.Models.Thing])=>AnnotateQuery(Include([t].DeparturePort))=>AnnotateQuery(Include([t].ArrivalPort))=>AnnotateQuery(Include([t].Commentations))}其中([t].CreatorBusinessId====\u businessId\u 0)和(Convert([t].Direction)=\u p\u 1))选择[t]=>AnnotateQuery(QueryAnnotation(从SQL(值(Microsoft.Data.Entity.Query.Internal.EntityQueryable`1[MyTestProj.Data.Models.Thing]),“按到达日期的订单asc,到达港代码asc”,[])=>Count()
    Microsoft.Data.Entity.Query.Internal.SqlServerQueryCompilationContextFactory:Verbose:Optimized Query model:'从值为t的对象(Microsoft.Data.Entity.Query.Internal.EntityQueryable`1[MyTestProj.Data.Models.Thing]),其中([t].CreatorBusinessId===\u businessId\u 0)和(转换([t].Direction=\u p\u 1))选择[t]=>Count()
    Microsoft.Data.Entity.Query.Internal.QueryCompiler:错误:在迭代查询结果时,数据库中发生异常。
    System.InvalidOperationException:调用存储过程时不支持Include操作。
    位于Microsoft.Data.Entity.Query.ExpressionVisitors.RelationalEntityQueryableExpressionVisitor.VisitEntityQueryable(类型elementType)
    位于Microsoft.Data.Entity.Query.ExpressionVisitors.EntityQueryableExpressionVisitor.VisitConstant(ConstantPression ConstantPression)
    at System.Linq.Expressions.ConstantExpression.Accept(ExpressionVisitor)
    位于System.Linq.Expressions.ExpressionVisitor.Visite(表达式节点)
    在Microsoft.Data.Entity.Query.ExpressionVisitors.ExpressionVisitorBase.Visit(表达式)中
    位于Microsoft.Data.Entity.Query.EntityQueryModelVisitor.ReplaceClauseReferences(表达式表达式、IQuerySource查询源、布尔inProjection)
    在Microsoft.Data.Entity.Query.EntityQueryModelVisitor.CompileManufomClauseExpression(MainFromClause MainFromClause,QueryModel QueryModel)
    在Microsoft.Data.Entity.Query.RelationalQueryModelVisitor.CompileManufomClause表达式(MainFromClause MainFromClause,QueryModel QueryModel)
    在Microsoft.Data.Entity.Query.EntityQueryModelVisitor.VisitManufomClause(MainFromClause fromClause,QueryModel QueryModel)
    at Remotion.Linq.子句.MainFromClause.Accept(IQueryModelVisitor、QueryModelQueryModel)
    在Remotion.Linq.QueryModelVisitorBase.VisitQueryModel(QueryModel QueryModel)
    位于Microsoft.Data.Entity.Query.EntityQueryModelVisitor.VisitQueryModel(QueryModel QueryModel)
    位于Microsoft.Data.Entity.Query.RelationalQueryModelVisitor.VisitQueryModel(QueryModel QueryModel)
    位于Microsoft.Data.Entity.Query.Internal.SqlServerQueryModelVisitor.VisitQueryModel(QueryModel QueryModel)
    在Microsoft.Data.Entity.Query.EntityQueryModelVisitor.CreateAyncQueryExecutor[TResult](QueryModel QueryModel)中
    位于Microsoft.Data.Entity.Storage.Database.CompileAsyncQuery[TResult](QueryModel QueryModel)
    在Microsoft.Data.Entity.Query.Internal.QueryCompiler.c_uuuDisplayClass19_0`1.b_uu0()中
    在Microsoft.Data.Entity.Query.Internal.CompiledQueryCache.GetOrAddAsyncQuery[TResult](对象缓存键,Func`1编译器)
    位于Microsoft.Data.Entity.Query.Internal.QueryCompiler.CompileAsyncQuery[TResult](表达式查询)
    在Microsoft.Data.Entity.Query.Internal.QueryCompiler.ExecuteAsync[TResult](表达式查询,CancellationToken CancellationToken)
    引发异常:EntityFramework.Core.dll中的“System.InvalidOperationException”
    引发异常:mscorlib.ni.dll中的“System.InvalidOperationException”
    引发异常:mscorlib.ni.dll中的“System.InvalidOperationException”
    Microsoft.AspNet.Diagnostics.Entity.DatabaseErrorPageMiddleware:Verbose:System.InvalidOperationException发生,检查Entity Framework是否将此异常记录为数据库操作失败导致的异常。
    Microsoft.AspNet.Diagnostics.Entity.DatabaseErrorPageMiddleware:Verbose:Entity Framework记录到当前异常是由于数据库操作失败造成的。正在尝试显示数据库错误页。
    Microsoft.Data.Entity.Storage.Internal.SqlServerConnection:Verbose:打开连接'Server=(localdb)\MSSQLServerLocalDB;数据库=SpeediCargo;Trusted_Connection=True;MultipleActiveResultSets=true'。
    Microsoft.Data.Entity.Storage.Internal.SqlServerConnection:Verbose:Closing connection'Server=(localdb)\MSSQLServerLocalDB;数据库=SpeediCargo;Trusted_Connection=True;MultipleActiveResultSets=true'。
    Microsoft.Data.Entity.Storage.Internal.SqlServerConnection:Verbose:打开连接'Server=(localdb)\MSSQLServerLocalDB;数据库=SpeediCargo;Trusted_Connection=True;MultipleActiveResultSets=true'。
    Microsoft.Data.Entity.Storage.Internal.SqlServerConnection:Verbose:Closing connection'Server=(localdb)\MSSQLServerLocalDB;数据库=SpeediCargo;Trusted_Connection=True;MultipleActiveResultSets=true'。
    Microsoft.Data.Entity.Storage.Internal.SqlServerConnection:Verbose:打开连接'Server=(localdb)\MSSQLServerLocalDB;数据库=SpeediCargo;信任
    
    Microsoft.Data.Entity.Query.Internal.SqlServerQueryCompilationContextFactory:Verbose: Compiling query model: 'from Thing t in {value(Microsoft.Data.Entity.Query.Internal.EntityQueryable`1[MyTestProj.Data.Models.Thing]) => AnnotateQuery(Include([t].DeparturePort)) => AnnotateQuery(Include([t].ArrivalPort)) => AnnotateQuery(Include([t].Consignments))} where (([t].CreatorBusinessId == __businessId_0) AndAlso (Convert([t].Direction) == __p_1)) select [t] => AnnotateQuery(QueryAnnotation(FromSql(value(Microsoft.Data.Entity.Query.Internal.EntityQueryable`1[MyTestProj.Data.Models.Thing]), " order by arrivalDate asc, arrivalPortCode asc", []))) => Count()'
    Microsoft.Data.Entity.Query.Internal.SqlServerQueryCompilationContextFactory:Verbose: Optimized query model: 'from Thing t in value(Microsoft.Data.Entity.Query.Internal.EntityQueryable`1[MyTestProj.Data.Models.Thing]) where (([t].CreatorBusinessId == __businessId_0) AndAlso (Convert([t].Direction) == __p_1)) select [t] => Count()'
    Microsoft.Data.Entity.Query.Internal.QueryCompiler:Error: An exception occurred in the database while iterating the results of a query.
    System.InvalidOperationException: The Include operation is not supported when calling a stored procedure.
       at Microsoft.Data.Entity.Query.ExpressionVisitors.RelationalEntityQueryableExpressionVisitor.VisitEntityQueryable(Type elementType)
       at Microsoft.Data.Entity.Query.ExpressionVisitors.EntityQueryableExpressionVisitor.VisitConstant(ConstantExpression constantExpression)
       at System.Linq.Expressions.ConstantExpression.Accept(ExpressionVisitor visitor)
       at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
       at Microsoft.Data.Entity.Query.ExpressionVisitors.ExpressionVisitorBase.Visit(Expression expression)
       at Microsoft.Data.Entity.Query.EntityQueryModelVisitor.ReplaceClauseReferences(Expression expression, IQuerySource querySource, Boolean inProjection)
       at Microsoft.Data.Entity.Query.EntityQueryModelVisitor.CompileMainFromClauseExpression(MainFromClause mainFromClause, QueryModel queryModel)
       at Microsoft.Data.Entity.Query.RelationalQueryModelVisitor.CompileMainFromClauseExpression(MainFromClause mainFromClause, QueryModel queryModel)
       at Microsoft.Data.Entity.Query.EntityQueryModelVisitor.VisitMainFromClause(MainFromClause fromClause, QueryModel queryModel)
       at Remotion.Linq.Clauses.MainFromClause.Accept(IQueryModelVisitor visitor, QueryModel queryModel)
       at Remotion.Linq.QueryModelVisitorBase.VisitQueryModel(QueryModel queryModel)
       at Microsoft.Data.Entity.Query.EntityQueryModelVisitor.VisitQueryModel(QueryModel queryModel)
       at Microsoft.Data.Entity.Query.RelationalQueryModelVisitor.VisitQueryModel(QueryModel queryModel)
       at Microsoft.Data.Entity.Query.Internal.SqlServerQueryModelVisitor.VisitQueryModel(QueryModel queryModel)
       at Microsoft.Data.Entity.Query.EntityQueryModelVisitor.CreateAsyncQueryExecutor[TResult](QueryModel queryModel)
       at Microsoft.Data.Entity.Storage.Database.CompileAsyncQuery[TResult](QueryModel queryModel)
       at Microsoft.Data.Entity.Query.Internal.QueryCompiler.<>c__DisplayClass19_0`1.<CompileAsyncQuery>b__0()
       at Microsoft.Data.Entity.Query.Internal.CompiledQueryCache.GetOrAddAsyncQuery[TResult](Object cacheKey, Func`1 compiler)
       at Microsoft.Data.Entity.Query.Internal.QueryCompiler.CompileAsyncQuery[TResult](Expression query)
       at Microsoft.Data.Entity.Query.Internal.QueryCompiler.ExecuteAsync[TResult](Expression query, CancellationToken cancellationToken)
    Exception thrown: 'System.InvalidOperationException' in EntityFramework.Core.dll
    Exception thrown: 'System.InvalidOperationException' in mscorlib.ni.dll
    Exception thrown: 'System.InvalidOperationException' in mscorlib.ni.dll
    Microsoft.AspNet.Diagnostics.Entity.DatabaseErrorPageMiddleware:Verbose: System.InvalidOperationException occurred, checking if Entity Framework recorded this exception as resulting from a failed database operation.
    Microsoft.AspNet.Diagnostics.Entity.DatabaseErrorPageMiddleware:Verbose: Entity Framework recorded that the current exception was due to a failed database operation. Attempting to show database error page.
    Microsoft.Data.Entity.Storage.Internal.SqlServerConnection:Verbose: Opening connection 'Server=(localdb)\mssqllocaldb;Database=SpeediCargo;Trusted_Connection=True;MultipleActiveResultSets=true'.
    Microsoft.Data.Entity.Storage.Internal.SqlServerConnection:Verbose: Closing connection 'Server=(localdb)\mssqllocaldb;Database=SpeediCargo;Trusted_Connection=True;MultipleActiveResultSets=true'.
    Microsoft.Data.Entity.Storage.Internal.SqlServerConnection:Verbose: Opening connection 'Server=(localdb)\mssqllocaldb;Database=SpeediCargo;Trusted_Connection=True;MultipleActiveResultSets=true'.
    Microsoft.Data.Entity.Storage.Internal.SqlServerConnection:Verbose: Closing connection 'Server=(localdb)\mssqllocaldb;Database=SpeediCargo;Trusted_Connection=True;MultipleActiveResultSets=true'.
    Microsoft.Data.Entity.Storage.Internal.SqlServerConnection:Verbose: Opening connection 'Server=(localdb)\mssqllocaldb;Database=SpeediCargo;Trusted_Connection=True;MultipleActiveResultSets=true'.
    Microsoft.Data.Entity.Storage.Internal.RelationalCommandBuilderFactory:Information: Executed DbCommand (0ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
    SELECT OBJECT_ID(N'__EFMigrationsHistory');
    Microsoft.Data.Entity.Storage.Internal.SqlServerConnection:Verbose: Closing connection 'Server=(localdb)\mssqllocaldb;Database=SpeediCargo;Trusted_Connection=True;MultipleActiveResultSets=true'.
    Microsoft.Data.Entity.Storage.Internal.SqlServerConnection:Verbose: Opening connection 'Server=(localdb)\mssqllocaldb;Database=SpeediCargo;Trusted_Connection=True;MultipleActiveResultSets=true'.
    Microsoft.Data.Entity.Storage.Internal.RelationalCommandBuilderFactory:Information: Executed DbCommand (0ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
    
    public static class QueryableExtensions
    {
        public static IQueryable<T> OrderBy<T>(this IQueryable<T> source, IEnumerable<SortModel> sortModels)
        {
            var expression = source.Expression;
            int count = 0;
            foreach (var item in sortModels)
            {
                var parameter = Expression.Parameter(typeof(T), "x");
                var selector = Expression.PropertyOrField(parameter, item.ColId);
                var method = string.Equals(item.Sort, "desc", StringComparison.OrdinalIgnoreCase) ?
                    (count == 0 ? "OrderByDescending" : "ThenByDescending") :
                    (count == 0 ? "OrderBy" : "ThenBy");
                expression = Expression.Call(typeof(Queryable), method,
                    new Type[] { source.ElementType, selector.Type },
                    expression, Expression.Quote(Expression.Lambda(selector, parameter)));
                count++;
            }
            return count > 0 ? source.Provider.CreateQuery<T>(expression) : source;
        }
    }
    
    var thingsQuery = _context.Things
            .Include(t => t.Other)
            .Where(t => t.Deleted == false)
            .OrderBy(sortModels);