首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >Linq2DB ( Server):动态链接Sql.Property<T>调用

Linq2DB ( Server):动态链接Sql.Property<T>调用
EN

Stack Overflow用户
提问于 2021-01-18 20:33:28
回答 2查看 809关注 0票数 0

我正在使用这个拉请求中的示例作为嵌套属性链的引用。不幸的是

代码语言:javascript
运行
复制
Sql.Property<object>(x, "SomeClass.SomeProperty") 

不起作用。

假设我有一个模型SortDescriptor (下面定义),它定义了我们如何动态地对代码中的项排序。是否有一种方法可以动态创建表达式树以生成嵌套的Sql.Property调用,如下面所示的ToPropertyExpression方法?

如果给定以下筛选器,则应从SortBy函数输出以下SQL:

代码语言:javascript
运行
复制
ORDER BY Category.EntityId, StackRank, Id

(CategoryCategoryProperty上的一个导航属性)

代码语言:javascript
运行
复制
        var filter1 = new PagedFilter
        {
            Take = 25,
            Sort = new List<SortDescriptor>
            {
                new SortDescriptor { Selector = "Category.EntityId" },
                new SortDescriptor { Selector = "StackRank" },
                new SortDescriptor { Selector = "Id" },
            }
        };

下面是我使用的函数和对象:

代码语言:javascript
运行
复制
public class CategoryPropertyRepository 
{
    public async Task<long> GetPageIndexById(PagedFilter filter, Guid id)
    {
        var entity = await DbContext.Set<Entities.CategoryProperty>()
            .Select(x => new
            {
                x.Id,
                RowNumber = Sql.Ext.RowNumber().Over().SortBy(x, filter.Sort).ToValue()
            }).FirstOrDefaultAsyncLinqToDB(x => x.Id == id);
        var rowNumber = entity.RowNumber;

        return rowNumber / filter.Take.Value;
    }
}

public static class IOrderExtensions
{
    [Sql.Extension("ORDER BY {entity}{filter}", TokenName = "order_by_clause", ServerSideOnly = true, BuilderType = typeof(SortByBuilder))]
    public static AnalyticFunctions.IOrderedReadyToFunction<T> SortBy<T, TEntity>(
        this AnalyticFunctions.IOverMayHavePartitionAndOrder<T> over, TEntity entity, IPagedFilter filter) => throw new InvalidOperationException("SortBy is server-side only.");

    public class SortByBuilder : Sql.IExtensionCallBuilder
    {
        public void Build(Sql.ISqExtensionBuilder builder)
        {
            var entity = builder.Arguments[1];
            var filter = builder.GetValue<IPagedFilter>("filter");
            var index = 0;
            var expression = $"ORDER BY {string.Join(", ", filter.Sort.Select(x => $"{{{index++}}}{(x.Descending ? " DESC" : string.Empty)}"))}";

            List<ISqlExpression> parameters = new List<ISqlExpression>();

            foreach (var sort in filter.Sort)
            {
                var sqlExpr = builder.ConvertExpressionToSql(sort.ToPropertyExpression(entity));
                parameters.Add(sqlExpr);
            }

            builder.ResultExpression = new SqlExpression(expression, Precedence.Primary, parameters.ToArray());
        }
    }

    public static Expression ToPropertyExpression(this SortDescriptor sort, object entity)
    {
        var nameParts = sort.Selector.Split('.');

        // x.SomeClass.SomeProperty should yield something like Sql.Property<object>(Sql.Property<object>(x, "SomeClass"), "SomeProperty);
        var propertyMethod = typeof(Sql).GetMethod("Property", BindingFlags.Public | BindingFlags.Static);
        propertyMethod = propertyMethod!.MakeGenericMethod(typeof(object));
        Expression exp = null;

        for (int i = nameParts.Length - 1; i >= 0; i--)
        {
            exp = Expression.Call(null, propertyMethod, Expression.Constant(exp ?? entity),
                Expression.Constant(nameParts[i]));
        }

        return exp;
    }
}

public class PagedFilter : IPagedFilter
{
    public virtual int? Skip { get; set; }
    public virtual int? Take { get; set; }
    public virtual IList<SortDescriptor> Sort { get; set; }
}

