Pergunta

Considere minha classe Event, e que eu armazenar DateTime está no DB como datas de UTC. Eu simplesmente quero retornar um intervalo filtrado com base na data atual em um determinado fuso horário -? Direita fácil

Esta multa funciona:

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

Isso também funciona bem:

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

.. e, adicionalmente, atende as minhas necessidades de fuso horário.

Então aqui estou eu pensando que eu pode mover esta conversão específica para este método de extensão:

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

Isso não funciona:

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

.. e eu recebo este NotSupportedException:. Método 'System.DateTime RiyadhTimeFromUtc (System.DateTime)' não tem suportado tradução para SQL

Esta é, obviamente lixo como o compilador feliz converteu-o para SQL quando o código idêntico não estava no método de extensão.

Eu tenho que correr para a "tradução não apoiou a SQL" problema antes com certos tipos e, mais recentemente DateTime do. Mas meus testes acima e este link provar que o método AddHours deve ser suportado na tradução SQL.

Se alguém pudesse me o que estou fazendo de errado aqui (ou uma abordagem alternativa diferente para este problema) dizer que eu ficaria muito grato.

Foi útil?

Solução

Você tem que pensar sobre isso em termos de uma árvore de expressão, que é como Linq-to-SQL analisa sua consulta para transformá-lo em SQL.

Ao examinar a árvore vai ver um objeto DateTime e, em seguida, verificar se o método chamado é um dos mais suportados (Add, AddHours, etc.) para que quando você usa o método diretamente ele funciona bem.

Quando você usar algum outro método de extensão, ele não pode ir e olhar dentro desse método para ver o que ele faz, como as informações sobre o corpo do método não está na árvore de expressão, está escondido no IL. Por isso, não importa se o conteúdo do método de extensão são suportados, porque Linq-to-SQL pode não funcionar para fora o que os conteúdos são.

O ponto de métodos de criação é encapsulamento e ocultamento de informações, que normalmente funciona bem em desenvolvimento de aplicações, mas infelizmente aqui ele está escondendo a informação de Linq-to-SQL que você precisa ser capaz de ver as informações.


Em resposta à pergunta editada - como você resolver isso? Se você quiser manter o cálculo da data de dentro da expressão Linq, a única coisa que você pode fazer para manter a consulta eficiente é não usar um método de extensão, e usar apenas AddHours(3) diretamente no objeto DateTime.

É uma das limitações infelizes de Linq. Como muitas coisas que é uma abstração um pouco de fuga que, ao fornecer a sintaxe comum sobre uma variedade de fontes, tem diferentes limitações e restrições sobre quais operações o provedor para a fonte pode / apoiarão (por exemplo, isso iria funcionar perfeitamente bem no Linq-to -Objects uma vez que não necessitam de traduzir a árvore de expressão para executá-lo).

Outras dicas

Parece que para LINQ to SQL para ser capaz de descobrir que RiyadhTimeFromUtc foi realmente apenas uma chamada para AddHours(3) que teria de fazer alguma análise bastante sofisticada de seu código. Digamos que você escreveu:

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

Como seria traduzir isso para SQL? Consulte esta discussão para mais algumas informações.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top