2008-11-11 9 views
28

.NET 3.5, C#Kann LINQ to SQL ein XML-Feld DB-serverseitig abfragen?

Ich habe eine Web-App mit einer "Suche" -Funktion. Einige der Felder, die durchsucht werden können, sind erstklassige Spalten in der Tabelle, aber einige von ihnen sind tatsächlich verschachtelte Felder innerhalb eines XML-Datentyps.

Zuvor baute ich ein System für die dynamische Erstellung der SQL für meine Suche. Ich hatte eine nette Klassenhierarchie, in der SQL-Ausdrücke und bedingte Anweisungen erstellt wurden. Das einzige Problem war, dass es vor SQL-Injection-Angriffen nicht sicher war.

Ich lese ausgezeichneten Artikel Rob Conery (http://blog.wekeroad.com/2008/02/27/creating-in-queries-with-linq-to-sql/), die das aufgezählt wird nie, wenn das IQueryable Ergebnis für den Server in eine einzige TSQL Abfrage kombiniert können mehrere Abfragen hingewiesen. Das brachte mich dazu zu denken, dass meine dynamische Suchkonstruktion viel zu kompliziert war - ich musste nur mehrere LINQ-Ausdrücke kombinieren.

Zum Beispiel (erfundene):

Autor: ID (int), Name VZ (varchar (32)), Vorname (varchar (32))

context.Author.Where(xx => xx.LastName == "Smith").Where(xx => xx.FirstName == "John") 

Ergebnisse in der folgenden Abfrage:

Ich erkannte, dass dies die perfekte Lösung für eine einfache dynamische Abfrage Generation, die sicher von SQL ist injection - Ich würde einfach mein IQueryable-Ergebnis durchlaufen und zusätzliche Konditionalausdrücke ausführen, um meinen letzten Ausdruck für die Einzelausführung zu erhalten.

Ich kann jedoch keine Unterstützung für die Auswertung von XML-Daten finden. In TSQL, einen Wert aus einem XML-Knoten erhalten, würden wir so etwas wie

XMLField.value('(*:Root/*:CreatedAt)[1]', 'datetime') = getdate() 

tun, aber ich kann nicht die LINQ to SQL-äquivalent von der Erstellung dieser Auswertung finden. Gibt es einen? Ich weiß, dass ich alle Nicht-XML-Bedingungen DB-Seite auswerten kann, und dann meine XML-Auswertungscodeseite, aber meine Daten sind groß genug, dass A) viel Netzwerkverkehr ist, um die Leistung zu beeinträchtigen, und B) ich werde von Speicherausnahmen, wenn ich die XML-DB-Seite nicht auswerten kann, um bestimmte Ergebnismengen auszuschließen.

Ideen? Vorschläge?

Bonus Frage - Wenn XML Auswertung tatsächlich möglich ist DB Seite, was ist mit FLWOR Unterstützung?

+1

es ist das Ende des Jahres 2013 - alle Updates? –

+1

@BenjaminGruenbaum Es ist lange her, seit ich über diese Frage nachgedacht habe. Mein Anwendungsfall dafür verschwand, als ich den Job zwischen damals und heute wechselte. Am besten, wie ich mich erinnern kann, habe ich den unten empfohlenen Ansatz DanialM verwendet, um eine benutzerdefinierte Funktion aufzurufen. Kann ich nicht sagen, da ich seit damals direkte Unterstützung in LINQ bemerkt habe (aber ich habe auch nicht gesucht) – Matt

+1

Interessant - Ich werde versuchen, neue Antworten 5 Jahre später mit einer Prämie zu fischen. –

Antwort

12

Das ist eine interessante Frage.

Derzeit können Sie SQL Server nicht anweisen, XML-Funktionen direkt von Linq aus auszuführen. Sie können jedoch Linq benutzerdefinierte Funktionen verwenden ... so können Sie ein UDF einrichten, um die XML zu verarbeiten, erhalten Sie die richtigen Daten usw., und dann verwenden Sie das in Ihrer Linq Ausdruck. Dies wird auf dem Server ausgeführt und sollte tun, was Sie wollen. Es gibt jedoch eine wichtige Einschränkung: Der gesuchte XML-Pfad (der erste Parameter zu xmlColumn.value oder ähnlichem) muss in die Funktion eingebaut werden, da es sich um eine Zeichenfolge Literal handeln muss Eingabeparameter (zum Beispiel). Sie können UDFs also zum Abrufen von Feldern verwenden, die Ihnen beim Schreiben der UDF bekannt sind, aber nicht als allgemeine Möglichkeit, Daten aus XML-Spalten abzurufen.

Weitere Informationen zur Implementierung finden Sie im Abschnitt Unterstützte benutzerdefinierte Funktionen (UDFs) von Scott Gutherie's excellent Blog series on Linq to SQL.

Hoffe, das hilft.

+0

Danke Daniel, das hat den Trick gemacht! Ich hatte diesen Artikel schon vor einer Weile gelesen, aber er hat nicht geklickt, als ich den oben erwähnten Artikel über die Kombination mehrerer Abfragen vor der Ausführung gelesen habe. – Matt

4

Um Daniels Antwort zu klären, können Sie keine Funktion verwenden, es sei denn, der XPath-Teil der Abfrage ist behoben. Siehe meinen Blogeintrag dazu: http://conficient.wordpress.com/2008/08/11/linq-to-sql-faq-xml-columns-in-sql/

Grundsätzlich können Sie eine XML-Spalte nicht über LINQ to SQL abfragen. Obwohl es einen XElement-Typ zurückgibt, können Sie keine SQL-Übersetzungen ausführen, wenn Sie versuchen, nach dieser Spalte zu filtern.

LINQ to SQL unterstützt die Verwendung von UDFs - aber SQL selbst erlaubt Ihnen nicht, eine Parameterzeichenfolge in einer XML-xpath-Abfrage zu verwenden - es muss ein Zeichenfolgenliteral sein. Das bedeutet, dass es funktionieren kann, wenn der XPath zur Entwurfszeit festgelegt wird, aber nicht, wenn Sie eine variable XPath-Anweisung übergeben möchten.

Dies führt zu nur zwei anderen alternativen Möglichkeiten, dies zu tun: Inline-SQL-Anweisung (die den Wert von LINQ negiert) und Schreiben einer SQL-Bibliothek-Funktion in .NET CLR, um dies zu tun.

+0

Siehe auch ein Update auf diesem Blog: http://conficient.wordpress.com/2011/01/20/querying-xml-fields-in-linq-to-sql/ – PaulG

+0

Der beste Weg, um eine Antwort zu klären, ist zu tun nur das: Bearbeiten Sie die Antwort, um es zu klären, oder wenn Sie nicht (zu dieser Zeit) Bearbeitungsrechte haben, kommentieren Sie das Problem. (War das so, wie SO im Dezember 2008 gearbeitet hat? Ich habe keine Ahnung, ich bin acht Monate später beigetreten. :-) Es war als ich beitrat, aber SO wuchs und änderte sich zu dieser Zeit sehr schnell.) –

-1

Dies ist nicht die beste, nicht für alle Abfragen und nicht vollständig Linq, aber funktioniert und ist schnell:

ein XML-SQL-Feld akzeptiert die „.ToString“, so können Sie tun:

Dim txt as String = "<File>3</File>" 
Return (From P In DC.LPlanningRefs Where P.Details.ToString.Contains(txt) Select P).FirstOrDefault 

ich es verwenden, um Linien zu beschränken zurück, und dann suche ich nach jeder zurück Linien