2012-02-08 7 views
5

He estado utilizando LINQ to SQL & a las entidades por un tiempo y en general estoy muy contento con ellos. Sin embargo, sé de sus limitaciones y una en particular se está convirtiendo en un gran problema para mí. Al hacer una consulta anidada compleja en forma deRecomiende un proveedor LINQ adecuado para mí (servidor SQL, consultas complejas)

MyContext.SomeTable 
.Select(item=>new{ 
    item.SomeProperty1, 
    item.SomeProperty2, 
    item.NavigationProperty1 
     .Select(nav1=> new {// retrieve some properties}), // This triggers a single query as long as don't have more than one subquery 
    item.NavigationProperty2 
     .Select(nav2=> new {// retrieve some properties}) // This triggers one query PER ROW in the original query 
}); 

Los proveedores que he probado son LINQ a SQL/LINQ a las entidades (y lo que es peor, LinqConnect devart que las tarifas peor y genera 1 por fila en la primera propiedad de navegación)

lo que me pasa ahora que se genera (pseudocódigo):

select t1.a,t1.b,t2.c,t2.d from mytable as t1 
join navproperty1table as t2 

y 1 millones (si hay 1 millón de resultados en el primer set) de consultas como esta: select t3.e,t3.f from navproperty2table as t3 where id = X (X cambiar el X consulta al siguiente elemento devuelto por primera consulta)

lo que quiero:

select t1.a,t1.b,t2.c,t2.d,t3.e,t3.f from mytable as t1 
join navproperty1table as t2 
join navproperty2table as t3 

Ahora, por supuesto, si había 3 filas de la tabla original no sería un problema, pero tengo 10 de miles a millones de filas en mis tablas "y" necesito una consulta mucho más compleja en una sola selección (quiero obtener un gráfico complejo a la vez). Piensa en más de 20 tablas con 3-6 niveles de anidación accediendo a 2-5 mesas adicionales cada una.

Mi servidor SQL puede soportarlo perfectamente, no me importa el ancho de banda, está en una instancia vinculada por una conexión gigabit, no puedo obtener esos datos de manera diferida, de hecho "uso" todo de inmediato, así que no es solo pereza. En este momento, por razones de rendimiento, tuve que dividir la consulta en muchas consultas pequeñas y unirlas manualmente en el LINQ al tamaño del objeto, lo que da un código realmente desagradable para quien lo mantiene pero era la única solución real que tenía, por lo que incluyo todo el consultas pequeñas y unión final, estoy en más de 600 líneas de código no compartido en un único método que es totalmente imposible de mantener.

¿Hay realmente "cualquier" proveedor de producción de LINQ listo hoy antes de que vaya y los evalué a todos los que trabajan en esa mentalidad o estoy mejor codificando y comercializando el mío? (Estoy muy sorprendido de que no todos funcionen de esa manera en realidad, no puedo ver una sola instancia en la que sea mejor con el caso foreach y los que he intentado reclamar para deshacerse de n +1 con loadwith, no te deshagas de él, ya que todavía hacen n + 1 consultas, pero solo hazlo en una sola llamada, 1 ida y vuelta & n + 1 consultas no son satisfactorias cuando 1 es 10 000 y luego 10 000 000 y luego 10 000 000 000)

  • (nota que estoy especulando sobre lo que desencadena exactamente esto, pero no es la cuestión, no importa lo que desencadena este "exactamente" estoy seguro de que golpear en mi contexto actual)

PD: Tenga en cuenta que estoy ejecutando .NET 4.0 full profi En un servidor de windows 2008 o superior y en SQL Server 2008 o superior, un proveedor que no soporta nada más estaría bien, no tengo requisitos para migración, portabilidad, versiones inferiores de .NET, menor compatibilidad con servidores sql, etc. incluso versiones más recientes es una opción si es necesario.Tampoco tengo ningún requisito previo para modelar o funciones avanzadas, la base de datos ya está allí, solo quiero consultar tablas, así que algo sin soporte de modelado/vistas/DML/procedimiento almacenado/funciones está bien, mi único requisito es la generación de SQL sensata en consultas complejas y gráficos de objetos

EDIT: una aclaración aquí es un ejemplo real de la emisión con un todo el mundo DB puede conseguir, AdventureWorks

consulta de los empleados para cada contacto

Contacts 
.Select(cont=>new 
{ 
    cont.EmailAddress, 
    cont.EmailPromotion, 
    Employees = cont.Employees 
     .Select(emp=>new 
     { 
      emp.Gender, 
      emp.HireDate 
     }).ToList() 
}).ToList() 

Genera

SELECT [t0].[EmailAddress], [t0].[EmailPromotion], [t1].[Gender], [t1].[HireDate], (
SELECT COUNT(*) 
FROM [HumanResources].[Employee] AS [t2] 
WHERE [t2].[ContactID] = [t0].[ContactID] 
) AS [value] 

DE [persona]. [Contacto] AS [t0] LEFT OUTER JOIN [HumanResources]. [Empleados] AS [t1] en [t1]. [Contact ID] = [t0]. [ContactID] ORDEN pOR [t0]. [ContactID], [t1]. [IdEmpleado]

Ahora simplemente consultar los vendedores para cada contacto Contactos .Elija (cont => nueva { cont.EmailAddress, cont.EmailPromotion, Vendedores = cont.VendorContacts.Select (vend => new { vend.ContactTypeID, vend.ModifiedDate..}) ToList() }) ToList()

todavía bien:.

SELECT [t0].[EmailAddress], [t0].[EmailPromotion], [t1].[ContactTypeID], [t1].[ModifiedDate], (
SELECT COUNT(*) 
FROM [Purchasing].[VendorContact] AS [t2] 
WHERE [t2].[ContactID] = [t0].[ContactID] 
) AS [value] 

DE [persona] [Contacto] AS [t0] LEFT OUTER JOIN [La adquisición] [VendorContact. ] AS [t1] en [t1]. [ContactID] = [t0]. [ContactID] ORDER BY [t0]. [ContactID], [t1]. [VendorID]

Ahora consulta de ambos a la vez (disparadores Consulta de fila X)

Contacts 
.Select(cont=>new 
{ 
    cont.EmailAddress, 
    cont.EmailPromotion, 
    Employees = cont.Employees 
     .Select(emp=>new 
     { 
      emp.Gender, 
      emp.HireDate 
     }).ToList(), 
    Vendors = cont.VendorContacts.Select(vend=>new 
    { 
     vend.ContactTypeID, 
     vend.ModifiedDate 
    }).ToList() 
}).ToList() 

genera el feo y lento (no pegar todo por razones obvias, pero usted consigue el punto):

SELECT [t0].[EmailAddress], [t0].[EmailPromotion], [t1].[Gender], [t1].[HireDate], (
SELECT COUNT(*) 
FROM [HumanResources].[Employee] AS [t2] 
WHERE [t2].[ContactID] = [t0].[ContactID] 
) AS [value], [t0].[ContactID] 
FROM [Person].[Contact] AS [t0] 
LEFT OUTER JOIN [HumanResources].[Employee] AS [t1] ON [t1].[ContactID] = [t0].[ContactID] 
ORDER BY [t0].[ContactID], [t1].[EmployeeID] 
GO 

-- Region Parameters 
DECLARE @x1 Int = 1 
-- EndRegion 
SELECT [t0].[ContactTypeID], [t0].[ModifiedDate] 
FROM [Purchasing].[VendorContact] AS [t0] 
WHERE [t0].[ContactID] = @x1 
GO 

-- Region Parameters 
DECLARE @x1 Int = 2 
-- EndRegion 
SELECT [t0].[ContactTypeID], [t0].[ModifiedDate] 
FROM [Purchasing].[VendorContact] AS [t0] 
WHERE [t0].[ContactID] = @x1 
GO 

-- Region Parameters 
DECLARE @x1 Int = 3 
-- EndRegion 
SELECT [t0].[ContactTypeID], [t0].[ModifiedDate] 
FROM [Purchasing].[VendorContact] AS [t0] 
WHERE [t0].[ContactID] = @x1 
GO 

-- Region Parameters 
DECLARE @x1 Int = 4 
-- EndRegion 
SELECT [t0].[ContactTypeID], [t0].[ModifiedDate] 
FROM [Purchasing].[VendorContact] AS [t0] 
WHERE [t0].[ContactID] = @x1 
GO 

-- Region Parameters 
DECLARE @x1 Int = 5 
-- EndRegion 
SELECT [t0].[ContactTypeID], [t0].[ModifiedDate] 
FROM [Purchasing].[VendorContact] AS [t0] 
WHERE [t0].[ContactID] = @x1 
GO 

-- Region Parameters 
DECLARE @x1 Int = 6 
-- EndRegion 
SELECT [t0].[ContactTypeID], [t0].[ModifiedDate] 
FROM [Purchasing].[VendorContact] AS [t0] 
WHERE [t0].[ContactID] = @x1 
GO 

-- Region Parameters 
DECLARE @x1 Int = 7 
-- EndRegion 
SELECT [t0].[ContactTypeID], [t0].[ModifiedDate] 
FROM [Purchasing].[VendorContact] AS [t0] 
WHERE [t0].[ContactID] = @x1 
GO 

-- Region Parameters 
DECLARE @x1 Int = 8 
-- EndRegion 
SELECT [t0].[ContactTypeID], [t0].[ModifiedDate] 
FROM [Purchasing].[VendorContact] AS [t0] 
WHERE [t0].[ContactID] = @x1 
GO 

-- Region Parameters 
DECLARE @x1 Int = 9 
-- EndRegion 
SELECT [t0].[ContactTypeID], [t0].[ModifiedDate] 
FROM [Purchasing].[VendorContact] AS [t0] 
WHERE [t0].[ContactID] = @x1 
GO 

-- Region Parameters 
DECLARE @x1 Int = 10 
-- EndRegion 
SELECT [t0].[ContactTypeID], [t0].[ModifiedDate] 
FROM [Purchasing].[VendorContact] AS [t0] 
WHERE [t0].[ContactID] = @x1 
GO 

lo que espero/les gustaría ver generada:

SELECT [t0].[EmailAddress], [t0].[EmailPromotion], [t1].[Gender], [t1].[HireDate], [t2].[ContactTypeID], [t2].[ModifiedDate] ,[t0].[ContactID] 
FROM [Person].[Contact] AS [t0] 
LEFT OUTER JOIN [HumanResources].[Employee] AS [t1] ON [t1].[ContactID] = [t0].[ContactID] 
LEFT OUTER JOIN [Purchasing].[VendorContact] AS [t2] ON [t2].[ContactID] = [t0].[ContactID] 
GO 
+0

¿Activa realmente una consulta por fila, o genera una subconsulta correlacionada que SQL Server podría optimizar a algún plan de ejecución join-esque? –

+0

Genera una subconsulta (que está bien) cuando hay a lo sumo 1 propiedad de navegación, después de eso genera una consulta real "por" fila, que es una locura, como en seleccionar bla de t1 donde id = un número real no una condición , esta línea repite 1 millón de veces –

+0

Supongo que está buscando una solución que produce una consulta con 1 combinación para el primer 'Seleccionar()' en una propiedad de navegación, seguido de 1 consulta por cada subsecuente 'Seleccionar()' (con una cláusula WHERE en el ID padre)? Me imagino que no quieres una gran consulta con un inevitable producto cartesiano. –

Respuesta

0

he encontrado un proveedor que parece manejar mi problema central (generación de SQL cuerdo vs generando millones de declaraciones para subconsultas), no estoy seguro si es una buena opción aún, ya que esto depende de sus respuestas.

http://www.llblgen.com/tinyforum/Messages.aspx?ThreadID=20658&StartAtMessage=0&#116494

cualquier otro proveedor que debo saber de? Si logré perder este hasta ahora, puede haber otros y estaré encantado de compararlos. Lo que tenemos ahora es

falla totalmente mi exigencia de no volver 1 consulta por número de fila: - LINQ to SQL - LINQ a Entidades - devart LinqConnect

parece funcionar - LLBLGen

No comprobado/necesita retroalimentación - Telerik OpenAccess - NHibernate - Mindscape velocidad de la luz

cualquier otro que deba conocer fuera?

+0

Aceptar mi propia respuesta ya que nadie como sugirió un proveedor que trabaja desde que encontré este. –

1

Una solución sería para crear un view

de su definición

select t1.a,t1.b,t2.c,t2.d,t3.e,t3.f from mytable as t1 join navproperty1table as t2 join navproperty2table as t3 

y use linq-2-sql para consultar esa vista.

No estoy seguro si entiendo su consulta por completo, pero uou sólo podría hacer

from x in MyContext.Sometable 
Select new { x.a, x.b, x.t2.c, x.t2.d, x.t3.f } 

y así sucesivamente .. No puedo probarlo ahora mismo, pero estoy bastante seguro de yhis crearán la selección (y sólo uno) usted quiere.

+0

Crear una vista no es realmente una opción para 2 razones: 1) Quiero evitar todo lo que tenga que ver con SQL, el objetivo de usar LINQ fue deshacerme de SQL completamente y manipular gráficos de objetos y 2) devolvería objetos planos, mientras que necesito un gráfico, construyendo el gráfico por la mano sería mucho más difícil de mantener que mi código actual (ten en cuenta que estamos hablando de muchos niveles de anidación con muchas tablas cada uno, infierno incluso si todo funcionaba y podría escribir la consulta LINQ, ya que naturalmente la consulta sería fácil) ser de 300 líneas de longitud) –

+0

Dividir en 2 comentarios debido a la restricción de tamaño, tampoco estoy buscando una solución alternativa, mi solución actual funciona muy bien, solo estoy buscando hacer que el código se pueda mantener haciendo que refleje el gráfico del objeto real (en lugar de toneladas de datos planos más tarde se unieron), y el ejemplo de consulta que usted dio no funcionaría, como puede ver en las propiedades de navegación, T2 y T3 serían colecciones, así que no hay manera de hacer T2.c porque hay muchos elementos con " c "en T2, que nos devuelve a las selecciones anidadas y la generación de código incorrecto –

0

Creo que lo más cercano que puede obtener es NHibernate's Fetch (sin linq).

Con datos profundamente anidados (p.ThenFetchMany), no me sorprendería si también alcanzas rápidamente los límites de NHibernate. Las consultas complejas siempre son muy difíciles en las herramientas O/RM. NHibernate siempre me ha impresionado cuando se trata de SQL generado (¡linq-to-nhibernate todavía no!). Pero incluso con escenarios menos complejos, a veces era un gran trabajo evitar el problema 1 + N.

Quizás con NHibernate's HQL puede lograr lo que quiere.

Con linq, creo que lo mejor que puede hacer es recuperar el gráfico de objetos requerido en el menor número posible de consultas.

+0

Aceptaremos esto como una respuesta después de un tiempo a menos que alguien sugiera un proveedor linq de la lista que haga lo que necesito. –

0

También podría hacer algo como esto:

var venderContacts= VendorContacts.ToLookup (u =>u.ContactID); 
var contracts=Contacts 
    .Select(cont=>new 
    { 
     cont.EmailAddress, 
     cont.EmailPromotion, 
     Employees = cont.Employees 
      .Select(emp=>new 
      { 
       emp.Gender, 
       emp.HireDate 
      }).ToList(), 
     Vendors = venderContacts[cont.ContanctID] 
    }).ToList(); 
+0

Ya estoy haciendo algo similar, ya que dije que no tengo ningún problema para trabajar en este momento, solo quiero evitar este lío y tener un proveedor de linq (es decir, cambiar la API, "no" cambiar mi código) para que el código pueda estar limpio. Un proveedor decente debería ser capaz de mapear un gráfico sin soluciones –

Cuestiones relacionadas