2011-11-14 17 views
7

Sto provando a prendere un valore DateTime e, se non è null, restituisce la stringa Short Time. La mia domanda è simile al seguente: (timein NON è NULLABLE, mentre TimeOut è NULLABLE)Perché non posso lanciare il valore DateTime nullable come stringa in una query 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(); 

, ma questo viene a mancare quando provo a DataBind con l'errore:

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.

Inoltre ottengo un errore simile se cerco di usare:

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

tuttavia, se cambio la proprietà TimeOut a:

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

funziona correttamente, ma non formatta l'orario come lo desidero (shortTimeString).

cosa succede?

risposta

9

Come altri hanno già detto, il problema è con il tentativo di convertire ToShortDateString ecc. In SQL. Fortunatamente, questo è facile da risolvere: preleva i dati con SQL con il formato in.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() 
            : "-------" 
      }; 

La chiamata a AsEnumerable() qui in pratica significa, "smettere di cercare di elaborare la query utilizzando SQL, fare il resto in LINQ to Objects".

+0

Suppongo che "AsEnumerable()" causi lo stesso effetto come "ToList()" nella risposta di Justin. L'uno o l'altro metodo è "migliore" da usare? –

+2

@ matthew_360: Non proprio lo stesso effetto, no - 'ToList()' deve creare un elenco di tutti gli elementi, piuttosto che eseguirne il flusso. Va bene se userai 'times' in più posti senza qualcosa di buffering * che * risultasse, ma altrimenti non vedo alcun motivo per costruire il buffer - Personalmente userò' AsEnumerable', che serve * esclusivamente * allo scopo vuoi: conversione da 'IQueryable ' a 'IEnumerable '. –

5

ToShortTimeString() non ha traduzione in SQL. Per questo motivo, la conversione dell'istruzione in una singola istruzione SQL non riesce e viene generata l'eccezione.

Se si interrompe la dichiarazione in due chiamate (una per recuperare i dati e un altro per creare la proiezione), le cose funzionano bene:

// 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

Risposta formidabile! grazie mille - questo ha perfettamente senso! –

+0

Non vedo alcun motivo per tirare giù più di TimeIn e TimeOut, e personalmente non sceglierei la chiamata "ToList" - perché non preservare la pigrizia di LINQ? Tutto ciò che vogliamo veramente è forzare il resto della query ad essere eseguita in LINQ to Objects. –

+0

@Jon Skeet - Senza la chiamata a ToList, la pigrizia non ricorrerebbe a LINQ to SQL (poiché la query non sarebbe stata valutata in quel momento)? Il resto era solo copia/incolla da sopra ... non ho ancora avuto il tempo per quella modifica. –

1

Vostri criteri viene trasformato da LINQ a uno SQL che è licenziato contro il tuo database, e non c'è ovviamente alcun modo per tradurre t.TimeOut.Value.ToShortTimeString() in SQL.

soluzioni possibili sono:

  1. Prima recuperare i dati dal database (chiamando .ToList() o .ToArray() sulla vostra query LINQ), che converte il vostro IQueryable<> in IEnumerable<> e quindi applicare la vostra trasformazione per ogni riga estratta.
  2. Utilizzare una vista che accetta la tabella originale ed esegue la conversione utilizzando la funzione CONVERT() su SQL Server e utilizzarla come origine per la classe Linq-to-SQL. Questo sarebbe performante, ma richiede alcune modifiche sul lato server.
2

Hai provato:

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

Questo sarà normalmente dare al formato breve della DateTime. Che funzioni o no dipenderà dal fatto che possa essere tradotto in SQL o meno.

Se non funziona, è necessario suddividere la query in due parti. Il primo ottiene i dati, il secondo lo formattano. Dovrai convertire la prima query in un elenco (.ToList()) per forzare l'SQL a essere valutato.

2

Semplicemente, non è supportato da questo provider linq specifico.

La query di linq viene convertita in un albero di espressioni. Spetta al provider SQL Linq convertire questo albero di espressioni in SQL. Comprensibilmente, non ha la capacità di tradurre ogni singola funzione .NET.

La soluzione è eseguire esplicitamente SQL chiamando ToArray o ToList e quindi consentire a LinqToObjects di gestire il resto.

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

sembra che abbiamo un terzo modo possibile per raggiungere questo obiettivo. ToList(), AsEnumerable() e now ToArray(). Sono causa che tutti lavorano, mi chiedo quale sia la più veloce? –

+0

@ mathew_360 - Ho imparato qualcosa da questo: non ero a conoscenza di AsEnumerable. Sono d'accordo con Jon Skeet - AsEnumerable sarebbe più approriato perché ToArray forzerebbe l'intera query a terminare prima di fare le conversioni temporali. –

0

Ho avuto lo stesso problema in un progetto in vb.net. La soluzione che ho trovato è basato sull'uso di:

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

In questo caso, se il campo selezionato (table.field) ha un valore questo viene convertito in una stringa di data altrimenti se il campo non ha un valore il campo di output è pieno di stringa "NULL"

Problemi correlati