асинхронная страница ASP.NET

Асинхронные страницы нужно применять там, где может произойти задержка в выполнении запроса. Обычно это происходит при обращении к удаленному веб серверу или к удаленной базе данных. Время может колебаться от доли секунды до целой вечности и все это время основой поток ASP.NET будет занят, а сайт будет висеть.

начальная страница

Скрыть

Показать

Копировать
  Default.aspx  
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>
 
<!DOCTYPE html>
 
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
 <meta charset="utf-8" />
 <title></title>
 <style>
  h1 {
   text-align: center;
  }
  a {
   font-size: 22px;
  }
 </style>
</head>
<body>
 <form id="form1" runat="server">
  <div>
   <h1>доступ к удаленной базе данных</h1>
   <br />
   <br />
   <a href="Page1.aspx" target="_blank">обычная страница</a>
   <br />
   <br />
   <a href="Page2.aspx" target="_blank">асинхронная страница</a>
   <br />
   <br />
   <a href="Page3.aspx" target="_blank">асинхронная страница с использование асинхронных задач класса PageAcyncTask</a>
  </div>
 </form>
</body>
</html>

обычная страница

Скрыть

Показать

Копировать
  Default.aspx  
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Page1.aspx.cs" Inherits="Page1" %>
 
<!DOCTYPE html>
 
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
 <meta charset="utf-8" />
 <title></title>
</head>
<body>
 <form id="form1" runat="server">
  <div>
   <asp:Label ID="Label1" runat="server" Text=""></asp:Label>
  </div>
 </form>
</body>
</html>
Скрыть

Показать

Копировать
  Default.aspx.cs  
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
 
//добавить ссылку
using MySql.Data.MySqlClient;
using System.Configuration;
 
public partial class Page1 : System.Web.UI.Page {
 protected void Page_Load(object sender, EventArgs e) {
  string connectionString = ConfigurationManager.ConnectionStrings["abc"].ConnectionString;
  string sql = "SELECT id, name, amount, price FROM Book";
  using(MySqlConnection connection = new MySqlConnection(connectionString)) {
   try {
    connection.Open();
    MySqlCommand cmd = new MySqlCommand(sql, connection);
    MySqlDataReader reader = cmd.ExecuteReader();
    while(reader.Read()) {
     Label1.Text  += reader[0].ToString() + " " + reader[1].ToString() + " " + reader[2].ToString() + " " + reader[3].ToString() + "<br />";
    }
   }
   catch {
    Label1.Text = "Error!";
   }
  }
 }
}

асинхронная страница

Для создания асинхронной страницы в директиве Page нужно указать атрибут Async="true"
Далее, нужно зарегистрировать методы начала и конца асинхронной операции в любом обработчике событий, которые следуют до обработчика события PreRender.
Скрыть

Показать

Копировать
  Default.aspx  
<%@ Page Language="C#" Async="true" AutoEventWireup="true" CodeFile="Page2.aspx.cs" Inherits="Page2" %>
 
<!DOCTYPE html>
 
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
 <title></title>
</head>
<body>
 <form id="form1" runat="server">
  <div>
   <asp:Label ID="Label1" runat="server" Text=""></asp:Label>
  </div>
 </form>
</body>
</html>
Скрыть

Показать

Копировать
  Default.aspx.cs  
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
 
//добавить ссылку
using MySql.Data.MySqlClient;
using System.Configuration;
using System.Net;
 
public partial class Page2 : System.Web.UI.Page {
 /*будет выполнять запрос к удаленному ресурсу*/
 private WebRequest request = null;
 
 protected void Page_Load(object sender, EventArgs e) {
  /*регистрация методов начала и конца асинхронной операции*/
  AddOnPreRenderCompleteAsync(new BeginEventHandler(BeginAsyncOperation), new EndEventHandler(EndAsyncOperation));
 }
 
 /*начало асинхронной операции*/
 //AsyncCallback cb - метод будет запускаться по звершению асинхронной задачи
 //object extraData - дополнительные данные состояния
 private IAsyncResult BeginAsyncOperation(object sender, EventArgs e, AsyncCallback cb, object extraData) {
  //запрос к удаленному MySql серверу
  request = WebRequest.Create("http://ros6.jetget.ru");
  //асинхронный запрос к удаленному ресурсу + 
  //создается новый поток + 
  //этот поток возвращается главному пулу ASP.NET
  return request.BeginGetResponse(cb, null);
 }
 
 /*конец асинхронной операции*/
 private void EndAsyncOperation(IAsyncResult ar) {
  using(WebResponse responce = request.EndGetResponse(ar)) {
   string connectionString = ConfigurationManager.ConnectionStrings["abc"].ConnectionString;
   string sql = "SELECT id, name, amount, price FROM Book";
   using(MySqlConnection connection = new MySqlConnection(connectionString)) {
    try {
     connection.Open();
     MySqlCommand cmd = new MySqlCommand(sql, connection);
     MySqlDataReader reader = cmd.ExecuteReader();
     while(reader.Read()) {
      Label1.Text  += reader[0].ToString() + " " + reader[1].ToString() + " " + reader[2].ToString() + " " + reader[3].ToString() + "<br />";
     }
    }
    catch {
     Label1.Text = "Error!";
    }
   }
  }
 }
}

