我使用此 Pull Request 中的示例作为嵌套属性链的参考。不幸的是像
Sql.Property<object>(x,"Someclass.SomeProperty")
不起作用。
假设我有一个模型 SortDescriptor
(定义如下),它定义了我们如何动态地对代码中的项目进行排序。有没有办法动态创建表达式树来生成嵌套的 Sql.Property
调用,就像下面显示的 ToPropertyExpression
方法一样?
如果给定以下过滤器,则应从 SortBy
函数输出以下 SQL:
ORDER BY Category.EntityId,StackRank,Id
(Category
是 CategoryProperty
上的导航属性)
var filter1 = new PagedFilter
{
Take = 25,Sort = new List<SortDescriptor>
{
new SortDescriptor { Selector = "Category.EntityId" },new SortDescriptor { Selector = "StackRank" },new SortDescriptor { Selector = "Id" },}
};
以下是我使用的函数和对象:
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.Property[T](Object entity,String propertyName)
在 LinqToDB.Linq.QueryRunner.SetParameters(Query query,Expression expression,IDataContext parametersContext,Object[] parameters,Int32 queryNumber,SqlParameterValues parameterValues)
在 LinqToDB.Linq.QueryRunnerBase.SetCommand(Boolean clearqueryHints)
在 LinqToDB.Data.DataConnection.QueryRunner.n__0(Boolean clearqueryHints)
在 LinqToDB.Data.DataConnection.QueryRunner.ExecuteReaderAsync(CancellationToken cancellingToken)
在 LinqToDB.Linq.QueryRunner.AsyncEnumeratorImpl1.MoveNextAsync() at microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ToListAsync[tsource](IQueryable
1 源,CancellationToken 取消令牌)
在 microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ToListAsync[tsource](IQueryable1 source,CancellationToken cancellationToken) at LinqToDB.AsyncExtensions.ToListAsync[tsource](IQueryable
1 源,CancellationToken 令牌)
在 Experlogix.Api.DesignStudio.Dataaccess.CategoryPropertyRepository.GetPageIndexById(CategoryPropertyFilter filter,Guid id) 在 D:\Dev\repos\Experlogix.Api.DesignStudio\src\Experlogix.Api.DesignStudio.Dataaccess\CategoryPropertyRepository.16
在 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,Valuetask
1 actionResultvalueTask)
在 microsoft.AspNetCore.Mvc.Infrastructure.ControlleractionInvoker.g__Awaited|10_0(ControlleractionInvoker invoker,Task lastTask,State next,Scope scope,Object state,Boolean isCompleted)
在 microsoft.AspNetCore.Mvc.Infrastructure.ControlleractionInvoker.Rethrow(actionExecutedContextSealed context)
在 microsoft.AspNetCore.Mvc.Infrastructure.ControlleractionInvoker.Next(State& next,Scope& scope,Object& state,Boolean& isCompleted)
在 microsoft.AspNetCore.Mvc.Infrastructure.ControlleractionInvoker.g__Awaited|13_0(ControlleractionInvoker invoker,Boolean isCompleted)
在 microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Awaited|19_0(ResourceInvoker invoker,Boolean isCompleted)
在 microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Awaited|17_0(ResourceInvoker invoker,Task task,IDisposable scope)
在 microsoft.AspNetCore.Routing.EndpointMiddleware.g__AwaitRequestTask|6_0(Endpoint endpoint,Task requestTask,ILogger logger)
在 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:第65行
在 microsoft.AspNetCore.Authorization.Authorizationmiddleware.Invoke(HttpContext context)
在 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 context)
任何有关如何实现与此类似的事情的指导将不胜感激!
环境详情
- linq2db 版本:linq2db.EntityFrameworkCore 5.1.0
- 数据库服务器:SQL Server
- 操作系统:Windows 10
- .NET 框架:.Net 5.0