Я создал следующий пример с использованием ASP.NET MVC 5, и я проверил вкладку «SQL Profiler» и «Сеть» в инструментах разработчика, и я могу подтвердить, что она работает хорошо, и сервер базы данных также получает отмену и отменяет выполнение запроса.
Обратите внимание, что те же решения будут работать и с EF, но, поскольку в посте использовался DataTable
, я также написал пример, используя DataTable
.
ASP.NET MVC 5 - Пример отмены
В следующем примере я создал простую страницу индекса, содержащую текстовое поле. Когда вы набираете TextBox
, он ждет 500 миллисекунд, чтобы определить, прекратили ли вы печатать. Затем, после обнаружения прекращения набора текста, он отправляет ajax-запрос для поиска.
Если вы снова начнете печатать (или у вас более 500 мс) между нажатиями клавиш, запрос будет отменен, а выполнение запроса также будет отменено на уровне базы данных. Вы можете увидеть это, используя профилировщик.
Table1Business.cs
Полагаю, у вас есть база данных, содержащая Table1
, в которой есть Id
и Name
столбцы. Поэтому я создаю класс бизнес-логики для поиска между записями Table1
, сделав небольшую задержку в запросе.
Примечание. Задержка указана, например, для имитации длительного выполнения запроса.
В следующем коде соединение было открыто асинхронно, считыватель выполнил асинхронно, а также как считыватель считан асинхронно:
using System.Data;
using System.Data.SqlClient;
using System.Threading;
using System.Threading.Tasks;
public class Table1Business
{
public async Task<DataTable> Search(string name,
CancellationToken cancellationToken = default(CancellationToken))
{
using (var connection = new SqlConnection(@"Your connection string"))
using (var command = new SqlCommand("WAITFOR DELAY '00:00:10'; " +
"SELECT TOP 10 [Id], [Name] " +
"FROM [Table1] WHERE [Name] LIKE '%' + @Name + '%'", connection))
{
var table = new DataTable();
table.Columns.Add("Id", typeof(int));
table.Columns.Add("Name", typeof(string));
command.Parameters.Add("@Name", SqlDbType.NVarChar, 50).Value = name;
await connection.OpenAsync(cancellationToken);
var reader = await command.ExecuteReaderAsync(cancellationToken);
while (await reader.ReadAsync(cancellationToken))
{
object[] values = new object[2];
reader.GetValues(values);
table.Rows.Add(values);
}
return table;
}
}
}
HomeController.cs
Следующий контроллер имеет простое действие Index
для возврата к просмотру Index
и действие Search
, которое отвечает за выполнение поиска и получение импульса отмены. В случае отмены выполнения будет отменено даже в базе данных. В этом сообщении вы можете найти больше информации об отмене.
using CancellationExample.Models;
using System.Data;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Mvc;
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
public async Task<ActionResult> Search(string name, CancellationToken cancellationToken)
{
CancellationToken disconnectedToken = Response.ClientDisconnectedToken;
var source = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken,
disconnectedToken);
DataTable dt = null;
var business = new Table1Business();
dt = await business.Search(name, source.Token);
return PartialView(dt.AsEnumerable().Select(x => x.Field<string>("Name")));
}
}
Index.cshtml
Способ обнаружения конца набора текста заимствован из этой записи . Это просто, например, вы можете использовать любое другое решение в зависимости от ваших предпочтений. Лично я предпочитаю полагаться на ввод, а не пытаться обнаружить изменение ввода.
В любом случае, в следующем коде мы проверяем, не является ли xhr
нулевым, это означает, что выполняется другой запрос ajax, и затем мы прерываем его:
@{
Layout = null;
}
<div>
<form action="/home/search" method="get" id="form">
<input type="text" name="name" id="name" />
</form>
<div id="result"></div>
</div>
<script src="~/Scripts/jquery-3.3.1.js"></script>
<script>
$(function () {
var xhr = null;
var timeout = null;
$("#name").keyup(function () {
clearTimeout(timeout);
if (xhr != null)
xhr.abort();
timeout = setTimeout(function () {
xhr = $.get("/home/search?name=" + $("#name").val(), function (data) {
$("#result").html(data);
});
}, 500);
});
});
</script>
Search.cshtml
@model IEnumerable<string>
<ul>
@foreach (var item in Model)
{
<li>@item</li>
}
</ul>