Опубликован: 07.05.2010 | Доступ: свободный | Студентов: 1676 / 62 | Оценка: 4.56 / 4.06 | Длительность: 34:11:00
Лекция 11:

Основы ADO.NET

Организация пула соединений

При запросе любой страницы, использующей БД, соединение будет открываться и закрываться. На это требуется определенное время и ресурсы, которое при большом количестве запросов и посетителей будут существенными. Одним из путей решения этой проблемы может быть организация пула соединений.

Пул соединений - это практика хранения приложением постоянного набора открытых соединений, разделяемых между сеансами при использовании одного и того же источника данных. Это позволяет избежать бесконечного создания и уничтожения соединений. При организации пула вызов клиентом метода Open() обслуживается непосредственно из доступного пула без повторного открытия соединения. Когда клиент освобождает соединение методом Close() или Dispose(), оно не закрывается, а возвращается в пул, чтобы обслужить следующий запрос.

Пулы автоматически организуются самими поставщиками данных только в том случае, если строки соединений в последовательных запросах полностью совпадают. Если имеется хоть небольшое отличие, создается новое подключение в новом пуле.

Пулы соединений SQL Server и Oracle используют механизм полнотекстового сравнения. Это значит, что любое минимальное изменение в строке соединений нарушает пул, даже если в ней просто изменяется порядок параметров или добавляется простой пробел. Поэтому лучше не помещать строку соединений в код установки соединения, а сохранять в файле web.config, как это мы сделали в примере.

Пулы соединений можно настраивать, добавляя соответствующие атрибуты в строку соединений. Параметры настройки пулов соединений приведены в таблице

Параметры настройки пулов в строке соединений
Параметр Описание
Max Pool Size Максимальное количество соединений, разрешенных для хранения в пуле (по умолчанию 100). Если достигается этот максимальный размер пула, любые последующие попытки соединения становятся в очередь. Если время жизни соединения, установленное параметром Connection.Timeout, истечет раньше, чем подошла очередь в пуле, возникает исключение.
Min Pool Size Минимальное количество соединений, которое должно оставаться в пуле (по умолчанию 0). Это число соединений будет создано в пуле при открытии первого соединения.
Pooling По умолчанию true. При значении false отключается механизм пула
Connection Lifetime Устанавливает время хранения соединения в пуле (в секундах). По умолчанию значение 0 устанавливает неограниченное время жизни

Некоторые поставщики имеют методы очистки пула соединений. Например, поставщик SqlConnection имеет статические методы SqlConnection.ClearPool(connectionString) - очистка пула конкретного соединения и SqlConnection.ClearAllPools() - очистка пулов всех соединений в текущем домене приложения. Эти методы не удаляют соединения физически, а помечают на удаление для бредущего сзади сборщика мусора.

Класс Command и DataReader

Класс Command позволяет выполнить любой SQL-запрос к открытому соединению базы данных. Для того, чтобы использовать команду, нужно выбрать ее тип, установить ее текст и привязать к открытому соединению. Эту работу можно выполнить, установив значения свойств CommandType, CommandText и Connection, либо передать необходимую информацию в аргументах конструктора класса Command.

Текстом команды может быть SQL-оператор, хранимая процедура ( stored procedure ) или имя таблицы. Это определяется значением перечисления CommandType, присваиваемого свойству Command.CommandType.

Значения перечисления CommandType
Значение Описание
System.Data.CommandType.Text Требование выполнить SQL-оператор, указанный в свойстве Command.CommandText. Установлено по умолчанию в свойстве класса Command.CommandType
System.Data.CommandType.StoredProcedure Требование выполнить хранимую процедуру, указанную в свойстве Command.CommandText
System.Data.CommandType.TableDirect Требование извлечь все записи из таблицы, указанной в свойстве Command.CommandText.

Не поддерживается поставщиком данных SQL Server

