2011-11-14 18 views
7

DateTime値を取得しようとしていますが、nullでない場合はShort Time Stringを返します。なぜ、null可能なDateTimeをLinQクエリの文字列としてキャストできないのですか?

var times = from t in db.TimePostings 
       where t.MemberID == member.MemberID 
       select new 
       { 
        Date = t.TimeIn.ToShortDateString(), 
        TimeIn = t.TimeIn.ToShortTimeString(), 
        TimeOut = t.TimeOut.HasValue ? t.TimeOut.Value.ToShortTimeString() : "-------" 
       }; 
gvTimePostings.DataSource = times; 
gvTimePostings.DataBind(); 

(タイムアウトがNULLABLEであるのに対し、TimeInがNULL可能でない)が、私はエラーでデータバインドしようとすると、これが失敗 :

Could not translate expression 'Table(TimePosting).Where(t => (t.MemberID == Invoke(value(System.Func 1[System.String])))).Select(t => new <>f__AnonymousType8 4(Date = t.TimeIn.ToShortDateString(), TimeIn = t.TimeIn.ToShortTimeString(), TimeOut = IIF(t.TimeOut.HasValue, (t.TimeOut ?? Invoke(value(System.Func`1[System.DateTime]))).ToShortTimeString(), "-------"), Hours = ""))' into SQL and could not treat it as a local expression.

は私も同様のエラーを取得する私のクエリは次のようになりますしかし

TimeOut = t.TimeOut.HasValue ? Convert.ToDateTime(t.TimeOut).ToShortTimeString() : "-------" 

、私はタイムアウトプロパティを変更した場合::私が使用しようとすると

TimeOut = t.TimeOut.HasValue ? t.TimeOut.ToString() : "-------", 

それはうまく動作しますが、私が望むように(shortTimeString)時間をフォーマットしません。

これは何ですか?

答えて

9

他にも述べたように、問題はToShortDateStringなどをSQLに変換しようとすることです。幸いにも、これは修正するのが簡単ですのデータをSQLで取得し、次にフォーマットです。NET:

var timesFromDb = from t in db.TimePostings 
        where t.MemberID == member.MemberID 
        select new { t.TimeIn, t.TimeOut }; 

var times = from t in timesFromDb.AsEnumerable() 
      select new 
      { 
       Date = t.TimeIn.ToShortDateString(), 
       TimeIn = t.TimeIn.ToShortTimeString(), 
       TimeOut = t.TimeOut.HasValue 
            ? t.TimeOut.Value.ToShortTimeString() 
            : "-------" 
      }; 

ここAsEnumerable()の呼び出しは基本的に、「;オブジェクトへのLINQで残りを行うSQLを使用してクエリを処理しようと停止」を意味します。

+0

"AsEnumerable()"が同じ効果をもたらすと仮定していますジャスティンの応答の "ToList()"と同じです。いずれの方法でも「より良い」使用が可能ですか? –

+2

@ matthew_360:全く同じ効果はありません。ToList()はストリーミングではなく、すべての項目のリストを作成する必要があります。 * times *をバッファリングすることなく複数の場所で使うつもりなら、それは良いことです。そうでなければ、バッファを構築する理由はありません。私は個人的にはAsEnumerableを使います。 'IQueryable 'から 'IEnumerable 'に変換します。 –

5

ToShortTimeString()にはSQLでの変換がありません。そのため、文を単一のSQL文に変換すると失敗し、例外がスローされます。

次の2つのコール(突起部を作成するために、データと別のを取得するために、1)に声明を破る場合は、物事がうまく動作しますが:

// must call ToList to force execution of the query before projecting 
var results = from t in db.TimePostings 
       where t.MemberID == member.MemberID 
       select new { t.TimeIn, t.TimeOut }; 

