2011-11-14 10 views
7

Estoy tratando de tomar un valor de DateTime, y si no es nulo, devuelva Short Time String. Mi consulta es el siguiente: (timein no es anulable, mientras que tiempo de espera es anulable)¿Por qué no puedo convertir DateTime con nulos como cadena en una consulta 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(); 

pero esto no funciona cuando intento DataBind con el error:

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.

También consigo un error similar si trato de usar:

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

Sin embargo, si cambio de la propiedad de tiempo de espera:

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

funciona bien, pero no formatea el tiempo como lo quiero (shortTimeString).

¿Qué pasa con eso?

Respuesta

9

Como han dicho otros, el problema está en tratar de convertir ToShortDateString etc a SQL. Afortunadamente, esto es fácil de arreglar: obtener los datos con SQL, luego formato en.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 llamada a AsEnumerable() aquí significa, básicamente, "dejar de tratar de procesar la consulta utilizando SQL; haga el resto en LINQ a Objetos".

+0

Supongo que el "AsEnumerable()" causa el mismo efecto como el "ToList()" en la respuesta de Justin. ¿Es el método "mejor" para usar? –

+2

@ matthew_360: No es exactamente el mismo efecto, no - 'ToList()' tiene que compilar una lista de todos los elementos, en lugar de transmitirlos. Eso es bueno si vas a usar 'times' en varios lugares sin que haya algo de buffer * que * como resultado, pero de lo contrario no veo ninguna razón para construir el buffer - personalmente usaría' AsEnumerable', que sirve * únicamente * el propósito que desea: conversión de 'IQueryable ' a 'IEnumerable '. –

5

ToShortTimeString() no tiene traducción en SQL. Por eso, la conversión de la instrucción en una única instrucción SQL falla y se lanza la excepción.

Si se rompe la declaración en dos llamadas (uno para recuperar los datos y otro para crear la proyección), las cosas van a funcionar bien:

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

¡Excelente respuesta! muchas gracias, ¡eso tiene mucho sentido! –

+0

No veo ninguna razón para desplegar más que TimeIn y TimeOut, y personalmente no elegiría la llamada 'ToList', ¿por qué no conservar la pereza de LINQ? Todo lo que realmente queremos es forzar que el resto de la consulta se realice en LINQ to Objects. –

+0

@Jon Skeet: sin la llamada a ToList, ¿no volvería la pereza a LINQ to SQL (ya que la consulta no se habría evaluado en ese punto)? El resto fue solo cortar/pegar desde arriba ... aún no he tenido tiempo para esa edición. –

1

Su consulta se transforma mediante LINQ a un SQL que es disparado contra su base de datos, y obviamente no hay forma de traducir t.TimeOut.Value.ToShortTimeString() a SQL.

soluciones posibles son:

  1. En primer lugar buscar a sus datos desde la base de datos (o llamando .ToList().ToArray() en su consulta LINQ), que convierte su IQueryable<> en IEnumerable<> y luego aplicar su transformación para cada fila obtenida.
  2. Utilice una vista que toma la tabla original y realiza la conversión utilizando la función CONVERT() en el servidor SQL y la utiliza como fuente para su clase Linq-to-SQL. Eso sería un rendimiento, pero requiere algunos cambios en el servidor.
2

Ha intentado:

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

Esto le dará normalmente el formato corto del DateTime. Si funciona o no dependerá de si se puede traducir a SQL o no.

Si no funciona, deberá dividir la consulta en dos partes. El primero obtiene los datos, el segundo los formatea. Tendrá que convertir la primera consulta en una lista (.ToList()) para forzar la evaluación del SQL.

2

Simplemente, no es compatible con este proveedor específico de linq.

Su consulta linq se convierte en un árbol de expresiones. Depende del proveedor SQL Linq convertir este árbol de expresiones en SQL. Es comprensible que no tenga la capacidad de traducir todas las funciones de .NET.

Su solución es ejecutar explícitamente el SQL llamando al ToArray o ToList, y luego permita que LinqToObjects maneje el 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

parece que tenemos una 3ra forma posible de lograr esto. ToList(), AsEnumerable() y ahora ToArray(). Estoy en demanda de que todos funcionen, me pregunto cuál es el más rápido. –

+0

@ mathew_360 - Aprendí algo de esto - No estaba al tanto de AsEnumerable. Estoy de acuerdo con Jon Skeet: AsEnumerable sería más apropiado porque ToArray obligaría a que toda la consulta termine antes de hacer las conversiones de tiempo. –

0

Tuve el mismo problema en un proyecto en vb.net. La solución que he encontrado se basa en el uso de:

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

En este caso, si el campo seleccionado (tabla.campo) tiene un valor de esto se convierte en una cadena de fecha de otro modo si el campo no tiene un valor el campo de salida se completa con la cadena "NULO"

Cuestiones relacionadas