ODP.net Пул подключений: ClientID, идентификатор клиента, никогда не меняется от первого пользователя, который входит в систему

#asp.net #oracle #oracle10g #connection-pooling #odp.net

#asp.net #Oracle #oracle10g #объединение в пул соединений #odp.net

Вопрос:

Сценарий: У нас есть приложение, использующее Oracle 10g и последнюю версию ODP.net в пределах ASP.net применение. Мы используем .Свойство ClientID WriteOnly для объекта OracleConnection для передачи определенного идентификатора пользователя в базу данных в целях аудита. Когда пул подключений отключен, это работает отлично.

Когда это включено, первый пользователь, который входит в систему (например, ПОЛЬЗОВАТЕЛЬ1), обновляет запись, и измененным ИМЕНЕМ_BY является USER1, но когда другой пользователь переходит на веб-сайт после этого, таким образом, захватывая объединенное соединение, измененным ИМЕНЕМ_BY по-прежнему является USER1, несмотря на передачу USER2 в ClientID.

Логика нашей базы данных следующая:

Мы сохраняем класс в ASP.net сеанс, в котором используется наша логика подключения к базе данных. При первоначальном вызове это наш конструктор:

 Public Sub New(ByVal connection As String, Optional ByVal oracleClientID As String = "")
        MyBase.New()
        _oracleConnection = New OracleConnection(connection)
        _clientID = oracleClientID
        End If
    End Sub
  

Вот суть кода для открытия соединения и закрытия, удаления:

 Try
    _OraCmd = New OracleCommand(command, _oracleConnection)
    With _OraCmd
        .BindByName = True
        .Parameters.Clear()
        .CommandType = CommandType.StoredProcedure
        _oracleConnection.Open()
            If _clientID <> "" Then _oracleConnection.ClientId = _clientID
        Dim OraDadpt As New OracleDataAdapter(_OraCmd)
            '' Logic to get data
        OraDadpt.Fill(ds)
    End With
Catch ex As Exception
    Throw ex
Finally

    ClearParameters()
    _OraCmd.Dispose()
    _oracleConnection.Close()
End Try
  

Идея заключается в том, что, поскольку соединение объединено, предполагается вызов триггера входа в систему, который никогда не выполняется, и идентификатор клиента никогда не устанавливается снова. Однако в документации ORACLE указано, что ClientID используется именно для того, что мы пытаемся сделать.

У кого-нибудь есть какие-либо мысли относительно того, почему SYS_CONTEXT (‘USERENV’, ‘CLIENT_IDENTIFIER’) не устанавливается на новый идентификатор пользователя, который передается в идентификатор клиента, когда пул подключений используется в нашем .СЕТЕВОЕ приложение с ODP.net ? Это настройка базы данных, настройка прослушивателя?

Обновить

Мы переадресовали проблему в Oracle. При этом нам пришлось создать небольшое тестовое приложение, которое имитировало проблему. При этом на моем локальном хостинге все работало отлично, используя встроенный веб-сервер Cassini Visual Studio. С IIS возникает проблема.

Обновить

Определено, что проблема не в IIS. Это были переменные пакета, которые не были удалены из-за повторного использования соединений, которые были объединены в пул, по сути, то, что должен делать пул. Мы решили это с помощью DBMS_SESSION.MODIFY_PACKAGE_STATE (DBMS_SESSION.ПОВТОРНАЯ ИНИЦИАЛИЗАЦИЯ).

Комментарии:

1. Вы упомянули триггер входа в систему. Какова именно цель триггера входа в систему в вашей настройке? И можете ли вы опубликовать его код.

2. У нас его нет. ODP.net следует обрабатывать это через . Свойство ClientID. Из всего, что я прочитал, когда происходит вход в базу данных — ODP устанавливает SYS_CONTEXT с помощью . ClientID.

3. Да, ODP.NET задает SYS_CONTEXT. Но это не имеет ничего общего с триггером входа в систему, который является чем-то другим. Можете ли вы показать строку подключения, которую вы используете для объединения в пул соединений? Возможно, вы не используете ODP.NET пул подключений (который сбрасывает идентификатор клиента, когда соединение возвращается в пул).

4. Строка подключения проста: «источник данных = DataSource; идентификатор пользователя =user; пароль = pass;» — Пул по умолчанию включен, насколько я понимаю. Я пытался использовать все свойства минимального и максимального размера пула и т.д., Но безуспешно.

5. Эй, как насчет установки DBMS_SESSION.SET_IDENTIFIER при открытии соединения

Ответ №1:

Попробуйте использовать DBMS.Rest_Package перед закрытием подключений.

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

Комментарии:

1. Как насчет DBMS_SESSION.MODIFY_PACKAGE_STATE (DBMS_SESSION. ПОВТОРНО ИНИЦИАЛИЗИРОВАТЬ)?

2. На самом деле это лучший подход. У него нет утечки памяти, которая была у DMBS.Reset_Package, или других накладных расходов. Вы также можете захотеть вызвать это ПЕРЕД выполнением вашего SQL, а не после и перед закрытием. Если по какой-либо причине вызов пакета зависает и вы не можете выполнить следующую строку кода, вы не будете сбрасывать сеанс. Если прослушиватель пула отключит соединение и вернет его обратно в пул, у вас будет соединение с глобальными переменными пакета, которые могут быть обнаружены следующим вызовом, который получит это соединение. Мне бы не хотелось отлаживать это позже.

3. asktom.oracle.com/pls/apex / … может помочь. Это указывает на то, что использование глобальных переменных пакета с объединением в пул соединений изначально нецелесообразно. Однако, если необходимо, использование повторной инициализации, о которой вы упомянули, должно устранить сохранение пакета при возврате соединения в пул.

4. После тщательного тестирования было установлено, что проблема заключается в переменных пакета. Хотя это и не далеко идущее решение, DBMS_SESSION.MODIFY_PACKAGE_STATE(DBMS_SESSION. ПОВТОРНАЯ ИНИЦИАЛИЗАЦИЯ) работает прямо сейчас.

5. Руководство: Не используйте глобальные общедоступные переменные пакета при объединении в пул. Почему? Потому что, когда вы закрываете соединение, оно на самом деле не закрывается. Он становится доступным для следующего запроса пулом; но пул поддерживает соединение. Поскольку соединение поддерживается, сохраняются все переменные сеанса DB. Сюда входят глобальные пакеты, временные таблицы и экземпляры объектов. Поэтому, когда второй пользователь получает соединение, которое вы только что выпустили, и пытается получить доступ к тем же пакетам, что и вы, они получают грязный сеанс DB. Это также означает, что блокировка anon больше не срабатывает!

Ответ №2:

Это отлично работает как при включении, так и при выключении пула. Идентификатор клиента и ClientInfo в сеансе Oracle не обновляются до тех пор, пока не будет выполнена команда.

Не могли бы вы проверить правильность вашего утверждения if? Если _clientID <> «» , то _oracleConnection.ClientID = _clientID. Даже когда вы закрываете соединение, идентификатор клиента все равно останется прежним. Не уверен, где ваша настройка / получение _clientId при передаче этого в ваш метод.

 class Program
{
    static void Main(string[] args)
    {
        TestClientId test = new TestClientId();
        test.DoSomething("FirstClientId");
        test.DoSomething("ChangedClientId");
    }
}

public class TestClientId
{
    /// <summary>
    /// The connection string. 
    /// </summary>
    private const string ConnString = "DATA SOURCE=//server:port/service_name;USER ID=user;PASSWORD=pswd;";

    /// <summary>
    /// The oracle connection.
    /// </summary>
    private OracleConnection connection;

    /// <summary>
    /// The oracle session id.
    /// </summary>
    private long sid;

    /// <summary>
    /// Initializes a new instance of the <see cref="TestClientId"/> class.
    /// </summary>
    public TestClientId()
    {
        this.connection = new OracleConnection(ConnString);
    }

    /// <summary>
    /// Changes the client id of the oracle connection.
    /// </summary>
    /// <param name="clientId">The client id.</param>
    public void DoSomething(string clientId)
    {            
        this.connection.Open();
        this.sid = this.GetSessionId(this.connection);

        if (!string.IsNullOrEmpty(clientId))
        {
            this.connection.ClientInfo = clientId;
            this.connection.ClientId = clientId;                
        }

        OracleCommand command = new OracleCommand("select * from dual", this.connection);
        command.ExecuteNonQuery();

        this.connection.Close();
    }

    /// <summary>
    /// Gets the session id.
    /// </summary>
    /// <param name="con">The connection object.</param>
    /// <returns>The current oracle session id.</returns>
    public int GetSessionId(OracleConnection con)
    {
        OracleCommand cmd = new OracleCommand();
        cmd.Connection = con;
        cmd.CommandText = "select SYS_CONTEXT('USERENV','SID') from dual";
        object sid = cmd.ExecuteScalar();
        return Convert.ToInt32(sid);
    }
}
  

Комментарии:

1. В нашем случае это работает только при первом входе в базу данных. Все последующие вызовы в пределах времени ожидания пула — это имя первого пользователя, вошедшего в систему. Если мы допустим истечение периода ожидания, соединение прервется, и следующий пользователь получит свое имя в ClientID. Как будто соединение никогда не закрывается. Но я закрываю его.

2. У меня это действительно работает. Я пробовал api 10.x для базы данных 11g и api 11.x для базы данных 10g. Работают оба способа. Проблема, с которой я столкнулся, заключалась в том, что мне нужно выполнить команду до фактического обновления идентификатора клиента или ClientInfo. Я тестировал с объединением = true и объединением = false, оба работали.

3. Согласно вашему предыдущему вопросу, мы передаем ClientID в конструктор и устанавливаем для него закрытую переменную. Затем получите доступ к частной переменной и установите для нее значение ClientID при выполнении вызова Execute.

4. Это то, что наводит меня на мысль, что это, должно быть, какая-то проблема с сервером. Мы перенаправляем этот случай в Oracle, чтобы проверить, может ли это быть так. Я попробовал ваше решение выше, точно такое, как у вас, но безуспешно.

5. Ранее, просматривая Google, я увидел пользователя с аналогичной проблемой на форумах Oracle, и они упомянули, что переустановили odp.net или odac, и это решило проблему. Мне трудно в это поверить в данном случае. Сборка есть, она должна работать. Хотя, возможно, стоит попробовать.

Ответ №3:

Идентификатор клиента сбрасывается только при закрытии соединения, и если вы закрываетесь, то его обязательно нужно сбросить.

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

Поэтому было бы неплохо установить идентификатор сеанса с помощью DBMS_SESSION.SET_IDENTIFIER

Надеюсь, это поможет

Комментарии:

1. Основываясь на приведенном выше коде, the . Close () выполняется при каждом запросе. Однако ваша логика кажется правильной. Это действует, если соединение никогда не закрывается. Из проведенного мной тестирования. Всегда нажимается Close.

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

3. Вы правы, что он достигает закрытия , но при объединении в пул он никогда не закрывается, а возвращается в пул, и когда используется следующий запрос в том же соединении. Итак, в вашем текущем контексте всегда используется то же соединение, что и у USER1

4. За исключением того, что это именно то, что должен делать ClientID … сбрасывать идентификатор клиента, даже при захвате существующего объединенного соединения.

Ответ №4:

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

Я подозреваю, что всевозможные неприятные вещи случаются, когда вы сохраняете соединение в своем сеансе. Вероятно, вы используете соединение, когда оно одновременно используется другим запросом. Или вы можете получить закрытое соединение, потому что пул connectin должен решить закрыть его.

Более того, триггер ВХОДА в систему, вероятно, выполняется только при первом создании подключения к базе данных, но не выполняется снова, когда соединение повторно используется для другого запроса или другого пользователя.

Чтобы устранить вашу проблему, перехватывайте подключение к базе данных в начале каждого запроса и явно задавайте идентификатор клиента (и / или выполняйте код, который запускается триггером входа в систему). Затем используйте это соединение на время выполнения запроса. Но не сохраняйте его нигде после завершения запроса.

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

Комментарии:

1. Соединение фактически не поддерживается открытым в сеансе. Это просто класс, который содержит функциональность. Соединение открывается и закрывается для каждой операции.

2. На самом деле мы перехватываем подключение к базе данных в начале каждого запроса, устанавливаем . ClientID, запустите любую операцию, затем закройте соединение.

3. Я только что попробовал это и вижу ту же проблему, что и вы. Я не вижу ничего плохого в способе вашего подключения. Сейчас я изучаю ответ.

4. Я полагаю, вопрос в том, является ли новейший ODP.net в этом качестве версия работает вместе с 10g Release 2.

5. Что-то в этом, кажется, терпимо. У меня было старое приложение, которое при подключении к базе данных 11g без проблем обновляло идентификатор клиента сразу после открытия соединения. Теперь у меня версия Oracle 11.2. API доступа к данным для базы данных 11g, и не повезло с обновлением идентификатора клиента или ClientInfo. Предыдущее приложение, которое работало, возможно, использовало ссылку на доступ к данным 10.2.

Ответ №5:

Когда объединение в пул подключений включено, что хорошо и, конечно, путь в ASP.NET сценарий (и фактически в большинстве сценариев), вы не должны хранить какое-либо подключение к БД. Вы должны открывать и закрывать соединения, когда вам это нужно.

Вот ссылка о SQL Server, но это то же самое с Oracle, что объясняет это: класс SqlConnection

Таким образом, код, который вам нужно использовать при вызове Oracle, должен быть примерно таким, в любом месте вашего приложения, когда вам это нужно:

 Using connection As New OracleConnection(connectionString)
    connection.Open()
    ' Do work here; connection closed on following line.
End Using
  

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