仅在 DateTime 对象上动态 LINQ OrderBy 日期 Entity Framework
Dynamic LINQ OrderBy Date only on DateTime Object Entity Framework
我目前正在使用 Dynamic LINQ (original codeplex link) 库来实现动态搜索功能。我有一个要求,我需要 OrderBy
一个 DateTime?
字段只考虑日期而不是时间。
我正在使用 EntityFramework
.
查询 SQL Azure 数据库
这是示例实体。
public class SampleEntity
{
public DateTime? DateCreated { get; set; }
public bool Flag { get; set; }
}
这是查询数据库的代码。
var orderByString = "DateCreated.Value.Date asc, Flag"; //This is a dynamic string
var query = _context.Set<SampleEntity>().OrderBy(orderByString);
这个表达式 ("DateCreated.Value.Date") 被 System.Linq.Dynamic
解析没有问题,但它抛出以下错误(这是可以理解的),因为它不受 LINQ to Entities 支持。
请记住,这必须在 IQueryable (I can't use the answer from this post) 上工作,因为我需要在服务器端完成排序。
The specified type member 'Date' is not supported in LINQ to Entities. Only initializers, entity members, and entity navigation properties are supported.
解决方案是使用 DbFunctions.TruncateTime()
表达 in this answer 等。但是,这不适用于 System.Linq.Dynamic
关于如何解决这个问题的任何想法。可能的解决方案是将另一列添加到数据库中,仅包含 DateCreated
的日期部分并查询该列。但是,我宁愿不这样做,而是正在寻找解决此问题的其他方法。
另一种方法是动态生成 lambda 表达式和 return DbFunctions.TruncateTime,然后对数据库执行它。
任何输入表示赞赏。
谢谢,
您可以使用我对 , i.e. post process the query expression with custom ExpressionVisitor
and replace the unsupported methods with their DbFunctions
equivalents. In this particular case, replace the DateTime.Date
property with DbFunctions.TruncateTime
方法调用的回答中的方法:
public static class QueryableExtensions
{
public static IQueryable<T> BindDbFunctions<T>(this IQueryable<T> source)
{
var expression = new DbFunctionsBinder().Visit(source.Expression);
if (expression == source.Expression) return source;
return source.Provider.CreateQuery<T>(expression);
}
public static IQueryable BindDbFunctions(this IQueryable source)
{
var expression = new DbFunctionsBinder().Visit(source.Expression);
if (expression == source.Expression) return source;
return source.Provider.CreateQuery(expression);
}
class DbFunctionsBinder : ExpressionVisitor
{
protected override Expression VisitMember(MemberExpression node)
{
if (node.Expression != null && node.Expression.Type == typeof(DateTime) && node.Member.Name == "Date")
{
var dateValue = Expression.Convert(Visit(node.Expression), typeof(DateTime?));
var methodCall = Expression.Call(typeof(DbFunctions), "TruncateTime", Type.EmptyTypes, dateValue);
return Expression.Convert(methodCall, typeof(DateTime));
}
return base.VisitMember(node);
}
}
}
示例用法:
var orderByString = "DateCreated.Value.Date asc, Flag"; //This is a dynamic string
var query = _context.Set<SampleEntity>().OrderBy(orderByString).BindDbFunctions();
我目前正在使用 Dynamic LINQ (original codeplex link) 库来实现动态搜索功能。我有一个要求,我需要 OrderBy
一个 DateTime?
字段只考虑日期而不是时间。
我正在使用 EntityFramework
.
这是示例实体。
public class SampleEntity
{
public DateTime? DateCreated { get; set; }
public bool Flag { get; set; }
}
这是查询数据库的代码。
var orderByString = "DateCreated.Value.Date asc, Flag"; //This is a dynamic string
var query = _context.Set<SampleEntity>().OrderBy(orderByString);
这个表达式 ("DateCreated.Value.Date") 被 System.Linq.Dynamic
解析没有问题,但它抛出以下错误(这是可以理解的),因为它不受 LINQ to Entities 支持。
请记住,这必须在 IQueryable (I can't use the answer from this post) 上工作,因为我需要在服务器端完成排序。
The specified type member 'Date' is not supported in LINQ to Entities. Only initializers, entity members, and entity navigation properties are supported.
解决方案是使用 DbFunctions.TruncateTime()
表达 in this answer 等。但是,这不适用于 System.Linq.Dynamic
关于如何解决这个问题的任何想法。可能的解决方案是将另一列添加到数据库中,仅包含 DateCreated
的日期部分并查询该列。但是,我宁愿不这样做,而是正在寻找解决此问题的其他方法。
另一种方法是动态生成 lambda 表达式和 return DbFunctions.TruncateTime,然后对数据库执行它。
任何输入表示赞赏。
谢谢,
您可以使用我对 ExpressionVisitor
and replace the unsupported methods with their DbFunctions
equivalents. In this particular case, replace the DateTime.Date
property with DbFunctions.TruncateTime
方法调用的回答中的方法:
public static class QueryableExtensions
{
public static IQueryable<T> BindDbFunctions<T>(this IQueryable<T> source)
{
var expression = new DbFunctionsBinder().Visit(source.Expression);
if (expression == source.Expression) return source;
return source.Provider.CreateQuery<T>(expression);
}
public static IQueryable BindDbFunctions(this IQueryable source)
{
var expression = new DbFunctionsBinder().Visit(source.Expression);
if (expression == source.Expression) return source;
return source.Provider.CreateQuery(expression);
}
class DbFunctionsBinder : ExpressionVisitor
{
protected override Expression VisitMember(MemberExpression node)
{
if (node.Expression != null && node.Expression.Type == typeof(DateTime) && node.Member.Name == "Date")
{
var dateValue = Expression.Convert(Visit(node.Expression), typeof(DateTime?));
var methodCall = Expression.Call(typeof(DbFunctions), "TruncateTime", Type.EmptyTypes, dateValue);
return Expression.Convert(methodCall, typeof(DateTime));
}
return base.VisitMember(node);
}
}
}
示例用法:
var orderByString = "DateCreated.Value.Date asc, Flag"; //This is a dynamic string
var query = _context.Set<SampleEntity>().OrderBy(orderByString).BindDbFunctions();