2009-03-04 6 views
4

Betrachten Sie meine Event Klasse, und dass ich DateTime in der DB als UTC-Daten speichern. Ich möchte einfach einen gefilterten Bereich basierend auf dem aktuellen Datum in einer bestimmten Zeitzone zurückgeben - einfach richtig?Warum wird LINQ to SQL durch eine Erweiterungsmethode ausgetrickst? was jetzt?

Dies funktioniert:

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

Dies funktioniert auch gut:

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

.. und erfüllt zusätzlich Anforderungen meine Zeitzone.

Also hier Ich denke ich diese spezifische Umwandlung aus dieser Erweiterung Methode bewegen kann:

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

arbeiten dies nicht:

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

.. und ich diese bekommen NotSupportedException: Die Methode 'System.DateTime RiyadhTimeFromUtc (System.DateTime)' hat keine unterstützte Übersetzung in SQL.

Dies ist natürlich Quatsch, da der Compiler es glücklich in SQL konvertiert hat, als der identische Code NICHT in der Erweiterungsmethode war.

Ich habe das Problem "hat keine unterstützte Übersetzung in SQL" vor mit bestimmten Typen und zuletzt DateTime's. Aber meine Tests oben und this link beweisen, dass die AddHours-Methode in der SQL-Übersetzung unterstützt werden sollte.

Wenn mir jemand sagen könnte, was ich falsch hier tun (oder eine andere Abhilfe Ansatz für dieses Problem) ich wirklich dankbar sein würde.

Antwort

9

Sie haben darüber in Form eines Ausdrucksbaum denken, das ist, wie Linq to SQL-Abfrage parst sie in SQL zu drehen.

Wenn der Baum Prüfung wird es ein DateTime Objekt sehen und dann prüfen, ob das Verfahren auf sie genannt eines der unterstützten Einsen (Add, AddHours, etc.) so, wenn Sie die Methode verwenden, um direkt funktioniert es gut.

Wenn Sie eine andere Erweiterungsmethode verwenden, kann sie nicht in diese Methode hineinschauen, um zu sehen, was sie tut, da die Informationen über den Körper dieser Methode nicht in der Ausdrucksbaumstruktur enthalten sind. Es spielt also keine Rolle, ob der Inhalt der Erweiterungsmethode unterstützt wird, weil Linq-to-SQL nicht herausfinden kann, was der Inhalt ist.

Der Zweck der Erstellung von Methoden ist die Kapselung und das Ausblenden von Informationen, die normalerweise gut in der Anwendungsentwicklung funktionieren, aber hier verstecken sie die Informationen von Linq-to-SQL, die Sie benötigen, um die Informationen sehen zu können.


Als Antwort auf die bearbeitete Frage - wie lösen Sie das? Wenn Sie die Datumsberechnung innerhalb des Linq-Ausdrucks beibehalten möchten, können Sie die effiziente Ausführung der Abfrage nur durchführen, indem Sie keine Erweiterungsmethode verwenden und einfach AddHours(3) direkt auf dem Objekt DateTime verwenden.

Es ist eine der unglücklichen Einschränkungen von Linq.Wie viele andere Dinge ist es eine etwas undichte Abstraktion, die, obwohl sie eine gemeinsame Syntax für eine Reihe von Quellen bietet, verschiedene Beschränkungen und Einschränkungen hat, welche Operationen der Provider für die Quelle unterstützen kann (zum Beispiel würde dies in Linq-to vollkommen funktionieren) -Objects, da der Ausdrucksbaum nicht übersetzt werden muss, um ihn auszuführen.

+0

Das ist eine fantastische Erklärung. Aber ich weiß nicht, wie es mir noch hilft. Irgendwelche konkreten Vorschläge für mein Problem? –

+0

Ich werde Linq Expressions verwenden, um es zu tun - http://www.albahari.com/nutshell/predicatebuilder.aspx Danke für Ihre Hilfe –

4

Es scheint für LINQ-to-SQL in der Lage zu sein herauszufinden, dass RiyadhTimeFromUtc war wirklich nur ein Anruf an AddHours(3) würde es einige ziemlich anspruchsvolle Analyse Ihres Codes zu tun haben. Angenommen, Sie haben geschrieben:

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

Wie würde es das in SQL übersetzen? Weitere Informationen finden Sie unter this discussion.

+0

Dank für diesen Link an der Unterseite. Das hat mich auf den richtigen Weg gebracht –