2008-11-11 7 views
28

.NET 3.5, C#Est-ce que LINQ to SQL peut interroger un champ XML DB-côté serveur?

J'ai une application web avec une fonction de "recherche". Certains des champs qui sont consultables sont des colonnes de première classe dans la table, mais certains d'entre eux sont en fait des champs imbriqués dans un type de données XML.

Auparavant, j'ai construit un système pour construire dynamiquement le SQL pour ma recherche. J'avais une belle hiérarchie de classes qui construisait des expressions SQL et des instructions conditionnelles. Le seul problème était qu'il n'était pas à l'abri des attaques par injection SQL. Je lisais l'excellent article de Rob Conery (http://blog.wekeroad.com/2008/02/27/creating-in-queries-with-linq-to-sql/) qui indiquait que plusieurs requêtes peuvent être combinées en une seule requête TSQL pour le serveur si le résultat IQueryable n'est jamais énuméré. Cela m'a amené à penser que ma construction de recherche dynamique était beaucoup trop compliquée - j'avais juste besoin de combiner plusieurs expressions LINQ.

Par exemple (pièce):

Auteur: ID (int), LastName (varchar (32)), FirstName (varchar (32))

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

résultats dans les domaines suivants requête:

SELECT [t0].[ID], [t0].[LastName], [t0].[FirstName] 
FROM [dbo].[Author] AS [t0] 
WHERE ([t0].[LastName] = Smith) AND ([t0].[FirstName] = John) 

Je réalise cela pourrait être la solution idéale pour une génération de requête dynamique simple qui est sûr de SQL injection - Je boucle juste sur mon résultat IQueryable et j'exécute des expressions conditionnelles supplémentaires pour obtenir mon expression finale à exécution unique.

Cependant, je ne trouve aucun support pour l'évaluation des données XML. En TSQL, pour obtenir une valeur à partir d'un noeud XML, nous aimerions faire quelque chose comme

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

Mais je ne trouve pas le LINQ to SQL équivalent de la création de cette évaluation. En existe-t-il un? Je sais que je peux évaluer toutes les conditions DB non-XML côté, et puis faire mon côté d'évaluations XML, mais mes données sont assez grandes que A) c'est beaucoup de trafic réseau pour faire glisser sur la performance et B) Je vais sortir ... des exceptions de mémoire si je ne peux pas évaluer le premier côté de la base de données XML pour exclure certains ensembles de résultats.

Des idées? Suggestions?

Question supplémentaire - Si l'évaluation XML est en fait possible côté DB, qu'en est-il du support FLWOR?

+1

C'est la fin de l'année 2013 - les mises à jour? –

+1

@BenjaminGruenbaum Cela fait longtemps que j'ai réfléchi à cette question. Mon cas d'utilisation a disparu quand j'ai changé d'emploi entre maintenant et maintenant. Le mieux que je me souvienne, j'ai utilisé l'approche recommandée par DanialM ci-dessous, qui était de faire un appel à une fonction définie par l'utilisateur. Je ne peux pas dire comme j'ai remarqué un support direct dans LINQ depuis (mais je n'ai pas regardé non plus) – Matt

+1

Intéressant - Je vais essayer de pêcher de nouvelles réponses 5 ans plus tard avec une prime. –

Répondre

12

Maintenant, c'est une question intéressante. À l'heure actuelle, vous ne pouvez pas demander à SQL Server d'exécuter des fonctions XML directement à partir de Linq. Cependant, vous pouvez demander à Linq d'utiliser des fonctions définies par l'utilisateur ... Ainsi, vous pouvez configurer un fichier udf pour traiter le fichier XML, obtenir les bonnes données, etc., puis l'utiliser dans votre expression Linq. Cela va s'exécuter sur le serveur et devrait faire ce que vous voulez. Il y a une limitation importante, cependant: Le chemin XML que vous recherchez (le premier paramètre à xmlColumn.value ou similaire) doit être intégré dans la fonction car il doit être une chaîne littéral, il ne peut pas être construit à partir d'un paramètre d'entrée (par exemple). Vous pouvez donc utiliser les fonctions définies par l'utilisateur pour obtenir les champs que vous connaissez lors de l'écriture de la fonction définie par l'utilisateur, mais pas comme moyen général d'obtenir des données à partir de colonnes XML.

Consultez la section Fonctions définies par l'utilisateur (UDF) de Scott Gutherie's excellent Blog series on Linq to SQL pour plus d'informations sur l'implémentation.

Espérons que cela aide.

+0

Merci Daniel, ça a fait l'affaire! J'avais lu cet article tout à l'heure, mais il ne m'a pas cliqué quand j'ai lu l'article que j'ai mentionné plus haut sur la combinaison de plusieurs requêtes avant l'exécution. – Matt

4

Pour clarifier la réponse de Daniel - vous ne pouvez pas utiliser une fonction pour ce faire à moins que la partie XPath de la requête ne soit corrigée. Voir mon article sur ce blog: http://conficient.wordpress.com/2008/08/11/linq-to-sql-faq-xml-columns-in-sql/

Fondamentalement, vous ne pouvez pas interroger une colonne xml via LINQ to SQL. Bien qu'il renvoie un type XElement, vous ne pouvez pas effectuer de traduction SQL lorsque vous essayez de filtrer sur cette colonne.

LINQ to SQL prend en charge l'utilisation de fonctions UDF, mais SQL lui-même ne vous permet pas d'utiliser une chaîne de paramètres dans une requête xpath XML - il doit s'agir d'un littéral de chaîne. Cela signifie que cela peut fonctionner si le XPath est corrigé au moment du design, mais pas si vous voulez pouvoir passer une instruction XPath variable. Ceci n'aboutit qu'à deux autres possibilités: l'instruction SQL inline (qui annule la valeur de LINQ) et l'écriture d'une fonction SQL Library dans .NET CLR pour ce faire.

+0

Voir aussi une mise à jour sur ce blog: http://conficient.wordpress.com/2011/01/20/querying-xml-fields-in-linq-to-sql/ – PaulG

+0

La meilleure façon de clarifier une réponse est de faire juste que: Modifier la réponse pour la clarifier, ou si vous n'aviez pas (à ce moment-là) de droits de modification, commentez pour signaler le problème. (Est-ce que ça a fonctionné en décembre 2008?) Je n'en ai aucune idée, j'ai rejoint huit mois plus tard :-) C'était quand je me suis joint, mais SO grandissait et changeait rapidement à ce moment-là. –

-1

Ce n'est pas le meilleur, pas pour toutes les requêtes, et non complètement LINQ, mais fonctionne et est rapide:

un champ sql xml accepte le « .toString », de sorte que vous pouvez faire:

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

Je l'utilise pour limiter les lignes retournées, puis, je regarde pour chaque retourné lignes