Pregunta

Considere mi clase Evento , y que almacene DateTime en la base de datos como fechas UTC. Simplemente quiero devolver un rango filtrado en función de la fecha actual en una zona horaria en particular, ¿no es así?

Esto funciona bien:

IQueryable<Event> test1 = this.GetSortedEvents().Where(e => e.FinishDateTime.Date >= DateTime.UtcNow.Date);

Esto también funciona bien:

IQueryable<Event> test2 = this.GetSortedEvents().Where(e => e.FinishDateTime.AddHours(3).Date >= DateTime.UtcNow.AddHours(3).Date);

.. y además cumple con los requisitos de mi zona horaria.

Entonces, aquí estoy pensando que puedo mover esta conversión específica a este método de extensión:

    public static DateTime RiyadhTimeFromUtc(this DateTime utcTime)
    {
        return utcTime.AddHours(3);
    }

Esto NO funciona:

IQueryable<Event> test3 = this.GetSortedEvents().Where(e => e.FinishDateTime.RiyadhTimeFromUtc().Date >= DateTime.UtcNow.RiyadhTimeFromUtc().Date);

.. y obtengo esta NotSupportedException: el método 'System.DateTime RiyadhTimeFromUtc (System.DateTime)' no tiene traducción compatible con SQL.

Esto obviamente es una basura ya que el compilador lo convirtió felizmente a SQL cuando el código idéntico NO estaba en el método de extensión.

Me he encontrado con el " no tiene traducción compatible con SQL " Problema antes con ciertos tipos y más recientemente DateTime. Pero mis pruebas anteriores y este enlace demuestran que el método AddHours debe ser admitido en la traducción de SQL.

Si alguien pudiera decirme qué estoy haciendo mal aquí (o una solución alternativa para este problema), estaría muy agradecido.

¿Fue útil?

Solución

Tienes que pensarlo en términos de un árbol de expresiones, que es como Linq-to-SQL analiza tu consulta para convertirla en SQL.

Al examinar el árbol verá un objeto DateTime y luego verificará si el método llamado es uno de los admitidos ( Add , AddHours , etc.) así que cuando usas el método directamente funciona bien.

Cuando usa algún otro método de extensión, no puede ir y mirar dentro de ese método para ver qué hace, ya que la información sobre el cuerpo de ese método no está en el árbol de expresiones, está oculta en la IL. Por lo tanto, no importa si los contenidos del método de extensión son compatibles, ya que Linq-to-SQL no puede determinar cuáles son los contenidos.

El punto de crear métodos es la encapsulación y la ocultación de información, que normalmente funciona bien en el desarrollo de aplicaciones, pero desafortunadamente aquí está ocultando la información de Linq-to-SQL a quien necesitas para poder ver la información.


En respuesta a la pregunta editada, ¿cómo resuelves esto? Si desea mantener el cálculo de la fecha dentro de la expresión Linq, lo único que puede hacer para mantener la consulta eficiente es no usar un método de extensión, y simplemente usar AddHours (3) directamente en la < código> DateTime objeto.

Es una de las limitaciones desafortunadas de Linq. Como muchas otras cosas, es una abstracción un tanto permeable que, si bien proporciona una sintaxis común en un rango de fuentes, tiene diferentes limitaciones y restricciones en las operaciones que el proveedor de la fuente puede admitir (por ejemplo, esto funcionaría perfectamente bien en Linq-to -Objetos, ya que no es necesario traducir el árbol de expresiones para ejecutarlo).

Otros consejos

Parece que para que LINQ-to-SQL sea capaz de entender que RiyadhTimeFromUtc era solo una llamada a AddHours (3) , tendría que hacer algo. Análisis bastante sofisticado de su código. Di que escribiste:

public static DateTime RiyadhTimeFromUtc(this DateTime utcTime)
{
    if (Random.GetInt() < 99) {
       return utcTime.AddHours(3);
    } else {
       return utcTime.AddDays(5);
    }
}

¿Cómo se traduciría eso a SQL? Consulte esta discusión para más información.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top