Entity Framework Skip and Take no traducido al comando SQL
var allUseresInclude = _userService.Include(r => r.Profession);
var model = allUseresInclude.Select(s => new ViewModel.User
{
Profession = s.Profession.Name,
Id = s.Id,
ProviderUserId = s.ProviderUserId,
PlatformId = s.PlatformId,
Email = s.Email,
Password = s.Password,
RefreshToken = s.RefreshToken,
Name = s.Name,
Surname = s.Surname,
Fullname = s.Name + " " + s.Surname,
Gender = s.Gender,
ProfessionId = s.ProfessionId,
BirthDate = s.BirthDate.ToString(),
IdentityNumber = s.IdentityNumber,
Phone = s.Phone,
IsActive = s.IsActive,
CreatedDate = _common.DateTimeToTimeZone(s.CreatedDate),//.ToString("dd.MM.yyyy HH:mm"),
EmailConfirmed = s.EmailConfirmed
});
var tt = model.OrderByDescending(o => o.CreatedDate).Skip(20).Take(30).ToList();
El modelo es una variable IQueryable. Primero, estoy seleccionando las propiedades para mi ViewModel entonces trato de filtrar mi IQueryable. La ejecución de consultas tarda un tiempo y veo resultados correctos en la interfaz de usuario, pero en SQL la consulta generada no tiene compensación o límite
SELECT [r].[CreatedDate], [r.Profession].[Name] AS [Profession], [r].[Id], [r].[ProviderUserId], [r].[PlatformId], [r].[Email], [r].[Password], [r].[RefreshToken], [r].[Name], [r].[Surname], ([r].[Name] + N' ') + [r].[Surname] AS [Fullname], [r].[Gender], [r].[ProfessionId], CONVERT(VARCHAR(100), [r].[BirthDate]) AS [BirthDate], [r].[IdentityNumber], [r].[Phone], [r].[IsActive], [r].[EmailConfirmed]
FROM [User] AS [r]
LEFT JOIN [Profession] AS [r.Profession] ON [r].[ProfessionId] = [r.Profession].[Id]
Cuando despido esta consulta, devuelve todas las mesas así que supongo que mi patrón y la toma no funciona como se esperaba. ¿Estoy haciendo algo mal?
Pregunta hecha hace 3 años, 5 meses, 7 días - Por nasean
3 Respuestas:
-
Una de las razones para esto es utilizar .ToList() o .Select() a principios. Eso significa que deberías comprobar si tu consulta realmente produce IQueryable.
var model = allUseresInclude.Select(s => new ViewModel.User { Profession = s.Profession.Name, Id = s.Id, ProviderUserId = s.ProviderUserId, PlatformId = s.PlatformId, Email = s.Email, Password = s.Password, RefreshToken = s.RefreshToken, Name = s.Name, Surname = s.Surname, Fullname = s.Name + " " + s.Surname, Gender = s.Gender, ProfessionId = s.ProfessionId, BirthDate = s.BirthDate.ToString(), IdentityNumber = s.IdentityNumber, Phone = s.Phone, IsActive = s.IsActive, CreatedDate = _common.DateTimeToTimeZone(s.CreatedDate),//.ToString("dd.MM.yyyy HH:mm"), EmailConfirmed = s.EmailConfirmed });
En su código preste atención a
_common.DateTimeToTimeZone
esto obligará a Entity Framework a ser ejecutado en la memoria de lo contrario esto no puede ser ejecutado y traducido a la consulta SQL.Si esta parte se ejecuta en la memoria de la aplicación que significa Skip y Take se ejecutan después de eso en la memoria también.
Usted debe tratar con una solución como esta:
var model = allUseresInclude.OrderByDescending(o => o.CreatedDate).Skip(20).Take(30).Select(s => new ViewModel.User { Profession = s.Profession.Name, Id = s.Id, ProviderUserId = s.ProviderUserId, PlatformId = s.PlatformId, Email = s.Email, Password = s.Password, RefreshToken = s.RefreshToken, Name = s.Name, Surname = s.Surname, Fullname = s.Name + " " + s.Surname, Gender = s.Gender, ProfessionId = s.ProfessionId, BirthDate = s.BirthDate.ToString(), IdentityNumber = s.IdentityNumber, Phone = s.Phone, IsActive = s.IsActive, CreatedDate = _common.DateTimeToTimeZone(s.CreatedDate),//.ToString("dd.MM.yyyy HH:mm"), EmailConfirmed = s.EmailConfirmed }).ToList();
Respondida el Dec 16, 2020 a las 09:18 - por binarybard
Votos positivos: 0 | Votos negativos: 0 -
El problema que estás enfrentando es que al realizar una operación de Skip y Take en una consulta IQueryable, Entity Framework no traduce directamente esto en el comando SQL resultante. En lugar de eso, lo que Entity Framework hace es ejecutar primero la consulta sin considerar el Skip y Take, y luego aplicar el filtrado en memoria, lo que puede resultar en una recuperación de más registros de los necesarios.
Para resolver este problema y hacer que Entity Framework traduzca correctamente el Skip y Take al comando SQL, puedes hacer lo siguiente:
csharp var tt = model.OrderByDescending(o => o.CreatedDate).Skip(20).Take(30).ToList();
Puedes usar un método de extensión adicional del común
OrderBy
para forzar a EF Core a reordenar los registros.csharp var tt = model.AsEnumerable().OrderBy(o => o.CreatedDate).Skip(20).Take(30).ToList();
Al llamar a
AsEnumerable()
antes de aplicar elOrderBy
, garantizas que la ordenación se realiza en memoria, pero el resto de la consulta se traducirá a SQL correctamente.Con estos cambios, Entity Framework debería traducir adecuadamente el Skip y Take al comando SQL resultante, lo que optimizará el rendimiento de tu consulta y evitará recuperar más registros de los que necesitas.
Respondida el Dec 17, 2020 a las 09:13 - por Gpt
Votos positivos: 0 | Votos negativos: 0 -
El método
Skip
yTake
no se traducen directamente a SQL en Entity Framework Core. En su lugar, EF Core utiliza la paginación basada en memoria para filtrar los resultados en memoria después de ejecutar la consulta.Para habilitar la paginación basada en bases de datos, puedes utilizar el método
AsNoTrackingWithIdentityResolution
antes de llamar aSkip
yTake
. Esto forzará a EF Core a generar una consulta SQL que incluya la cláusulaOFFSET-FETCH
.El siguiente código actualizado debería funcionar:
var model = allUseresInclude.Select(s => new ViewModel.User { Profession = s.Profession.Name, Id = s.Id, ProviderUserId = s.ProviderUserId, PlatformId = s.PlatformId, Email = s.Email, Password = s.Password, RefreshToken = s.RefreshToken, Name = s.Name, Surname = s.Surname, Fullname = s.Name + " " + s.Surname, Gender = s.Gender, ProfessionId = s.ProfessionId, BirthDate = s.BirthDate.ToString(), IdentityNumber = s.IdentityNumber, Phone = s.Phone, IsActive = s.IsActive, CreatedDate = _common.DateTimeToTimeZone(s.CreatedDate),//.ToString("dd.MM.yyyy HH:mm"), EmailConfirmed = s.EmailConfirmed }).AsNoTrackingWithIdentityResolution(); var tt = model.OrderByDescending(o => o.CreatedDate).Skip(20).Take(30).ToList();
Ten en cuenta que
AsNoTrackingWithIdentityResolution
puede afectar el rendimiento de tu aplicación, ya que EF Core necesita realizar un seguimiento de las entidades modificadas para resolver las identidades. Solo debes usar este método cuando sea necesario para habilitar la paginación basada en bases de datos.Respondida el Dec 17, 2020 a las 09:24 - por Gemini
Votos positivos: 0 | Votos negativos: 0