Почему я не могу приводить обнуляемый DateTime в виде строки в запросе LinQ? - PullRequest
8 голосов
/ 15 ноября 2011

Я пытаюсь взять значение DateTime, и если оно не равно нулю, вернуть строку короткого времени.Мой запрос выглядит следующим образом: (TimeIn НЕ NULLABLE, тогда как TimeOut NULLABLE)

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

, но это не удается, когда я пытаюсь связать данные с ошибкой:

Не удалось перевестивыражение 'Table (TimePosting) .Where (t => (t.MemberID == Invoke (значение (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 (значение (System.Func`1 [System.DateTime]))). ToShortTimeString (), "-------"), часы= "")) 'в SQL и не может обрабатывать его как локальное выражение.

Я также получаю похожую ошибку, если пытаюсь использовать:

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

однако,если я изменю свойство TimeOut на:

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

, оно будет работать нормально, но не будет форматировать время так, как я хочу (shortTimeString).

что с этим?

Ответы [ 6 ]

9 голосов
/ 15 ноября 2011

Как уже говорили другие, проблема заключается в попытке преобразовать 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() здесь в основном означает «прекратите пытаться обработать запрос с использованием SQL; сделайте все остальное в LINQ to Objects».

6 голосов
/ 15 ноября 2011

ToShortTimeString() не имеет перевода на SQL. По этой причине преобразование оператора в один оператор SQL завершается неудачно, и генерируется исключение.

Если разбить оператор на два вызова (один для извлечения данных, а другой для создания проекции), все будет работать нормально:

// 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() :
                    "-------"
            };
2 голосов
/ 15 ноября 2011

Проще говоря, этот конкретный поставщик linq не поддерживается.

Ваш запрос linq преобразуется в дерево выражений. Поставщик SQL Linq должен преобразовать это дерево выражений в SQL. Понятно, что он не имеет возможности переводить каждую функцию .NET.

Ваше решение - явно запустить SQL, вызвав ToArray или ToList, а затем разрешить 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 = ""                                               
                                         }
                                );
2 голосов
/ 15 ноября 2011

Вы пробовали:

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

Обычно это дает короткий формат DateTime. Работает ли он или нет, будет зависеть от того, может ли он быть переведен в SQL или нет.

Если это не сработает, вам придется разбить запрос на две части. Первый получает данные, второй форматирует их. Вам придется преобразовать первый запрос в список (.ToList()), чтобы заставить SQL быть оцененным.

1 голос
/ 15 ноября 2011

Ваш запрос преобразуется LINQ в SQL, который запускается для вашей базы данных, и, очевидно, нет способа перевести t.TimeOut.Value.ToShortTimeString() в SQL.

Возможные решения:

  1. Сначала выберите ваши данные из базы данных (вызвав .ToList() или .ToArray() в вашем запросе LINQ), который преобразует ваш IQueryable<> в IEnumerable<> и затем примените свое преобразование для каждой выбранной строки.
  2. Используйте представление, которое берет исходную таблицу и выполняет преобразование с помощью функции CONVERT () на SQL Server и использует его в качестве источника для Linq-to-Класс SQL.Это было бы исполнителем, но требует некоторых изменений на стороне сервера.
0 голосов
/ 15 марта 2013

У меня была такая же проблема в проекте на vb.net.Решение, которое я нашел, основано на использовании:

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

В этом случае, если выбранное поле (table.field) имеет значение, оно конвертируется в датуиначе строка, если поле не имеет значения, выходное поле заполняется строкой "NULL"

...