2016-10-06 9 views
2

SQLのグループ化を行う前に、UTCの時刻をLocalに移動する方法を見つけようとしています。私はSystem.Linq.Dynamic(ここではhttps://github.com/kahanu/System.Linq.Dynamicで管理)を使用しています。コンパイル時に必要なフィールドを持たずに動的選択を行うのに最適です。我々のケースでは、すべてのdatetimesをUTCで保存します。この動的選択では、誰かが時間、年、月などでグループバイをしたいと思う可能性があります。混乱を避けるために、この場合は現地時間にデータを移動する必要があります。Dynamic Linq + Entity Framework:動的選択のdatetimeの変更

例:ラムダ式を使用して私たちのTSQLで、あるいはエンティティフレームワークでは通常

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

、ご希望のオフセットに追加することができます。しかし、動的linqオプションでは、Linq2Sqlのように、DateTime.AddHours(x)やDateTime.Subtract(x)などの日付操作は実行できないようです。 Entity Framework 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
に存在しない(名前空間を削除しても解決しません)。所望のコードを使用し

:エラーと

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

結果:メソッド「のSystem.DateTime AddHours(ダブル)」方法、及びこの方法を認識しないLINQエンティティには、格納式に変換することができません。

私は、SQLにgroupbyの前にdatetime演算を実行させたいと思っています。 groupbyが実行されると、ユーザーにはローカライズされた結果セットが表示されるため、UTCの概念はありません。

私のgithub forkをエンティティフレームワーク拡張を渡すための拡張機能をいくつか追加して更新してもらえましたが、私がする前に誰かが解決策やアイデアを持っているかどうかを見たいと思っていました。

注:SQLデータストアテクノロジを変更する可能性があるため、DateTimeOffsetは使用しません。

答えて

5

クエリ式をカスタムExpressionVisitorでポストし、サポートされていないメソッドを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(); 
+3

ファンタスティックソリューションここ

だけでアイデアを得るための出発点です。ありがとう!それは表現をよりよく理解し、新しいことを学ぶのに役立ちます。優れたソリューションとすぐにすぐに働いた。私はほんの少しの変更を加えたが、私にDbFunctionsの使用を許可した。これにより、他の潜在的な用途も解消されます。この回答が将来他の誰かに役立つことを願っています! – TravisWhidden

+0

イワン、あなたはこの質問を見ることができますか?ファッションと同様に、実装に固執しています。 http://stackoverflow.com/questions/40271588/entity-framework-dayofweek – TravisWhidden

+1

確かに、そこに行く:) –

関連する問題