Расширим наш пример, чтобы применить код создания объекта Command.

  • Создайте из копии файла TestConnection1.aspx файл с именем TestConCommand.aspx и установите его стартовым
  • Дополните код страницы TestConCommand.aspx так
    <%@ Page Language="C#" %>
        
    <script runat="server">
        
        protected void Page_Load(object sender, EventArgs e)
        {
            // Извлекли строку соединения из web.config
            string connectString = System.Web.Configuration.WebConfigurationManager.
                ConnectionStrings["Northwind"].ConnectionString;
            
            // Установили минимальный размер пула соединения равный 10
            connectString += "Min Pool Size=10;";
            
            // Создали объект соединения
            System.Data.SqlClient.SqlConnection con =
                new System.Data.SqlClient.SqlConnection(connectString);
        
            using (con)
            {
                // Получить соединение из пула (если есть)
                // или создать пул с 10 соединениями (если нет)
                con.Open();
                lblInfo.Text = "<b>Соединение:</b> " + con.State.ToString();
                
                // Создали объект Command
                System.Data.SqlClient.SqlCommand cmd = 
                    new System.Data.SqlClient.SqlCommand();
                
                // Настроили объект Command
                cmd.Connection = con;
                cmd.CommandType = System.Data.CommandType.Text;
                cmd.CommandText = "SELECT * FROM Employees";
            }
        
            // Вернуть соединение в пул
            lblInfo.Text += "<br /><b>Соединение:</b> ";
            lblInfo.Text += con.State.ToString();
        }
    </script>
        
    <html xmlns="http://www.w3.org/1999/xhtml" >
    <head runat="server">
        <title>Untitled Page</title>
    </head>
    <body>
        <form id="form1" runat="server">
        <div>
            <asp:Label ID="lblInfo" runat="server" Text="Label"></asp:Label></div>
        </form>
    </body>
    </html>
  • Исполните страницу, чтобы получить такой результат

Учитывая, что по умолчанию установлено CommandType = CommandType.Text, команду можно сразу указать в перегруженном конструкторе при создании объекта команд. Например, код

// Создали объект Command
System.Data.SqlClient.SqlCommand cmd = 
    new System.Data.SqlClient.SqlCommand();

// Настроили объект Command
cmd.Connection = con;
cmd.CommandType = System.Data.CommandType.Text;
cmd.CommandText = "SELECT * FROM Employees";

можно заменить кодом

// Создали и настроили объект Command
System.Data.SqlClient.SqlCommand cmd = 
    new System.Data.SqlClient.SqlCommand("SELECT * FROM Employees", con);

В данном примере мы просто настроили, но не выполнили объект класса Command, поэтому никакой выборки данных из БД не получили. Объект Command имеет три метода выполнения команды, приведенные в таблице на примере класса SqlCommand

Методы выполнения объекта Command
Метод Описание
System.Data.SqlClient.SqlCommand.ExecuteNonQuery() Выполняет команды, отличные от SELECT, такие как SQL-операторы вставки, удаления или обновления записей. Возвращает количество строк, обработанное командой
System.Data.SqlClient.SqlCommand.ExecuteScalar() Выполняет запрос SELECT и возвращает первое поле первой строки результирующего набора, сгенерированного командой. Обычно применяется в запросах с функциями SUM() или COUNT() для вычисления единственного значения
System.Data.SqlClient.SqlCommand.ExecuteReader() Выполняет запрос выборки данных SELECT и возвращает объект DataReader - результирующий набор данных, доступный только для чтения

Метод SqlCommand.ExecuteReader()

Объект класса DataReader представляет собой результирующий набор данных только для чтения и является самым простым и быстрым средством доступа к данным. Он является выборкой данных по запросу SQL-команды SELECT и состоит из строк и колонок, которые можно анализировать и отображать. Но в нем нет возможностей сортировки, как в более развитых объектах. Ниже приведены несколько методов DataReader на примере класса System.Data.SqlClient.SqlDataReader

Методы класса DataReader
Метод Описание
SqlDataReader.Read() Перемещает курсор строки на следующую строку в потоке. Его нужно также вызывать первым после создания объекта DataReader, поскольку начальный курсор позиционируется перед первой строкой. Возвращает false при чтении последней строки набора данных
SqlDataReader.GetValue(int) Читает значение поля текущей строки по указанному индексу (индекс начинается с нуля)
SqlDataReader.GetValues(object[ ]) Сохраняет значение текущей строки в массиве. Количество сохраняемых полей зависит от размера массива, переданного этому методу. Чтобы сохранить все поля текущей строки, нужно создать массив размерности, определяемой свойством SqlDataReader.FieldCount
SqlDataReader.GetInt32(int) Возвращает значение поля целого типа по индексу столбца
SqlDataReader.GetDateTime(int) Возвращает значение поля типа даты по индексу столбца
SqlDataReader.NextResult() Если команда, которая сгенерировала DataReader, возвратила более одного результирующего набора строк, то этот метод перемещает текущий курсор данных на первую строку следующего набора
SqlDataReader.Close() Закрывает модуль чтения

