2017-07-23 6 views
2

Ich freue mich auf einige häufige Abfragen über mehrere Tabellen. In einem sehr einfachen Beispiel alle Tabellen haben eine DataDate Spalte, so habe ich Anfragen wie folgt aus:Parametrize F # Abfragen basierend auf Eigenschaften

let dtexp1 = query { for x in table1 do maxBy x.Datadate } 
let dtexp2 = query { for x in table2 do maxBy x.Datadate } 

Basierend auf einem previous question ich folgendes tun:

let mkQuery t q = 
     query { for rows in t do maxBy ((%q) rows) } 

let getMaxDt1 = mkQuery table1 (<@ fun q -> q.Datadate @>) 
let getMaxDt2 = mkQuery table2 (<@ fun q -> q.Datadate @>) 

würde mich interessieren, ob es andere Lösungen, die keine Angebote verwenden. Der Grund dafür ist, dass für kompliziertere Anfragen die Zitate und das Spleißen schwierig zu lesen sind.

Das wird zum Beispiel nicht funktionieren, offensichtlich, da wir nicht wissen, dass x Eigenschaft DataDate hat.

let getMaxDt t = query { for x in t do maxBy x.Datadate } 

Wenn ich nicht abstrahieren kann über die Art der Tabelle 1, Tabelle 2, usw., die durch SqlProvider erzeugt werden.

Antwort

3

Die Antwort hängt sehr davon ab, welche Art von Abfragen Sie erstellen müssen und wie statisch oder dynamisch sie sind. Generell gilt:

  • LINQ ist toll, wenn sie meist statisch sind und wenn man leicht alle Vorlagen für alle Abfragen auflisten können Sie benötigen - die Haupt nette Sache ist, dass es statisch überprüft die Abfragen geben

  • LINQ ist nicht so toll, wenn Ihre Abfragestruktur sehr dynamisch ist, weil Sie dann am Ende viele Zitate schreiben und die Typprüfung manchmal in den Weg kommt.

Wenn Ihre Abfragen sind sehr dynamisch (einschließlich der Quelle dynamisch Auswahl), aber nicht zu komplex ist (zB keine Lust Gruppierungen keine Lust Joins), dann könnte es einfacher sein, Code zu schreiben SQL-Abfrage von einem generieren F # -Domänenmodell.

Für Ihr einfaches Beispiel ist die Abfrage wirklich nur ein Tabellenname und Aggregation:

type Column = string 
type Table = string 

type QueryAggregate = 
    | MaxBy of Column 

type Query = 
    { Table : Table 
    Aggregate : QueryAggregate } 

Sie können dann zwei Abfragen erstellen mit:

let q1 = { Table = "table1"; Aggregate = MaxBy "Datadate" } 
let q2 = { Table = "table2"; Aggregate = MaxBy "Datadate" } 

Übersetzen diese Abfragen zu SQL recht ist einfach:

let translateAgg = function 
    | MaxBy col -> sprintf "MAX(%s)" col 

let translateQuery q = 
    sprintf "SELECT %s FROM %s" (translateAgg q.Aggregate) q.Table 

Je nachdem, wie reich Ihre Abfragen sein können, die Übersetzung kann sehr kompliziert werden, aber wenn die Struktur ziemlich einfach ist, dann könnte dies einfach eine einfachere Alternative als das Konstruieren der Abfrage mit LINQ sein. Wie gesagt, es ist schwer zu sagen, was besser sein wird, ohne den genauen Anwendungsfall zu kennen!

+0

thx. Das ist ein interessanter Ansatz. Wird darüber nachdenken. Ja, die Abfragen sind etwas dynamisch und ich kann mit Zitaten umgehen. Aber war neugierig, ob es etwas gibt, das es idiomatisch/einfacher macht, mit Generika oder Schnittstellen, oder (keuchen) SRTP als letzten Ausweg. – s952163

+1

Wenn Sie die "string concat" Straße gehen: Stellen Sie sicher, dass Sie Ihre Anwendung nicht in die SQL Injection-Gefahrenzone bringen. – CaringDev

+1

@CaringDev Guter Punkt. Wenn 'SqlCommand' verwendet wird, um die resultierende Abfrage auszuführen, besteht die beste Option darin, Parameter über' SqlParameter' hinzuzufügen (siehe https://docs.microsoft.com/en-us/dotnet/framework/data/adonet/configuring-parameters- Und-Parameter-Datentypen).Auf diese Weise werden Benutzereingaben niemals mit der Abfragezeichenfolge verknüpft und Sie sollten in Ordnung sein! –

Verwandte Themen