Aquí, finalmente, agregaré proyectos para utilizar procedimientos almacenados en lugar de sentencias construidas en el código.
De paso, y por recomendación de un mayor experto que yo en Entity Framework, agregué «AsNoTracking()» a la consulta LINQ de Entity Framework establecida en Generando una API REST (JSON) [2].
El procedimiento Almacenado.
Este es el procedimiento almacenado que recibe, el Id de país, la fecha desde y la fecha hasta, y la página a mostrar.
Es el procedimiento almacenado el responsable de establecer valores adecuados a los parámetros de fecha, en lugar de establecerlos desde el componente en C#.
CREATE PROCEDURE [dbo].[Owid Covid Data Get By Country] @fromDate SMALLDATETIME NULL , @toDate SMALLDATETIME NULL , @CountryId INT , @Page INT = 1 AS BEGIN SELECT @froMDate = ISNULL(@fromDate, ( SELECT MIN([o].[date]) FROM [Owid Covid Data] AS [o] )); SELECT @toDate = ISNULL(@toDate, ( SELECT MAX([o].[date]) FROM [Owid Covid Data] AS [o] )); DECLARE @Skip INT = ((@Page - 1) * 100); BEGIN SELECT [o].[ContinentId] , [o].[CountriesId] , [o].[date] , [o].[hosp_patients] , [o].[hosp_patients_per_million] , [o].[icu_patients] , [o].[icu_patients_per_million] , [o].[new_cases] , [o].[new_cases_per_million] , [o].[new_cases_smoothed] , [o].[new_cases_smoothed_per_million] , [o].[new_deaths] , [o].[new_deaths_per_million] , [o].[new_deaths_smoothed] , [o].[new_deaths_smoothed_per_million] , [o].[new_tests] , [o].[new_tests_per_thousand] , [o].[new_tests_smoothed] , [o].[new_tests_smoothed_per_thousand] , [o].[new_vaccinations] , [o].[new_vaccinations_smoothed] , [o].[new_vaccinations_smoothed_per_million] , [o].[people_fully_vaccinated] , [o].[people_fully_vaccinated_per_hundred] , [o].[people_vaccinated] , [o].[people_vaccinated_per_hundred] , [o].[positive_rate] , [o].[tests_per_case] , [o].[tests_units] , [o].[total_cases] , [o].[total_cases_per_million] , [o].[total_deaths] , [o].[total_deaths_per_million] , [o].[total_tests] , [o].[total_tests_per_thousand] , [o].[total_vaccinations] , [o].[total_vaccinations_per_hundred] , [o].[weekly_hosp_admissions] , [o].[weekly_hosp_admissions_per_million] , [o].[weekly_icu_admissions] , [o].[weekly_icu_admissions_per_million] FROM [Owid Covid Data] AS [o] WHERE [o].[date] >= @fromDate AND [o].[date] <= @toDate AND [o].[CountriesId] = @CountryId ORDER BY date OFFSET @skip ROWS FETCH NEXT 100 ROWS ONLY; END; END;
Exactamente igual, pero con «FOR JSON PATH» al final, se usa en el proyecto que utiliza código puro.
El Cambio en Entity Framework
Basado en la propuesta y comentario, el código queda como sigue:
public IEnumerable<OwidCovidDatum> GetCountryData( int CountryId, DateTime? fromDate=null, DateTime? toDate=null, int Page=1) { fromDate=fromDate?? (from el in _context.OwidCovidData orderby el.Date select el.Date).FirstOrDefault(); toDate=toDate?? (from el in _context.OwidCovidData orderby el.Date descending select el.Date).FirstOrDefault(); return (from OwidCovidDatum el in _context.OwidCovidData .Where(x=> x.Date>=fromDate && x.Date<=toDate && x.CountriesId==CountryId) .Skip((Page-1)*100) .Take(100) select el).AsNoTracking().ToList(); }
Dapper Usando Procedimientos Almacenados
Utilizamos la capacidad de ejecución de procedimientos almacenados de Dapper, que es capaz de asignar valores a los parámetros por coincidencia de nombres.
public async Task<string> GetCountryData( int CountryId, DateTime? fromDate = null, DateTime? toDate = null, int Page = 1) { var result =await _dal.GetDataAsync("[Owid Covid Data Get By Country]",new { fromDate, toDate, CountryId, Page }); string json = JsonSerializer.Serialize(result, new JsonSerializerOptions() { WriteIndented = true, ReferenceHandler = ReferenceHandler.Preserve }); return json; }
Código usando Procedimientos Almacenados
En el caso del código directo, asignamos los parámetros uno a uno, especificando además el tipo de dato, que permite una mayor especificidad.
public async Task<string> GetCountryData( int CountryId, DateTime? fromDate = null, DateTime? toDate = null, int Page = 1) { var command = _dal.CreateCommand("[Owid Covid Data Get By Country JSON]"); command.Parameters.Add("@fromDate", System.Data.SqlDbType.SmallDateTime).Value = fromDate; command.Parameters.Add("@toDate", System.Data.SqlDbType.SmallDateTime).Value = toDate; command.Parameters.Add("@CountryId", System.Data.SqlDbType.Int).Value = CountryId; command.Parameters.Add("@skip", System.Data.SqlDbType.Int).Value = Page; var json =await _dal.GetJSONDataAsync(command); return json; }
Rendimiento
El gráfico muestra que, aún cuando se utilizan características que mejoran la efectividad, la simpleza del código mejora el rendimiento.
O sea, para mejor respuesta al usuario, se deberá invertir más tiempo de los desarrolladores en mejorar su desarrollo.
Como detalle, las llamadas de procedimiento almacenado, realizan directamente llamadas al mismo, en lugar de utilizar, como vimos en la publicación anterior, sp_executesql.
EXEC [Owid Covid Data Get By Country] @fromDate = NULL , @toDate = NULL , @CountryId = 4 , @Page = 3;
