2011-11-14 12 views
7

Próbuję pobrać wartość DateTime, a jeśli nie jest pusta, zwraca krótki ciąg czasu. Moje zapytanie wygląda następująco: (czas nauki nie jest dopuszczalna wartość pusta, natomiast timeout NULLABLE)Dlaczego nie można rzucić null z datą jako ciąg w kwerendy 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(); 

ale to się nie powiedzie, gdy próbuję databind z błędem:

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.

ja również uzyskać podobny błąd jeśli próbuję użyć:

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

jednak, jeśli zmienić właściwość timeout:

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

działa poprawnie, ale nie formatuje czasu tak, jak tego chcę (shortTimeString).

co się z tym dzieje?

Odpowiedz

9

Jak powiedzieli inni, problemem jest próba konwersji ToShortDateString itd. Do SQL. Na szczęście jest to łatwe do naprawienia: pobrać danych z SQL, a następnie format to w.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() 
            : "-------" 
      }; 

Wezwanie do AsEnumerable() tutaj w zasadzie oznacza "stop próbuje przetworzyć kwerendy przy użyciu SQL; zajmiemy się resztą w LINQ to Objects".

+0

Zakładam, że "AsEnumerable()" powoduje ten sam efekt jako "ToList()" w odpowiedzi Justina. Czy którakolwiek z metod jest "lepsza"? –

+2

@ matthew_360: Nie całkiem ten sam efekt, nie - 'ToList()' musi zbudować listę wszystkich elementów, zamiast przesyłać je strumieniowo. To dobrze, jeśli zamierzasz używać 'times' w wielu miejscach bez buforowania *, które * powodują, ale poza tym nie widzę powodu do budowania bufora - osobiście użyłbym' AsEnumerable', który służy wyłącznie do celu chcesz: konwertowanie z 'IQueryable ' na 'IEnumerable '. –

5

ToShortTimeString() nie ma tłumaczenia w języku SQL. Z tego powodu konwertowanie instrukcji na pojedynczą instrukcję SQL kończy się niepowodzeniem i generowany jest wyjątek.

Jeśli złamiesz oświadczenie do dwóch połączeń (jeden do pobierania danych, a inny do tworzenia projekcji), wszystko będzie działać dobrze:

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

Wspaniała odpowiedź! wielkie dzięki - to ma sens! –

+0

Nie widzę powodu, aby zrywać więcej niż TimeIn i TimeOut, i osobiście nie wybrałbym wywołania 'ToList' - dlaczego nie zachować lenistwa LINQ? Chcemy jedynie zmusić resztę zapytania do wykonania w LINQ do Objects. –

+0

@Jon Skeet - czy bez wezwania do ToList, czy lenistwo nie powróciłoby do LINQ do SQL (ponieważ zapytanie nie byłoby w tym momencie oceniane)? Reszta była po prostu wycięta/wklejona z góry ... jeszcze nie miałem czasu na tę edycję. –

1

Zapytanie zostaje zwerbowany przez LINQ do SQL, który jest wystrzelony z bazy danych i oczywiście nie ma możliwości przetłumaczenia t.TimeOut.Value.ToShortTimeString() na SQL.

Możliwe rozwiązania to:

  1. Najpierw pobrać swoje dane z bazy danych (poprzez wywołanie .ToList() lub .ToArray() na zapytania LINQ), który konwertuje IQueryable<> do IEnumerable<> a następnie zastosować transformację dla każdego wiersza pobranego.
  2. Użyj widoku, który pobiera oryginalną tabelę i wykonuje konwersję przy użyciu funkcji CONVERT() na serwerze SQL Server i używaj jej jako źródła dla klasy Linq-SQL. To byłby performanter, ale wymaga pewnych zmian po stronie serwera.
2

Czy próbowałeś:

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

to zwykle dać krótki format DateTime. To, czy zadziała, czy nie, będzie zależało od tego, czy można je przetłumaczyć na język SQL, czy nie.

Jeśli to nie zadziała, musisz podzielić zapytanie na dwie części. Pierwszy dostaje dane, drugi format. Konieczne będzie przekonwertowanie pierwszego zapytania na listę (.ToList()), aby wymusić ocenę SQL.

2

Po prostu nie jest obsługiwany przez tego konkretnego dostawcę linq.

Twoje zapytanie linq jest konwertowane na drzewo wyrażeń. Do dostawcy SQL Linq należy przekonwertowanie tego drzewa wyrażeń na SQL. Zrozumiałe, że nie ma możliwości przetłumaczenia każdej funkcji .NET.

Twoje rozwiązanie polega na jawnym uruchomieniu SQL przez wywołanie ToArray lub ToList, a następnie zezwolenie LinqToObjects na obsłużenie reszty.

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

wygląda na to, że mamy trzeci możliwy sposób osiągnięcia tego. ToList(), AsEnumerable(), a teraz ToArray(). Jestem pozwać, że oni wszyscy pracują, zastanawiam się, który jest najszybszy? –

+0

@ mathew_360 - Nauczyłem się czegoś od tego - nie byłem świadom AsZumerow. Zgadzam się z Jonem Skeetem - AsEnumerable byłoby bardziej godne uwagi, ponieważ ToArray wymusiłoby zakończenie całego zapytania przed wykonaniem konwersji czasu. –

0

Miałem ten sam problem w projekcie w vb.net. Rozwiązanie znalazłem opiera się na wykorzystaniu:

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

W tym przypadku, jeżeli wybrany obszar (table.field) ma wartość ta zostaje przekształcona w ciąg data inaczej jeśli pole nie ma wartości, pole wynikowe jest wypełnione ciągiem "NULL"