Закрытие соединения в методе «выгрузить» - PullRequest
5 голосов
/ 11 июля 2011

Я унаследовал веб-фреймворк, посредством которого предыдущий разработчик открывал и закрывал соединения с базой данных в методах init / unload жизненного цикла страницы.По сути, конструктор такой (упрощенный, чтобы продемонстрировать смысл);

public class BasePage
{
   protected DBConnection _conn;

   public BasePage()
   {
      Init += StartConnection;
      Unload += EndConnection;
   }

   private void StartConnection(object sender, EventArgs e)
   {
      _conn = new DBConnection(Application["connectionstring"].ToString());   
   }

   private void EndConnection(object sender, EventArgs e)
   {
      if (_conn == null)
         return;

      if (_conn.Connection.State == ConnectionState.Open)
      {
     _conn.Close();
         _conn.Dispose();
      }
   }
}

С тех пор, как я попал сюда, разработка была довольно быстрой, поэтому я никогда не останавливался, чтобы рассмотреть ее.Недавно посещения были завершены, и мы начали получать страшную ошибку «Истекло время ожидания. Период ожидания истек до получения соединения из пула ...».

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

Могу ли я рассчитывать на то, что метод unload вызывается ВСЕГДА, даже в случае исключения?Или кто-то может увидеть какие-либо другие потенциальные проблемы, используя приведенный выше шаблон, который сделает его основным подозреваемым для этих утечек соединения?метод unload всегда вызывается, даже если есть исключение.Мне просто нужно знать о любых сценариях, в которых этот метод не будет вызываться, поэтому я могу выяснить, нужен ли мне этот бит для рефакторинга в первую очередь.

РЕДАКТИРОВАТЬ: Спасибо тем, кто уже ответил, нопожалуйста, больше никаких рекомендаций по поводу класса IDisposable или шаблонов "using" или "catch / finally" - это был не мой вопрос!Мой вопрос, в частности, может ли страница когда-либо запускать событие «Init», но затем не запускаться - это событие «Unload», и почему это может произойти.

Ответы [ 5 ]

2 голосов
/ 11 июля 2011

У меня нет точных сведений о том, безопасно ли это, но я просмотрел исходный код класса System.Web.UI.Page, и событие unload вызывается частной процедурой ProcessRequestCleanup (), если запрос не является асинхронным или межстраничный запрос. Вызов метода очистки находится внутри блока finally, связанного с блоком try, окружающим ProcessRequest. Запрос процесса вызывает все события жизненного цикла страницы от PreInit до Render. Это будет означать, что выгрузка всегда будет запускаться (за исключением случаев асинхронности и кросс-страницы), даже если возникает исключение.

Однако мне было бы очень неловко иметь этот код на моих страницах, поскольку поведение unload точно не задокументировано.

2 голосов
/ 11 июля 2011

Я всегда использую блочные средства, как показано ниже

using( SqlConnection)
{

}

так, чтобы это никогда не вызывало проблем

если вы не хотите писать код для открытия соединения снова и снова, создайте один класс

public class SqlConnectionManager
{
    public SqlConnection GetSqlConnectionManager()
    {
       //create and return connection
       //SqlConnection con = new SqlConnection();
       //return con;
     }

}

In You файлы классов

SqlConnection conn = null;
using (conn = (new SqlConnectionManager()).GetSqlConnectionManager())
{
     //do work with connection
}

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

1 голос
/ 11 июля 2011

(очень) быстрый тест (веб-сервер VS2010, .net v4) показал, что событие Unload вызывается , когда вызывается необработанное исключение (по крайней мере, если оно вызывается в Page_Load), поэтому принцип выглядит так, как будто он должен работать.

Шаблон, приведенный в примере, только dispose ', если соединение было открыто.

Поскольку _conn защищено, страницы, происходящие от BasePage, могут взаимодействовать с _conn и изменять его значение. У классов-потомков есть два способа сломать шаблон:

  1. Позвоните _conn.Close() напрямую. Если соединение не открыто, оно не удаляется в EndConnection.

  2. Измените значение _conn, задав для него значение null или присвоив ему новый экземпляр DBConnection.

Попробуйте изменить метод EndConnection, чтобы _conn был всегда расположен.

private void EndConnection(object sender, EventArgs e)
{
    if (_conn == null)
    {
       return;
    }
    if (_conn.Connection.State == ConnectionState.Open)
    {
         _conn.Close();
    }
    _conn.Dispose(); // always dispose even if not actually open. It may have been closed explicitly elsewhere.
}

Случай 2 не может быть обнаружен EndConnection. Подумайте о том, чтобы сделать _conn частным и предоставить свойство геттера:

private DBConnection _conn;

protected DBConnection Connection {
     get 
     {
         return _conn;
     }
}

, который запрещает классам-потомкам изменять значение _conn.

Наконец, DBConnection - это ваш собственный класс? Я только спрашиваю, когда вы цитируете "_conn.Connection.State", а не просто _conn.State. Если это так, просто дважды проверьте правильность расположения метода DBConnection для своего экземпляра Connection.

1 голос
/ 11 июля 2011

РЕДАКТИРОВАТЬ: Согласно ответу Фейберга это определенно возможно в .net 4, и можно предположить, что выгрузка всегда будет вызываться.

Я также проверил код .net 2.0, и там тоже самое.

0 голосов
/ 11 июля 2011

Нет. Возьмите следующий пример.

Что произойдет, если пользователь закроет браузер? Функция Unload не будет вызываться, и у вас будет открытое соединение с базой данных.

Этот вопрос Unload в StackOverflow имеет похожую проблему, с которой вы столкнулись.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...