var times = from t in results.AsEnumerable() 
      select new 
      { 
       Date = t.TimeIn.ToShortDateString(), 
       TimeIn = t.TimeIn.ToShortTimeString(), 
       TimeOut = t.TimeOut.HasValue ? 
        t.TimeOut.Value.ToShortTimeString() : 
        "-------" 
      }; 
+0

すばらしい答え!本当にありがとうございます - それは完全に意味があります! –

+0

私はTimeInとTimeOutより多くをプルダウンする理由はなく、個人的には 'ToList'コールを選択しませんでした。なぜLINQの怠惰を保存しないのですか?残りのクエリをLINQ to Objectsで実行するようにしてください。 –

+0

@Jon Skeet - ToListへの呼び出しがなければ、怠惰はLINQ to SQLに戻らないでしょう(クエリはその時点で評価されていないでしょうから)。残りはまだ上からカット/ペーストされています...まだその編集のための時間がありませんでした。 –

1

あなたのクエリがあるSQLにLINQによって変換されますあなたのデータベースに対して解雇され、明らかにt.TimeOut.Value.ToShortTimeString()をSQLに変換する方法はありません。

可能な解決策は以下のとおりです。

  1. まず、データベースからIEnumerable<>にあなたのIQueryable<>を変換し、フェッチされた行ごとに、あなたの変換を適用し、(あなたのLINQクエリに.ToList()または.ToArray()を呼び出すことで)あなたのデータをフェッチします。
  2. 元のテーブルを使用し、SQL ServerでCONVERT()関数を使用して変換を実行し、Linq-to-SQLクラスのソースとして使用するビューを使用します。これはパフォーマンスにはなりますが、サーバー側の変更が必要です。
2

あなたは試してみました:

TimeOut = t.TimeOut.HasValue ? t.TimeOut.ToString("d") : "-------", 

をこれは通常DateTimeの短い形式を提供します。それが動作するかどうかは、SQLに変換できるかどうかによって異なります。

それが機能しない場合は、クエリを2つの部分に分割する必要があります。最初はデータを取得し、2番目はデータを取得します。 SQLを強制的に評価するには、最初のクエリをリスト(.ToList())に変換する必要があります。

2

単に、この特定のlinqプロバイダではサポートされていません。

linqクエリは式ツリーに変換されます。この式ツリーをSQLに変換するのは、SQL Linqプロバイダの責任です。わかりやすいことに、すべての.NET関数を変換する機能はありません。

解決策は、ToArrayまたはToListを呼び出して明示的にSQLを実行し、残りの部分をLinqToObjectsで処理できるようにすることです。

var times = from t in db.TimePostings 
      where t.MemberID == member.MemberID 
      select new { 
         TimeIn = t.TimeIn, 
         TimeOut = t.TimeOut 
         }; 

    var timesFormated = times.ToArray() // Runs the query - any further processing will be run in memory by the local .NET code 
         .Select(t => new { 
              Date = t.TimeIn.ToShortDateString(), 
              TimeIn = t.TimeIn.ToShortTimeString(), 
              TimeOut = t.TimeOut.HasValue ? t.TimeOut.Value.ToShortTimeString() : "-------", 
              Hours = ""            
             } 
           ); 
+0

は、これを実現する3番目の方法があるようです。 ToList()、AsEnumerable()、ToArray()を呼び出します。彼らはすべてうまくいっていると訴えています。 –

+0

@ mathew_360 - 私はこれから何かを学んだ - 私はAsEnumerableを知らなかった。私はJon Skeetに同意します.ToArrayは時間の変換を行う前にクエリ全体を終了させるので、AsEnumerableはより適切です。 –

0

私はvb.netのプロジェクトで同じ問題がありました。 Iが見つけた溶液をの使用に基づいている:選択したフィールド(table.field)の値を有する場合、フィールドこの場合

if(table.field.hasvalue, table.field.value.ToShortDateString, string.format("NULL"))

、これは、そうでない場合、日付文字列に変換され値がありません出力フィールドが文字列 "NULL"で埋められています

関連する問題