Domanda

Considera la mia classe Event e che conservo DateTime nel DB come date UTC. Voglio semplicemente restituire un intervallo filtrato in base alla data corrente in un particolare fuso orario - facile vero?

Funziona bene:

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

Anche questo funziona bene:

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

.. e inoltre soddisfa i miei requisiti di fuso orario.

Quindi qui sto pensando di poter spostare questa conversione specifica su questo metodo di estensione:

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

NON funziona:

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

.. e ottengo questo NotSupportedException: il metodo 'System.DateTime RiyadhTimeFromUtc (System.DateTime)' non ha una traduzione supportata in SQL.

Questo è ovviamente spazzatura poiché il compilatore l'ha convertito felicemente in SQL quando lo stesso codice NON era nel metodo di estensione.

Ho incontrato " non ha una traduzione supportata in SQL " problema prima con alcuni tipi e più recentemente DateTime. Ma i miei test sopra e questo link dimostrano che il metodo AddHours dovrebbe essere supportato nella traduzione SQL.

Se qualcuno potesse dirmi cosa sto facendo di sbagliato qui (o un approccio diverso a questo problema) sarei davvero grato.

È stato utile?

Soluzione

Devi pensarci in termini di un albero delle espressioni, che è il modo in cui Linq-SQL analizza la tua query per trasformarla in SQL.

Quando esamina l'albero vedrà un oggetto DateTime e quindi verificherà se il metodo chiamato è uno di quelli supportati ( Aggiungi , AddHours , ecc.), quindi quando si utilizza direttamente il metodo funziona correttamente.

Quando usi un altro metodo di estensione, non può andare a guardare dentro quel metodo per vedere cosa fa, poiché le informazioni sul corpo di quel metodo non si trovano nell'albero delle espressioni, sono nascoste nell'IL. Quindi non importa se i contenuti del metodo di estensione sono supportati, poiché Linq-to-SQL non è in grado di capire quali siano i contenuti.

Il punto di creare metodi è l'incapsulamento e il nascondimento delle informazioni, che in genere funziona bene nello sviluppo di applicazioni, ma sfortunatamente qui nasconde le informazioni da Linq-a-SQL di cui è necessario essere in grado di vedere le informazioni.


In risposta alla domanda modificata: come risolverlo? Se vuoi mantenere il calcolo della data all'interno dell'espressione Linq, l'unica cosa che puoi fare per mantenere efficiente la query è non usare un metodo di estensione e usare AddHours (3) direttamente su < codice> DateTime oggetto.

È una delle sfortunate limitazioni di Linq. Come molte cose è un'astrazione un po 'permeabile che, pur fornendo una sintassi comune su una vasta gamma di fonti, ha diverse limitazioni e restrizioni sulle operazioni che il provider per la fonte può / supporterà (per esempio, questo funzionerebbe perfettamente bene in Linq-to -Oggetti in quanto non è necessario tradurre l'albero delle espressioni per eseguirlo).

Altri suggerimenti

Sembra che LINQ-to-SQL sia in grado di capire che RiyadhTimeFromUtc era davvero solo una chiamata a AddHours (3) avrebbe dovuto fare un po ' analisi piuttosto sofisticate del tuo codice. Di che hai scritto:

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

Come lo tradurrebbe in SQL? Vedi questa discussione per ulteriori informazioni.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top