асинхронная страница с использование асинхронных задач класса PageAcyncTask

Для создания асинхронной страницы в директиве Page нужно указать атрибут Async="true"
Для создания асинхронной страницы, которая использует класс PageAcyncTask нужно в файле Web.config установить данные настройки
  • <!--обязательно установить данную настройку для Framework 4 и 4.5-->
  • <appSettings>
  •  <add key="aspnet:UseTaskFriendlySynchronizationContext" value="false" />
  • </appSettings>
Скрыть

Показать

Копировать
  Default.aspx  
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Page3.aspx.cs" Inherits="Page3" %>
 
<!DOCTYPE html>
 
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
 <meta charset="utf-8" />
 <title></title>
</head>
<body>
 <form id="form1" runat="server">
  <div>
   <asp:Label ID="Label1" runat="server" Text=""></asp:Label>
  </div>
 </form>
</body>
</html>
Скрыть

Показать

Копировать
  Default.aspx.cs  
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
 
//добавить ссылку
using MySql.Data.MySqlClient;
using System.Configuration;
using System.Net;
 
public partial class Page3 : System.Web.UI.Page {
 /*будет выполнять запрос к удаленному ресурсу*/
 private WebRequest request = null;
 
 protected void Page_Load(object sender, EventArgs e) {
  /*создаем экземпляр класса PageAsyncTask*/
  //делегат OnBegin - начало асинхронной операции
  //делегат OnEnd - конец асинхронной операции
  //делегат OnTimeOut - метод возвратит сообщение, если лимит времени
  //ожидания (AsyncTimeout="40" по умолчанию) превышен
  //null - значение для аргумента extraData метода OnBegin
  PageAsyncTask task = new PageAsyncTask(OnBegin, OnEnd, OnTimeOut, null);
  /*регистрация*/
  RegisterAsyncTask(task);
 }
 
 /*начало асинхронной операции*/
 /*начало асинхронной операции*/
 //AsyncCallback cb - метод будет запускаться по звершению асинхронной задачи
 //object extraData - дополнительные данные состояния
 private IAsyncResult OnBegin(object sender, EventArgs e, AsyncCallback cb, object extraData) {
  //запрос к удаленному MySql серверу
  request = WebRequest.Create("http://ros6.jetget.ru");
  //асинхронный запрос к удаленному ресурсу + 
  //создается новый поток + 
  //этот поток возвращается главному пулу ASP.NET
  return request.BeginGetResponse(cb, null);
 }
 
 /*конец асинхронной операции*/
 /*конец асинхронной операции*/
 private void OnEnd(IAsyncResult ar) {
  using(WebResponse responce = request.EndGetResponse(ar)) {
   string connectionString = ConfigurationManager.ConnectionStrings["abc"].ConnectionString;
   string sql = "SELECT id, name, amount, price FROM Book";
   using(MySqlConnection connection = new MySqlConnection(connectionString)) {
    try {
     connection.Open();
     MySqlCommand cmd = new MySqlCommand(sql, connection);
     MySqlDataReader reader = cmd.ExecuteReader();
     while(reader.Read()) {
      Label1.Text  += reader[0].ToString() + " " + reader[1].ToString() + " " + reader[2].ToString() + " " + reader[3].ToString() + "<br />";
     }
    }
    catch {
     Label1.Text = "Error!";
    }
   }
  }
 }
 
 /*если лимит времени превышен*/
 private void OnTimeOut(IAsyncResult ar) {
  Label1.Text = "Время ожидания gревышено!";
 }
}
Скрыть

Показать

Копировать
  Web.config  
<?xml version="1.0"?>
<configuration>
  <system.web>
    <compilation debug="true" targetFramework="4.5">
      <assemblies>
        <add assembly="MySql.Data, Version=6.9.5.0, Culture=neutral, PublicKeyToken=C5687FC88969C44D"/>
      </assemblies>
    </compilation>
    <httpRuntime targetFramework="4.5"/>
  </system.web>
 <connectionStrings>
  <add name="abc" connectionString="Server=ros6.beget.ru;Database=zzz;User ID=tipliz;Password=12345"/>
 </connectionStrings>
<!--обязательно установить данную настройку для Framework 4 и 4.5-->
 <appSettings>
  <add key="aspnet:UseTaskFriendlySynchronizationContext" value="false" />
 </appSettings>
</configuration>

нагрузочный тест

Обратите внимание на данные нагрузочного теста, обычная страница Page1практически в два раза загружается дольше, чем асинхронные страницы.