Обработка одного результирующего набора данных SqlDataReader

Приведем пример, в котором создается и выполняется простая команда запроса для выборки всех записей из таблицы Employees учебной базы данных Northwind. Мы помним, что строка соединения была нами задана ранее в конфигурационном файле.

  • Создайте копию страницы TestConCommand.aspx с именем TestDataReader.aspx
  • Заполните страницу приведенным кодом
    <%@ Page Language="C#" %>
        
    <script runat="server">
        
        protected void Page_Load(object sender, EventArgs e)
        {
            // Извлекли строку соединения из web.config
            string connectString = System.Web.Configuration.WebConfigurationManager.
                ConnectionStrings["Northwind"].ConnectionString;
        
            // Создали объект соединения
            System.Data.SqlClient.SqlConnection con =
                new System.Data.SqlClient.SqlConnection(connectString);
        
            // Создали объект Command
            System.Data.SqlClient.SqlCommand cmd =
                new System.Data.SqlClient.SqlCommand();
        
            // Настроили объект Command
            cmd.Connection = con;
            cmd.CommandType = System.Data.CommandType.Text;
            cmd.CommandText = "SELECT * FROM Employees";
        
            // Создаем ссылку на вспомогательную строку
            System.Text.StringBuilder htmlStr = new StringBuilder("");
            
            using (con)
            {
                // Открыли соединение
                con.Open();
        
                // Выполняем команду и получаем результирующий набор данных
                System.Data.SqlClient.SqlDataReader reader =
                    cmd.ExecuteReader();
                
                // Перебираем все записи результирующего набора
                // и строим HTML-строку
                while (reader.Read())
                {
                    htmlStr.Append("<li>Служащий ");
                    htmlStr.Append(reader["TitleOfCourtesy"]);
                    htmlStr.Append(" <b>");
                    htmlStr.Append(reader.GetString(1));
                    htmlStr.Append("</b>, ");
                    htmlStr.Append(reader.GetString(2));
                    htmlStr.Append(" - работает с ");
                    htmlStr.Append(reader.GetDateTime(6).ToString("d"));
                    htmlStr.Append("</li>");
                }
                
                // Закрываем набор данных
                reader.Close();
            }
        
            // Выдаем результаты запроса
            lblInfo.Text = htmlStr.ToString();
        }
    </script>
        
    <html xmlns="http://www.w3.org/1999/xhtml" >
    <head runat="server">
        <title>Untitled Page</title>
    </head>
    <body>
        <form id="form1" runat="server">
        <div>
            <asp:Label ID="lblInfo" runat="server" Text="Label"></asp:Label></div>
        </form>
    </body>
    </html>
  • Исполните страницу TestDataReader.aspx, должен получиться такой клиентский результат
  • Служащий Ms. Davolio, Nancy - работает с 01.05.1992
  • Служащий Dr. Fuller, Andrew - работает с 14.08.1992
  • Служащий Ms. Leverling, Janet - работает с 01.04.1992
  • Служащий Mrs. Peacock, Margaret - работает с 03.05.1993
  • Служащий Mr. Buchanan, Steven - работает с 17.10.1993
  • Служащий Mr. Suyama, Michael - работает с 17.10.1993
  • Служащий Mr. King, Robert - работает с 02.01.1994
  • Служащий Ms. Callahan, Laura - работает с 05.03.1994
  • Служащий Ms. Dodsworth, Anne - работает с 15.11.1994

В этом примере объект класса StringBuilder существенно увеличивает производительность за счет выделения буфера памяти для символов. Если использовать объект класса String, то при каждой операции сложения строк будет создаваться новый объект с копированием результатов из старого объекта, что значительно медленнее.

В большинстве случаев нам такой способ выборки информации из базы в будущем не понадобится, поскольку существуют библиотечные элементы управления для визуализации и работы с данными.