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
¿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? –
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 –
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. –