2008-11-11 12 views
28

.NET 3.5, C#¿Puede LINQ to SQL consultar un campo XML DB-serverside?

Tengo una aplicación web con una función de "búsqueda". Algunos de los campos que se pueden buscar son columnas de primera clase en la tabla, pero algunos de ellos son, de hecho, campos anidados dentro de un tipo de datos XML.

Anteriormente, construí un sistema para construir dinámicamente el SQL para mi búsqueda. Tenía una buena jerarquía de clases que creaba expresiones SQL y sentencias condicionales. El único problema era que no era seguro contra ataques de inyección SQL.

Estaba leyendo el excelente artículo de Rob Conery (http://blog.wekeroad.com/2008/02/27/creating-in-queries-with-linq-to-sql/) que indica que múltiples consultas pueden combinarse en una única consulta TSQL para el servidor si el resultado IQueryable nunca se enumera. Esto me hizo pensar que la construcción de mi búsqueda dinámica era demasiado complicada, solo necesitaba combinar múltiples expresiones LINQ.

Por ejemplo (artificial):

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

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

resultados en las siguientes consulta:

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

Me di cuenta de que esta podría ser la solución perfecta para una simple generación de consultas dinámicas segura de SQL Inyección: simplemente recorro mi resultado IQueryable y ejecuto expresiones condicionales adicionales para obtener mi expresión de ejecución única final.

Sin embargo, no puedo encontrar ningún soporte para la evaluación de datos XML. En TSQL, para obtener un valor de un nodo XML, que haría algo así

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

Pero no puedo encontrar el LINQ a SQL equivalente a crear esta evaluación. ¿Existe uno? Sé que puedo evaluar todas las condiciones no XML de la base de datos, y luego hacer mi evaluación XML del lado del código, pero mis datos son lo suficientemente grandes que A) eso es mucho tráfico de red para arrastrar el rendimiento y B) Saldré excepciones de memoria si no puedo evaluar el primer lado de la base de datos XML para excluir ciertos conjuntos de resultados.

Ideas? Sugerencias?

Pregunta de bonificación: si la evaluación XML es, de hecho, posible de lado DB, ¿qué pasa con el soporte de FLWOR?

+1

es a finales de 2013 - las actualizaciones? –

+1

@BenjaminGruenbaum Ha pasado mucho tiempo desde que pensé en esta pregunta. Mi caso de uso desapareció cuando cambié de trabajo entre entonces y ahora. Lo mejor que puedo recordar es que utilicé el enfoque recomendado por DanialM a continuación, que consistía en hacer una llamada a una función definida por el usuario. No puedo decir que he notado un soporte directo en LINQ desde entonces (pero tampoco he estado buscando) – Matt

+1

Interesante - Trataré de obtener nuevas respuestas 5 años después con una recompensa. –

Respuesta

12

Ahora que es una pregunta interesante.

En este momento, no se puede indicar a SQL Server que realice funciones XML directamente desde Linq. Sin embargo, puede hacer que Linq use funciones definidas por el usuario ... entonces, podría configurar un udf para procesar el xml, obtener los datos correctos, etc., y luego usar eso en su expresión de Linq. Esto se ejecutará en el servidor y debe hacer lo que desee. Sin embargo, hay una limitación importante: la ruta XML que está buscando (el primer parámetro para xmlColumn.value o similar) tiene que estar integrada en la función porque tiene que ser una cadena literal, no puede construirse desde un parámetro de entrada (por ejemplo). Por lo tanto, puede usar las UDF para obtener los campos que conoce cuando escribe la UDF, pero no como una forma general de obtener datos de columnas XML.

Consulte la sección de Funciones definidas por el usuario (UDF) de Scott Gutherie's excellent Blog series on Linq to SQL para obtener más información sobre la implementación.

Espero que esto ayude.

+0

¡Gracias, Daniel, fue el truco! Había leído ese artículo hace un tiempo, pero no hice clic en él cuando leí el artículo que mencioné anteriormente sobre la combinación de múltiples consultas juntas antes de la ejecución. – Matt

4

Para aclarar la respuesta de Daniel, no puede usar una función para hacer esto a menos que se corrija la parte de XPath de la consulta. Ver mi entrada de blog en este: http://conficient.wordpress.com/2008/08/11/linq-to-sql-faq-xml-columns-in-sql/

Básicamente no puede consultar una columna xml a través de LINQ a SQL. Aunque devuelve un tipo de elemento XElement, no puede hacer ninguna traducción SQL cuando intenta filtrar en esta columna.

LINQ to SQL admite el uso de UDF, pero SQL no le permitirá usar una cadena de parámetros en una consulta XML xpath; tiene que ser un literal de cadena. Eso significa que puede funcionar si el XPath está fijo en el momento del diseño, pero no si usted quiere poder pasar una declaración XPath variable.

Esto lleva a otras dos maneras alternativas de hacerlo: instrucción SQL en línea (que niega el valor de tener LINQ) y escribir una función de Biblioteca SQL en .NET CLR para hacer esto.

+0

Consulte también una actualización en ese blog: http://conficient.wordpress.com/2011/01/20/querying-xml-fields-in-linq-to-sql/ – PaulG

+0

La mejor manera de aclarar una respuesta es hacer solo eso: edite la respuesta para aclararla, o si no (en ese momento) tiene derechos de edición, comente para señalar el problema. (¿Fue así como SO trabajó en diciembre de 2008? No tengo ni idea, me uní ocho meses después. :-) Fue cuando me uní, pero SO estaba creciendo y cambiando rápidamente en ese momento). –

-1

Este no es el mejor, no para todas las consultas, y no completamente LINQ, pero funciona y es rápido:

un campo SQL XML aceptar el ".ToString", por lo que puede hacer:

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

lo uso para limitar las líneas volvieron, y luego, miro para cada regresaron líneas