动态Linq +实体框架:日期时间修改动态选择
本文关键字:动态 时间 修改 选择 日期 框架 Linq 实体 | 更新日期: 2023-09-27 18:11:50
我正试图找到一种方法来移动UTC时间到本地做sql分组之前。我正在使用System.Linq.Dynamic(在这里管理https://github.com/kahanu/System.Linq.Dynamic)。它可以很好地执行动态选择,而无需在编译时使用必需的字段。在我们的例子中,我们用UTC存储所有日期时间。在这个动态选择中,可能有人想要按小时、年、月等进行分组。在这种情况下,我们必须将数据移动到本地时间,以防止混淆。
的例子:
var select = queryable.Select(string.Format("new ({0}, {1})", datetimeColumn, someOtherColumn));
通常在使用lambda表达式的tsql或实体框架中,您可以添加所需的偏移量。但是在动态linq选项中,您似乎不能像使用Linq2Sql那样执行任何日期操作,例如DateTime.AddHours(x)或DateTime.Subtract(x)。实体框架6希望你使用DbFunctions.AddHours(x)等。然而,动态linq代码,如果不修改,将不会接受没有错误的DbFunctions。
的例子:
var select = queryable.Select(string.Format("new (System.Data.Entity.DbFunctions.AddHours({0},7) as {0}, {1})", datetimeColumn, someOtherColumn));
返回错误:XXX类型中不存在属性或字段"System"
(删除命名空间没有帮助)。
使用所需代码:
var select = queryable.Select(string.Format("new ({0}.AddHours(7), {1})", datetimeColumn, someOtherColumn));
结果错误:LINQ to Entities不识别方法System。DateTime AddHours(Double)'方法,该方法不能转换为存储表达式。
我想让SQL在groupby之前执行datetime数学。一旦groupby发生,UTC的概念就不再存在了,因为用户将看到本地化的结果集。
我担心我只是更新我的github分支与一些扩展,以支持在实体框架扩展传递,但在我这样做之前,想看看是否有人有一个解决方案或想法。
注意:我不使用DateTimeOffset由于改变SQL数据存储技术的可能性
您可以使用自定义ExpressionVisitor
对查询表达式进行post处理,并将不支持的方法替换为相应的DbFunctions
。
这里是一个起点,只是为了了解这个想法:
public static class QueryableExtensions
{
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 VisitMethodCall(MethodCallExpression node)
{
if (node.Object != null && node.Object.Type == typeof(DateTime))
{
if (node.Method.Name == "AddHours")
{
var timeValue = Visit(node.Object);
var addValue = Visit(node.Arguments.Single());
if (timeValue.Type != typeof(DateTime?)) timeValue = Expression.Convert(timeValue, typeof(DateTime?));
if (addValue.Type != typeof(int?)) addValue = Expression.Convert(addValue, typeof(int?));
var methodCall = Expression.Call(
typeof(DbFunctions), "AddHours", Type.EmptyTypes,
timeValue, addValue);
return Expression.Convert(methodCall, typeof(DateTime));
}
}
return base.VisitMethodCall(node);
}
}
}
和示例用法:
var select = queryable
.Select(string.Format("new ({0}.AddHours(7), {1})", datetimeColumn, someOtherColumn))
.BindDbFunctions();