Получение SqlParameter уже содержится в другой ошибке SqlParameterCollection с приложением c # / ASP.net - PullRequest
0 голосов
/ 07 декабря 2018

У меня есть универсальный метод, который добавляет параметры в коллекцию cmd.parameters.Я получаю следующую ошибку, когда 2 человека одновременно выполняют одну и ту же хранимую процедуру:

SqlParameter уже содержится в другой SqlParameterCollection.

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

protected DataSet ExecuteDataSet(string StoredProcName, List<TParameter> Params) {
bool internalOpen = false;
DataSet resultDataSet = null;
TDataAdapter dataAdapter;
TCommand cmd;

try {
    resultDataSet = new DataSet();
    dataAdapter = new TDataAdapter();
    cmd = new TCommand();
    cmd.CommandType = CommandType.StoredProcedure;
    cmd.CommandText = StoredProcName;

    if (transaction != null) {
        cmd.Transaction = transaction;
        cmd.Connection = transaction.Connection;
    } else {
        cmd.Connection = connection;
    }

    if (Params != null && Params.Count > 0) {
        foreach (TParameter param in Params) {
            cmd.Parameters.Add(param);
        }
    }

    dataAdapter.SelectCommand = cmd;

    if (connection.State == ConnectionState.Closed) {
        connection.Open();
        internalOpen = true;
    }

    dataAdapter.Fill(resultDataSet);
    dataAdapter.SelectCommand.Parameters.Clear();

    cmd.Parameters.Clear();
    return resultDataSet;
} catch {
    throw;
} finally {
    if (internalOpen) {
        connection.Close();
    }

}

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

Это вызывается из другого метода, используя следующее:

 result = base.ExecuteDataSet(procName,this.ConvertArrayList(this.ParamsList));

Это старое приложение на c # / asp.net (веб-формы), которое я использую, поэтому код отражает это.

Сообщение об ошибке начинается следующим образом:

[ArgumentException: SqlParameter уже содержится в другом SqlParameterCollection.] System.Data.SqlClient.SqlParameterCollection.Validate (индекс Int32, значение объекта) +5955779 System.Data.SqlClient.SqlParameterCollection.Add (значение объекта) + 34

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

Любая помощь будет принята с благодарностью.Извиняюсь, если я не объяснил это достаточно хорошо, но я смотрел на это весь день и не смог прогрессировать.

Заранее спасибо.

РЕДАКТИРОВАТЬ:

ParamsList устанавливается / получается так:

public ArrayList ParamsList {
        get {
            //Check to see if the parameters list has been initialised.
            if (m_paramsList == null) {
                //Create a new empty parameters list to pass back.
                m_paramsList = new ArrayList();
            }
            return m_paramsList;
        }
        set {
            m_paramsList = value;
        }
    }

И заполняется так:

internal void AddParameter(string name, string value) {
        IDbDataParameter param = CreateStringParameter(name);

        param.Value = GetValueFromString(value);

        Dal.ParamsList.Add(param);
    }

для каждого типа параметра типа ....

внутренняя пустота AddParameter (имя строки, двойное значение?) {

внутренняя пустота AddParameter (имя строки, значение байта []) {

и т. д.

Ответы [ 3 ]

0 голосов
/ 07 декабря 2018

Я предполагаю, что TCommand является типом SqlCommand, а TParameter является типом SqlParameter.Я думаю, что проблема здесь:

    foreach (TParameter param in Params) {
        cmd.Parameters.Add(param);
    }

param является членом Params, который является ссылочным типом;это означает, что вы можете совместно использовать один и тот же список параметров между несколькими вызовами ExecuteDataSet.Вместо этого сделайте что-то вроде этого:

    foreach (TParameter param in Params) {
        cmd.Parameters.Add(new SqlParameter(param.ParameterName, param.SqlDbType, param.Size)).Value = param.Value;
    }

Кроме того, вы могли бы совместно использовать один и тот же SqlConnection между несколькими потоками, что может быть проблемой.Как правило, я бы рекомендовал создать новый SqlConnection в методе ExecuteDataSet.Новый SqlConnection по-прежнему сможет подключаться к любой транзакции в полете.

Наконец, используйте блоки "using" для всех объектов IDisposable, например, SqlCommand, SqlCommand, DataSet и т. Д.

0 голосов
/ 07 декабря 2018

Просматривая код в вопросе ... большая часть не достигла того, что автор, вероятно, намеревался.Например, попытка / поймать / наконец-то была совершенно бесполезной, поскольку блок catch просто перебросил то же исключение, а использование метода Fill() означало, что блок finally не нужен.В другом коде есть похожие проблемы.

За исключением транзакций, вы можете сократить код до этого, предполагая, что TCommand и компания полностью внедрит провайдеров ADO.Net, где сокращенный код фактически увеличивается производительность, безопасность и полезность:

protected DataSet ExecuteDataSet(string StoredProcName, IEnumerable<TParameter> Params = null) 
{    
    DataSet resultDataSet = new DataSet();
    using (var cn = new TConnection(connection.ConnectionString))
    using (var cmd = new TCommand(StoredProcName, cn))
    using (var adapter = new TAdapter(cmd))
    {
        cmd.CommandType = CommandType.StoredProcedure;

        if (Params != null) 
        {
            foreach (TParameter param in Params)
            {
                cmd.Parameters.Add(param);
            }
        }    
        adapter.Fill(resultDataSet);
    }
    return resultDataSet;        
}

Но у нас do есть это значение transaction, и этого достаточно, чтобы нарушить шаблон using здесь.Из-за этого вы захотите эффективно удвоить длину кода, чтобы учесть оба варианта.Да, шаблон using действительно настолько важен , что вы фактически удвоите длину кода, чтобы сохранить его там, где это возможно:

protected DataSet ExecuteDataSet(string StoredProcName, IEnumerable<TParameter> Params = null) 
{  
    DataSet resultDataSet = new DataSet(); 
    if (transaction == null)
    { 
        using (var cn = new TConnection(connection.ConnectionString))
        using (var cmd = new TCommand(StoredProcName, cn))
        using (var adapter = new TAdapter(cmd))
        {
            cmd.CommandType = CommandType.StoredProcedure;

            if (Params != null) 
            {
                foreach (TParameter param in Params)
                {
                    cmd.Parameters.Add(param);
                }
            }    
            adapter.Fill(resultDataSet);
        }
    }
    else
    {
        using (var cmd = new TCommand(StoredProcName, transaction.Connection))
        using (var adapter = new TAdapter(cmd))
        {
            cmd.Transaction = transaction;
            cmd.CommandType = CommandType.StoredProcedure;

            if (Params != null) 
            {
                foreach (TParameter param in Params)
                {
                    cmd.Parameters.Add(param);
                }
            }    
            adapter.Fill(resultDataSet);
        }
    }
    return resultDataSet; 
}

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

0 голосов
/ 07 декабря 2018

Похоже, что объекты в this.ParamsList используются повторно для каждого запроса.Если вы покажете, как этот список определяется и заполняется, мы можем подтвердить, но это звучит так.

Решение в основном состоит в том, чтобы этого не делать.Перестройте список параметров с новыми SqlParameter объектами для каждого запроса.

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

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