Когда вызывается DbConnection.StateChange?

У меня есть следующий код:

   class Program
{
    static void Main()
    {
        var connection = new SqlConnection("myConnectionString");
        connection.Open();
        connection.StateChange += HandleSqlConnectionDrop;
        Console.WriteLine("Hi");
        Console.ReadLine();
    }

    private static void HandleSqlConnectionDrop(object connection, StateChangeEventArgs args)
    {
        Console.WriteLine("DB change detected");
    }
}

Я запускаю приведенный выше код во время работы экземпляра SQL-сервера. Затем я приступаю к выполнению

SHUTDOWN WITH NOWAIT;

на экземпляре сервера sql, к которому подключена программа. Затем я наблюдаю, как служба SQL-сервера останавливается. Однако я никогда не вижу в выводе сообщения «Обнаружено изменение БД». Почему это?

Кроме того: я увижу вызов обработчика StateChange, если затем попытаюсь выполнить операцию над соединением SQL, но никогда раньше. Можно ли как-то изменить это поведение?


person Jacob Horbulyk    schedule 25.05.2016    source источник
comment
Я увижу, что обработчик StateChange вызывается, если я затем попытаюсь выполнить операцию с SQL-соединением. Фактически вы отвечаете на свой вопрос. Объект подключения не пингует сервер и проверяет состояние только при необходимости. Поместите connection.Open() после .StateChange+=..., чтобы убедиться, что это работает.   -  person Alex Kudryashev    schedule 25.05.2016
comment
@AlexKudryashev Разве не должно быть какой-то поддержки между клиентом SQL и сервером SQL? Насколько я понимаю, объекты SqlConnection должны сопоставляться один к одному с сеансами SQL-сервера. Если сервер убивает сеанс, есть ли способ передать эту информацию в мой код?   -  person Jacob Horbulyk    schedule 25.05.2016
comment
вы можете попробовать опросить сервер чем-то безобидным, например select 1   -  person alex.b    schedule 27.05.2016
comment
Обычно он вызывается, когда вы пытаетесь отправить команду sql на ваш сервер, а соединение разрывается или время ожидания истекло. Поскольку SqlConnection не поддерживает сервер в активном состоянии, вы можете узнать, что это состояние изменилось, только когда вы попытаетесь выполнить команду, используя это соединение.   -  person v.chjen    schedule 02.06.2016
comment
@v.chjen Есть ли какая-нибудь официальная документация, на которую вы можете мне указать, которая поддерживает идею о том, что SqlConnection не поддерживает работу сервера?   -  person Jacob Horbulyk    schedule 02.06.2016


Ответы (2)


Когда вызывается DbConnection.StateChange?

Вы можете узнать это, просмотрев справочный исходный код Microsoft.

Событие StateChange вызывается DbConnection.OnStateChange. Поиск ссылок на эту функцию дает только несколько экземпляров:

Во-первых, в классе SqlConnection OnStateChange вызывается только в Close< /а> метод.

Затем в файле DbConnectionHelper.cs есть разделяемый класс с именем DBCONNECTIONOBJECT. Похоже, что он используется для всех классов, производных от DbConnection, с использованием некоторых махинаций во время сборки. Так что вы можете считать его частью SqlConnection. В любом случае он вызывает OnStateChange только из функции SetInnerConnectionEvent. .

Насколько я могу судить (бессмыслица частичного класса усложняет задачу), SqlConnection.SetInnerConnectionEvent вызывается только из SqlConnectionFactory.SetInnerConnectionEvent. И это вызывается из:

Итак, в итоге - событие возникает только в ответ на действия на стороне клиента - похоже, не происходит никакого опроса состояния соединения, встроенного в SQLConnection.

Можно ли как-то изменить это поведение?

Глядя на исходный код, я не вижу ни одного. Как предлагали другие, вы, конечно, можете реализовать свой собственный опрос.

person Blorgbeard    schedule 31.05.2016

Событие StateChange предназначено для состояния соединения, а не для экземпляра сервера базы данных. Чтобы получить состояние сервера базы данных,

Событие StateChange возникает, когда состояние события меняется с закрытого на открытое или с открытого на закрытое.

Из MSDN: https://msdn.microsoft.com/en-us/library/system.data.common.dbconnection.statechange(v=vs.110).aspx

Если вы собираетесь развернуть свой собственный монитор для базы данных, вы можете рассмотреть возможность использования метода, который возвращает true/false, если соединение доступно, и пинговать этот метод по расписанию. Вы даже можете обернуть метод для этого в бесконечный цикл, повторяющийся через некоторое время, и вызвать собственное событие, когда это «состояние» действительно изменится.

Вот быстрый метод из другого ответа SO, который представляет собой простой подход:

/// <summary>
/// Test that the server is connected
/// </summary>
/// <param name="connectionString">The connection string</param>
/// <returns>true if the connection is opened</returns>
private static bool IsServerConnected(string connectionString)
{
    using (SqlConnection connection = new SqlConnection(connectionString))
    {
        try
        {
            connection.Open();
            return true;
        }
        catch (SqlException)
        {
            return false;
        }
    }
}

Источник: https://stackoverflow.com/a/9943871/4154421

person Jason W    schedule 27.05.2016
comment
Событие StateChange предназначено для состояния соединения, а не для экземпляра сервера базы данных. -› Но если состояние сервера изменится, то и состояние соединения должно измениться (т.е. перестать функционировать). - person Jacob Horbulyk; 31.05.2016
comment
Но соединение не обнаружит изменение состояния сервера базы данных, если вы на самом деле не запросите что-то на сервере, поэтому у вас должна быть оболочка, как указано в ответе, чтобы у вас был надежный способ определить истинные изменения состояния, а не только на вашем прикладном уровне. - person Jason W; 01.06.2016