public class SortDescriptor
{
    public string Selector { get; set; }
    public bool Descending { get; set; }
}

每当我尝试执行类似于上面的内容时,我都会收到这样的错误消息,它试图在客户端计算Sql.Property表达式链:

异常消息: LinqToDB.Linq.LinqException:“Property”只是服务器端的方法。 堆栈跟踪: 在LinqToDB.Sql.PropertyT at LinqToDB.Linq.QueryRunner.SetParameters(查询、表达式表达式、IDataContext parametersContext、Object[]参数、Int32 queryNumber、SqlParameterValues parameterValues) 在LinqToDB.Linq.QueryRunnerBase.SetCommand(Boolean clearQueryHints) 在LinqToDB.Data.DataConnection.QueryRunner.<>n__0(Boolean clearQueryHints) 在LinqToDB.Data.DataConnection.QueryRunner.ExecuteReaderAsync(CancellationToken cancellationToken) 在LinqToDB.Linq.QueryRunner.AsyncEnumeratorImpl1.MoveNextAsync() at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ToListAsync[TSource](IQueryable1源代码,CancellationToken cancellationToken) 在Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ToListAsyncTSource at LinqToDB.AsyncExtensions.ToListAsyncTSource 在Experlogix.Api.DesignStudio.DataAccess.CategoryPropertyRepository.GetPageIndexById(CategoryPropertyFilter滤波器上,D:\Dev\repos\Experlogix.Api.DesignStudio\src\Experlogix.Api.DesignStudio.DataAccess\CategoryPropertyRepository.cs:line 116中的Guid ) 在Experlogix.Core.DataAccess.Service6.GetPageIndexById(TFilter filter, TId id) at Experlogix.Api.DesignStudio.Controllers.CategoryPropertyController.GetPageIndexAsync(Guid id, CategoryPropertyFilter filter) in D:\Dev\repos\Experlogix.Api.DesignStudio\src\Experlogix.Api.DesignStudio\Controllers\CategoryPropertyController.cs:line 46 at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.TaskOfIActionResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments) at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeActionMethodAsync>g__Awaited|12_0(ControllerActionInvoker invoker, ValueTask1 actionResultValueTask) 在Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.g__Awaited|10_0(ControllerActionInvoker调用程序,任务lastTask,状态下一步,范围范围,对象状态,布尔isCompleted) (在Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed上下文中) 在Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next,范围和作用域,对象和状态,布尔& isCompleted) 在Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.g__Awaited|13_0(ControllerActionInvoker调用程序,任务lastTask,状态下一步,范围范围,对象状态,布尔isCompleted) 在Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Awaited|19_0(ResourceInvoker调用程序,任务lastTask,状态下一步,范围范围,对象状态,布尔isCompleted) 在Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Awaited|17_0(ResourceInvoker调用程序,任务任务,IDisposable作用域) 在Microsoft.AspNetCore.Routing.EndpointMiddleware.g__AwaitRequestTask|6_0(Endpoint端点,任务requestTask,ILogger记录器) 在Experlogix.Api.DesignStudio.Middleware.DatabaseConnectionMiddleware.Invoke(HttpContext httpContext,IUserContext userContext,IConnectionContext connectionContext,IDatabaseClient databaseClient,在D:\Dev\repos\Experlogix.Api.DesignStudio\src\Experlogix.Api.DesignStudio\Middleware\DatabaseConnectionMiddleware.cs:line 65 (在Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext上下文中) 在Serilog.AspNetCore.RequestLoggingMiddleware.Invoke(HttpContext httpContext) 在Swashbuckle.AspNetCore.SwaggerUI.SwaggerUIMiddleware.Invoke(HttpContext httpContext) 在Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext,ISwaggerProvider swaggerProvider) (在Experlogix.Core.AspNet.Middleware.ApiExceptionHandlerMiddleware.Invoke(HttpContext上下文中)

任何关于如何实现类似的东西的指导将是非常感谢的!

环境详细信息

  • linq2db版本: linq2db.EntityFrameworkCore 5.1.0
  • 数据库服务器: Server
  • 操作系统: Windows 10
  • .NET框架:.Net 5.0
EN

回答 2

Stack Overflow用户

发布于 2021-01-18 21:25:09

通过这样更改ToPropertyExpression,我能够使查询按需要工作:

代码语言:javascript
运行
复制
    public static Expression ToPropertyExpression(this SortDescriptor sort, Expression entity)
    {
        return sort.Selector.Split('.').Aggregate(entity, Expression.Property);
    }

所有其他代码保持不变,并生成以下SQL查询:

代码语言:javascript
运行
复制
SELECT
    [f].[Id],
    ROW_NUMBER() OVER(ORDER BY [a_Category].[EntityId], [f].[StackRank], [f].[Id])
FROM
    [ds].[CategoryProperty] [f]
        INNER JOIN [ds].[Category] [a_Category] ON [f].[CategoryId] = [a_Category].[Id]
票数 0
EN

Stack Overflow用户

发布于 2021-01-19 17:02:48

我提出了更通用的分页解决方案。它可以处理任何查询并构建正确的SQL。此外,它还可以通过单次往返返回TotalCount到数据库。

实现并不简单,但可以用作如何使用表达式树的参考源。

用法示例:

代码语言:javascript
运行
复制
var pageSize = 20;

var query = table.Where(x => x.Id % 2 == 0).OrderBy(x => x.Id).ThenByDescending(x => x.Value);
var pagination1 = query.Paginate(1, pageSize);
var pagination2 = query.Paginate(2, pageSize, true); // with total count

// extensions which accepts predicate to find page. Query must be ordered.
var byKey = query.GetPageByCondition(pageSize, x => x.Id == someId);

// returns page number. Query must be ordered.
var pageNumber = query.GetPageNumberByCondition(pageSize, x => x.Id == someId);

// dynamic ordering
var query = table.Where(x => x.Id % 2 == 0)
   .ApplyOrderBy(new []{Tuple.Create("Id", false), Tuple.Create("Value", true)});

Implementation:

代码语言:javascript
运行
复制
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using LinqToDB;
using LinqToDB.Async;
using LinqToDB.Expressions;

namespace Tests.Playground
{
    public static class PaginationExtensions
    {
        public class PaginationResult<T>
        {
            public PaginationResult(int totalCount, int page, int pageSize, List<T> items)
            {
                TotalCount = totalCount;
                Page = page;
                PageSize = pageSize;
                Items = items;
            }

            public int TotalCount { get; }
            public List<T> Items { get; }
            public int Page { get; }
            public int PageSize { get; }
        }

        public static PaginationResult<T> Paginate<T>(this IQueryable<T> query, int page, int pageSize, bool includeTotalCount = false)
        {
            return ProcessPaginationResult(EnvelopeQuery(query, page, pageSize, includeTotalCount), pageSize);
        }

        public static Task<PaginationResult<T>> PaginateAsync<T>(this IQueryable<T> query, int page, int pageSize, bool includeTotalCount = false, CancellationToken cancellationToken = default)
        {
            return ProcessPaginationResultAsync(EnvelopeQuery(query, page, pageSize, includeTotalCount), pageSize, cancellationToken);
        }

        public static Expression ApplyOrderBy(Type entityType, Expression queryExpr, IEnumerable<Tuple<string, bool>> order)
        {
            var param = Expression.Parameter(entityType, "e");
            var isFirst = true;
            foreach (var tuple in order)
            {
                var lambda = Expression.Lambda(MakePropPath(param, tuple.Item1), param);
                var methodName =
                    isFirst ? tuple.Item2 ? nameof(Queryable.OrderByDescending) : nameof(Queryable.OrderBy)
                    : tuple.Item2 ? nameof(Queryable.ThenByDescending) : nameof(Queryable.ThenBy);

                queryExpr = Expression.Call(typeof(Queryable), methodName, new[] { entityType, lambda.Body.Type }, queryExpr, lambda);
                isFirst = false;
            }

            return queryExpr;
        }

        public static PaginationResult<T> GetPageByCondition<T>(this IQueryable<T> query, int pageSize,
            Expression<Func<T, bool>> predicate, bool includeTotal = false)
        {
            return ProcessPaginationResult(GetPageByConditionInternal(query, pageSize, predicate, includeTotal), pageSize);
        }

        public static int GetPageNumberByCondition<T>(this IQueryable<T> query, int pageSize,
            Expression<Func<T, bool>> predicate, bool includeTotal = false)
        {
            return GetPageNumberByConditionInternal(query, pageSize, predicate, includeTotal).FirstOrDefault();
        }

        public static Task<int> GetPageNumberByConditionAsync<T>(this IQueryable<T> query, int pageSize,
            Expression<Func<T, bool>> predicate, bool includeTotal = false, CancellationToken cancellationToken = default)
        {
            return GetPageNumberByConditionInternal(query, pageSize, predicate, includeTotal).FirstOrDefaultAsync(cancellationToken);
        }

        public static Task<PaginationResult<T>> GetPageByConditionAsync<T>(this IQueryable<T> query, int pageSize,
            Expression<Func<T, bool>> predicate, bool includeTotal = false, CancellationToken cancellationToken = default)
        {
            return ProcessPaginationResultAsync(GetPageByConditionInternal(query, pageSize, predicate, includeTotal), pageSize, cancellationToken);
        }

        public static IQueryable<T> ApplyOrderBy<T>(this IQueryable<T> query, IEnumerable<Tuple<string, bool>> order)
        {
            var expr = ApplyOrderBy(typeof(T), query.Expression, order);
            return query.Provider.CreateQuery<T>(expr);
        }

        #region Helpers

        static Expression? Unwrap(Expression? ex)
        {
            if (ex == null)
                return null;

            switch (ex.NodeType)
            {
                case ExpressionType.Quote:
                case ExpressionType.ConvertChecked:
                case ExpressionType.Convert:
                    return ((UnaryExpression)ex).Operand.Unwrap();
            }

            return ex;
        }

        static MethodInfo? FindMethodInfoInType(Type type, string methodName, int paramCount)
        {
            var method = type.GetRuntimeMethods()
            .FirstOrDefault(m => m.Name == methodName && m.GetParameters().Length == paramCount);
            return method;
        }

        static MethodInfo FindMethodInfo(Type type, string methodName, int paramCount)
        {
            var method = FindMethodInfoInType(type, methodName, paramCount);

            if (method != null)
                return method;

            method = type.GetInterfaces().Select(it => FindMethodInfoInType(it, methodName, paramCount))
                .FirstOrDefault(m => m != null);

            if (method == null)
                throw new Exception($"Method '{methodName}' not found in type '{type.Name}'.");

            return method;
        }


        static Expression ExtractOrderByPart(Expression query, List<Tuple<Expression, bool>> orderBy)
        {
            var current = query;
            while (current.NodeType == ExpressionType.Call)
            {
                var mc = (MethodCallExpression)current;
                if (typeof(Queryable) == mc.Method.DeclaringType)
                {
                    var supported = true;
                    switch (mc.Method.Name)
                    {
                        case "OrderBy":
                        case "ThenBy":
                        {
                            orderBy.Add(Tuple.Create(mc.Arguments[1], false));
                            break;
                        }
                        case "OrderByDescending":
                        case "ThenByDescending":
                        {
                            orderBy.Add(Tuple.Create(mc.Arguments[1], true));
                            break;
                        }
                        default:
                            supported = false;
                            break;
                    }
                    if (!supported)
                        break;

                    current = mc.Arguments[0];
                }
                else
                    break;
            }

            return current;
        }

        static Expression FinalizeFunction(Expression functionBody)
        {
            var toValueMethodInfo = FindMethodInfo(functionBody.Type, "ToValue", 0);
            functionBody = Expression.Call(functionBody, toValueMethodInfo);
            return functionBody;
        }

        static Expression GenerateOrderBy(Expression entity, Expression functionBody, List<Tuple<Expression, bool>> orderBy)
        {
            var isFirst = true;

            for (int i = orderBy.Count - 1; i >= 0; i--)
            {
                var order = orderBy[i];
                string methodName;
                if (order.Item2)
                    methodName = isFirst ? "OrderByDesc" : "ThenByDesc";
                else
                    methodName = isFirst ? "OrderBy" : "ThenBy";
                isFirst = false;

                var currentType = functionBody.Type;
                var methodInfo = FindMethodInfo(currentType, methodName, 1).GetGenericMethodDefinition();

                var arg = ((LambdaExpression)Unwrap(order.Item1)!).GetBody(entity);

                functionBody = Expression.Call(functionBody, methodInfo.MakeGenericMethod(arg.Type), arg);
            }

            return functionBody;
        }

        static Expression GeneratePartitionBy(Expression functionBody, Expression[] partitionBy)
        {
            if (partitionBy.Length == 0)
                return functionBody;

            var method = FindMethodInfo(functionBody.Type, "PartitionBy", 1);

            var partitionsExpr = Expression.NewArrayInit(typeof(object), partitionBy);

            var call = Expression.Call(functionBody, method, partitionsExpr);

            return call;
        }

        static Expression MakePropPath(Expression objExpression, string path)
        {
            return path.Split('.').Aggregate(objExpression, Expression.PropertyOrField);
        }

        private class Envelope<T>
        {
            public int TotalCount { get; set; }
            public T Data { get; set; } = default!;
            public int Page { get; set; }
        }

        static IQueryable<Envelope<T>> EnvelopeQuery<T>(IQueryable<T> query, int page, int pageSize, bool includeTotalCount)
        {
            var withCount = includeTotalCount
                ? query.Select(q =>
                    new Envelope<T> {TotalCount = Sql.Ext.Count().Over().ToValue(), Page = page, Data = q})
                : query.Select(q => new Envelope<T> {TotalCount = -1, Page = page, Data = q});

            return withCount.Skip((page - 1) * pageSize).Take(pageSize);
        }

        static PaginationResult<T> ProcessPaginationResult<T>(IQueryable<Envelope<T>> query, int pageSize)
        {
            int totalRecords;
            int page = 0;

            using (var enumerator = query.GetEnumerator())
            {
                List<T> result;
                if (!enumerator.MoveNext())
                {
                    totalRecords = 0;
                    result = new List<T>();
                }
                else
                {
                    totalRecords = enumerator.Current.TotalCount;
                    page = enumerator.Current.Page;
                    result = new List<T>(pageSize);
                    do
                    {
                        result.Add(enumerator.Current.Data);
                    } while (enumerator.MoveNext());
                }

                return new PaginationResult<T>(totalRecords, page, pageSize, result);
            }
        }

        static async Task<PaginationResult<T>> ProcessPaginationResultAsync<T>(IQueryable<Envelope<T>> query, int pageSize, CancellationToken cancellationToken)
        {
            var items = query.AsAsyncEnumerable();
            int totalRecords;
            int page = 0;

            await using (var enumerator = items.GetAsyncEnumerator(cancellationToken))
            {
                List<T> result;
                if (!await enumerator.MoveNextAsync())
                {
                    totalRecords = 0;
                    result = new List<T>();
                }
                else
                {
                    totalRecords = enumerator.Current.TotalCount;
                    page = enumerator.Current.Page;
                    result = new List<T>(pageSize);
                    do
                    {
                        result.Add(enumerator.Current.Data);
                    } while (await enumerator.MoveNextAsync());
                }

                return new PaginationResult<T>(totalRecords, page, pageSize, result);
            }
        }

        class RownNumberHolder<T>
        {
            public T Data = default!;
            public long RowNumber;
            public int TotalCount;
        }

        static Expression<Func<int>> _totalCountTemplate = () => Sql.Ext.Count().Over().ToValue();
        static Expression _totalCountEmpty = Expression.Constant(-1);

        static Expression GetRowNumberQuery<T>(Expression queryWithoutOrder, List<Tuple<Expression, bool>> orderBy, bool includeTotal)
        {
            if (orderBy.Count == 0)
                throw new InvalidOperationException("OrderBy for query is not specified");

            Expression<Func<T, AnalyticFunctions.IOverMayHavePartitionAndOrder<long>>> overExpression =
                t => Sql.Ext.RowNumber().Over();

            Expression<Func<IQueryable<T>, long, int, IQueryable<RownNumberHolder<T>>>> selectExpression =
                (q, rn, tc) => q.Select(x => new RownNumberHolder<T> {Data = x, RowNumber = rn, TotalCount = tc});


            Expression totalCountExpr = includeTotal ? _totalCountTemplate.Body : _totalCountEmpty;

            var entityParam = ((LambdaExpression)((MethodCallExpression)selectExpression.Body).Arguments[1].Unwrap())
                .Parameters[0];

            var windowFunctionBody = overExpression.Body;
            windowFunctionBody = GenerateOrderBy(entityParam, windowFunctionBody, orderBy);
            windowFunctionBody = FinalizeFunction(windowFunctionBody);

            var queryExpr = selectExpression.GetBody(queryWithoutOrder, windowFunctionBody, totalCountExpr);

            return queryExpr;
        }

        static IQueryable<Envelope<T>> GetPageByConditionInternal<T>(IQueryable<T> query, int pageSize, Expression<Func<T, bool>> predicate, bool includeTotal)
        {
            Expression<Func<IQueryable<RownNumberHolder<T>>, IQueryable<RownNumberHolder<T>>>> cteCall = q => q.AsCte("pagination_cte");

            var queryExpr = query.Expression;

            var orderBy = new List<Tuple<Expression, bool>>();
            var withoutOrder = ExtractOrderByPart(queryExpr, orderBy);

            var rnQueryExpr = GetRowNumberQuery<T>(withoutOrder, orderBy, includeTotal);
            rnQueryExpr = cteCall.GetBody(rnQueryExpr);

            Expression<Func<IQueryable<RownNumberHolder<T>>, Expression<Func<RownNumberHolder<T>, bool>>, int,
                IQueryable<Envelope<T>>>> dataTemplate =
                includeTotal
                    ? (q, f, ps) =>
                        q
                            .Where(f).Take(1).Select(x => (int)(x.RowNumber - 1) / ps + 1)
                            .SelectMany(page => q.Where(x => x.RowNumber.Between((page - 1) * ps + 1, page * ps))
                                .OrderBy(x => x.RowNumber)
                                .Select(x =>
                                    new Envelope<T>
                                    {
                                        Data = x.Data, Page = page, TotalCount = (int)x.TotalCount
                                    }))
                    : (q, f, ps) =>
                        q
                            .Where(f).Take(1).Select(x => (int)(x.RowNumber - 1) / ps + 1)
                            .SelectMany(page => q.Where(x => x.RowNumber.Between((page - 1) * ps + 1, page * ps))
                                .OrderBy(x => x.RowNumber)
                                .Select(x =>
                                    new Envelope<T> {Data = x.Data, Page = page, TotalCount = -1}));



            var param = Expression.Parameter(typeof(RownNumberHolder<T>), "h");
            var newPredicate = Expression.Lambda(predicate.GetBody(Expression.PropertyOrField(param, "Data")), param);

            var resultExpr = dataTemplate.GetBody(rnQueryExpr, newPredicate, Expression.Constant(pageSize));
            return query.Provider.CreateQuery<Envelope<T>>(resultExpr);
        }

        static IQueryable<int> GetPageNumberByConditionInternal<T>(IQueryable<T> query, int pageSize, Expression<Func<T, bool>> predicate, bool includeTotal)
        {
            var queryExpr = query.Expression;

            var orderBy = new List<Tuple<Expression, bool>>();
            var withoutOrder = ExtractOrderByPart(queryExpr, orderBy);

            var rnQueryExpr = GetRowNumberQuery<T>(withoutOrder, orderBy, includeTotal);

            Expression<Func<IQueryable<RownNumberHolder<T>>, Expression<Func<RownNumberHolder<T>, bool>>, int,
                IQueryable<int>>> dataTemplate =
                (q, f, ps) =>
                    q.AsSubQuery().Where(f).Select(x => (int)((x.RowNumber - 1) / ps + 1));

            var param = Expression.Parameter(typeof(RownNumberHolder<T>), "h");
            var newPredicate = Expression.Lambda(predicate.GetBody(Expression.PropertyOrField(param, "Data")), param);

            var resultExpr = dataTemplate.GetBody(rnQueryExpr, newPredicate, Expression.Constant(pageSize));
            return query.Provider.CreateQuery<int>(resultExpr);
        }

        #endregion
    }
}
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/65781684

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档