简体   繁体   English

动态Linq +实体框架:动态选择的日期时间修改

[英]Dynamic Linq + Entity Framework: datetime modifications for dynamic select

I am trying to find a way to move UTC time to Local before doing a sql grouping. 我正在尝试找到一种方法来在进行sql分组之前将UTC时间移动到Local。 I am using the System.Linq.Dynamic (managed here https://github.com/kahanu/System.Linq.Dynamic ). 我正在使用System.Linq.Dynamic(在这里https://github.com/kahanu/System.Linq.Dynamic进行管理)。 It works great for doing dynamic selects without having at compile time the required fields. 它非常适合进行动态选择,而无需在编译时填写必填字段。 In our case, we store all datetimes in UTC. 就我们而言,我们将所有日期时间存储在UTC中。 In this dynamic select, its possible that someone would want to do a groupby on the Hour, year, month, etc. We have to move the data to a local time in this case, to prevent confusion. 在这种动态选择中,有人可能希望对小时,年,月等进行分组。在这种情况下,我们必须将数据移至本地时间,以免造成混淆。

Example: 例:

var select = queryable.Select(string.Format("new ({0}, {1})", datetimeColumn, someOtherColumn));

Normally in our tsql or even in entity framework using lambda expressions, you can add in your desired offset. 通常,在我们的tsql中,甚至在使用lambda表达式的实体框架中,您都可以添加所需的偏移量。 But in the dynamic linq option, it appears that you can't perform any date operations such as DateTime.AddHours(x) or DateTime.Subtract(x) like you could with Linq2Sql. 但是在动态linq选项中,似乎无法像Linq2Sql一样执行任何日期操作,例如DateTime.AddHours(x)或DateTime.Subtract(x)。 Entity Framework 6 wants you to use DbFunctions.AddHours(x) , etc. However the dynamic linq code, without modification, will not accept the DbFunctions without error. 实体框架6希望您使用DbFunctions.AddHours(x)等。但是,动态linq代码,未经修改,将不会错误地接受DbFunction。

Example: 例:

var select = queryable.Select(string.Format("new (System.Data.Entity.DbFunctions.AddHours({0},7) as {0}, {1})", datetimeColumn, someOtherColumn));

Returns an error: No property or field 'System' exists in type XXX 返回错误:XXX类型中不存在属性或字段“系统”
(removing the namespace doesn't help). (删除名称空间无济于事)。

Using the desired code: 使用所需的代码:

var select = queryable.Select(string.Format("new ({0}.AddHours(7), {1})", datetimeColumn, someOtherColumn));

Results with error: LINQ to Entities does not recognize the method 'System.DateTime AddHours(Double)' method, and this method cannot be translated into a store expression. 错误结果:LINQ to Entities无法识别方法'System.DateTime AddHours(Double)',并且该方法无法转换为商店表达式。

I want to have SQL perform the datetime math prior to the groupby. 我想让SQL在groupby之前执行日期时间数学。 Once the groupby happens, there is no concept of UTC any longer as the user will see the localized result set. 一旦发生groupby,就不再有UTC的概念,因为用户将看到本地化的结果集。

I'm afraid that Ill just to update my github fork with some extensions to support passing in the entity framework extensions, but before I did, wanted to see if anyone else has a solution or idea. 恐怕我只会用一些扩展来更新我的github分支,以支持传递实体框架扩展,但是在我这样做之前,我想看看是否还有其他人有解决方案或想法。

Note: I am not using DateTimeOffset due to possibilities of changing SQL data store technologies. 注意:由于可能更改SQL数据存储技术,因此我不使用DateTimeOffset。

You can post process the query expression with custom ExpressionVisitor and replace the unsupported methods with their DbFunctions equivalents. 您可以使用自定义ExpressionVisitor对查询表达式进行后期处理,并用其DbFunctions等效项替换不受支持的方法。

Here is a starting point just to get the idea: 这只是一个开始的想法:

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);
        }
    }
}

and sample usage: 和样本用法:

var select = queryable
    .Select(string.Format("new ({0}.AddHours(7), {1})", datetimeColumn, someOtherColumn))
    .BindDbFunctions();

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM