ОБНОВЛЕНИЕ:
Задержка доступа к данным игнорируется.Я регистрирую время выполнения вызова GetAllProductsAsync, и истекшее время стабильно.Проблема должна быть в другом месте.
У меня есть API, который возвращает список продуктов.API получает это от сервисов, в которых хранимая процедура SQL Server выполняется с использованием System.Data.SqlClient
, чтобы обеспечить максимальную производительность.
Выполнение хранимой процедуры выполняется менее чем за 6 мс, а ответы API - за 20 мс.средний.Если я запускаю его локально и отправляю несколько запросов GET
с помощью Почтальона (Бегун), время отклика практически одинаково для каждого запроса.
Но затем я развернул его на рабочем сервере (IIS) и выполнил тот же тест с Postman.Рабочий сервер простаивает, потому что он новый и доступ к нему осуществляется через локальную сеть, поэтому нет лишних затрат.
Полученные результаты довольно нестабильны.Шаблон в тесте 1000 запросов всегда одинаков.Один или два запроса выполняются за 20 мс, затем еще пара запросов за 500 мс.Между ними существует огромная разница.
Я запустил трассировку с помощью SQL Server Profiler, чтобы проверить, не связано ли замедление с базой данных.Но каждое выполнение в базе данных занимает одно и то же время, не более 2 мс.
Еще одна странная вещь - это то, что каждое выполнение базы данных выполняется с одним и тем же SPID.Похоже, что соединение всегда одинаковое, несмотря на то, что служба временная и соединение установлено.
Я думаю, что между запросами и соединением SQL может быть некоторая блокировка.Что это может быть?
Я также пытался извлечь данные, используя асинхронные методы, но результат был тот же.Пробовал Dapper тоже, тот же результат.
Это мой код:
Контроллер:
namespace WebApplication1.Controllers
{
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;
[Route("api/[controller]")]
[ApiController]
public class ProductController : ControllerBase
{
private readonly ProductService _productService;
public ProductController(ProductService productService)
{
_productService = productService;
}
[HttpGet]
public async Task<IActionResult> GetProducts()
{
var result = await _productService.GetAllProductsAsync();
return Ok(result);
}
}
}
Служба (методы асинхронизации и синхронизации):
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Threading.Tasks;
public class ProductService
{
private string connectionString = "xxxxx";
public IEnumerable<Product> GetAllProducts()
{
var productList = new List<Product>();
using (SqlConnection con = new SqlConnection(connectionString))
{
SqlCommand cmd = new SqlCommand("spGetAllProducts", con);
cmd.CommandType = CommandType.StoredProcedure;
con.Open();
SqlDataReader reader = cmd.ExecuteReader();
while (reader.Read())
{
productList.Add(new Product
{
Id = (int)reader["Id"],
Name = (string)reader["Name"],
From = (DateTime)reader["From"],
To = (DateTime)reader["To"]
});
}
con.Close();
}
return productList;
}
public async Task<IEnumerable<Product>> GetAllProductsAsync()
{
var productList = new List<Product>();
using (SqlConnection con = new SqlConnection(connectionString))
{
SqlCommand cmd = new SqlCommand("spGetAllEmployees", con);
cmd.CommandType = CommandType.StoredProcedure;
await con.OpenAsync();
SqlDataReader reader = await cmd.ExecuteReaderAsync();
while (await reader.ReadAsync())
{
productList.Add(new Product
{
Id = (int)reader["Id"],
Name = (string)reader["Name"],
From = (DateTime)reader["From"],
To = (DateTime)reader["To"]
});
}
con.Close();
}
return productList;
}
}
Запуск:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<ProductService>();
services.AddMvc();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
}