C # ADO.NET Именованные параметры IBM DB2 с одинаковыми именами выдают Недостаточно заданных параметров Исключение

У меня есть достаточно независимое приложение ADO.NET, которое подключается к ряду баз данных и может извлекать необходимую информацию для запуска. У меня возникла проблема с DB2 и с тем, как она обрабатывает именованные параметры, особенно когда я повторно использую именованный параметр в том же запросе. Я знаю несколько способов обойти это, просто добавив дополнительные параметры, но теоретически он должен работать так же, как и в других базах данных, к которым я подключаюсь, поскольку имя параметра такое же.

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

выберите значение из test.table, где cola = @ key1 и colb = @ key1;

Именованный параметр @ key1 используется дважды.

Мой код выглядит следующим образом:

       try
        {
            DbProviderFactory dbfFactory = DbProviderFactories.GetFactory("IBM.Data.DB2.iSeries");
            using (DbConnection dbConnection = dbfFactory.CreateConnection())
            {
                dbConnection.ConnectionString = "DataSource=xxx.xxx.xxx.xxx;UserID=xxxxxxxx;password=xxxxxxxxx";

                using (DbCommand dbCommand = dbConnection.CreateCommand())
                {
                    IDbDataParameter iddpParameter1 = dbCommand.CreateParameter();
                    iddpParameter1.ParameterName = "@key1";
                    iddpParameter1.DbType = DbType.String;
                    iddpParameter1.Value = "1";

                    dbCommand.Parameters.Add(iddpParameter1);
                    dbCommand.CommandType = CommandType.Text;
                    dbCommand.CommandText = "select value from test.table where cola=@key1 and colb=@key1";
                    dbConnection.Open();

                    using (IDataReader idrReader = dbCommand.ExecuteReader())
                    {
                        while (idrReader.Read())
                        {
                                   ...
                        }
                    }
                }

            } // end dbConnection
        } // end try

        catch (Exception ex)
        {
            Console.Write(ex.Message);
        }

Когда я запускаю это, я получаю исключение, которое говорит мне:

System.InvalidOperationException: Not enough parameters specified.  The command requires 2 parameter(s), but only 1 parameter(s) exist in the parameter collection.

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

Спасибо, ценю помощь.


person Brent    schedule 05.06.2014    source источник
comment
Вы когда-нибудь находили решение этой проблемы? У нас такое же поведение   -  person Andronicus    schedule 05.09.2014
comment
К сожалению, решения я не нашел. Мне пришлось создать еще один именованный параметр и просто присвоить ему то же значение.   -  person Brent    schedule 24.10.2014
comment
Интересный. Поскольку я был новичком в ADO.NET, я еще не осознавал, что поставщик .NET может позволить вам использовать именованные параметры с DB2 for i. DB2 в IBM i использует ? маркеры параметров. Я предполагаю, что какой-то слой на стороне Windows переводит именованный параметр в вопросительный знак перед отправкой запроса на сервер.   -  person WarrenT    schedule 15.01.2015
comment
@WarrenT в предоставленной вами ссылке упоминаются именованные параметры, но они просто не работают должным образом. Что воняет, потому что у меня такая же проблема, как и у сообщения.   -  person Robert Snyder    schedule 10.12.2015


Ответы (1)


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

в моем файле конфигурации приложения у меня есть

<connectionStrings>
    <add name="AWOLNATION" providerName="Ibm.Data.DB2" connectionString="Server=sail:50000;Database=Remix;" />
</connectionStrings>

в моем классе Databasemanager я бы инициализировал его так

    public static DatabaseManager Instance(string connectionStringName)
    {
        var connectionStringSettings = ConfigurationManager.ConnectionStrings[connectionStringName];
        if (connectionStringSettings == null) throw new MissingMemberException("[app.config]", string.Format("ConnectionStrings[{0}]", connectionStringName));
        return new DatabaseManager(connectionStringSettings);
    }

    private DatabaseManager(ConnectionStringSettings connectionInformation)
    {
        _connectionInformation = connectionInformation;
        _parameters = new Dictionary<string, object>();
    }
    private void Initialize()
    {
        _connection = DbProviderFactories.GetFactory(_connectionInformation.ProviderName).CreateConnection();
        _connection.ConnectionString = _connectionInformation.ConnectionString;

        _command = _connection.CreateCommand();
    }

Хотя я добавляю параметры немного по-другому. У меня есть Dictionary<string,object>, который я тоже добавляю при настройке запроса. Чтобы использовать ваш пример, у меня было бы это

    public IEnumerable<object> GetSomething(string key)
    {
        var sql = "select value from test.table where cola = @key1 and colb = @key1";
        _manager.AddParameter("@key1", key);

        return _manager.ExecuteReader<object>(sql, ToSomethignUseful);
    }

    private object ToSomethignUseful(DatabaseManager arg)
    {
        return new { Value = arg.GetArgument<object>("value") };
    }

то чтение - это то, где у OP и у меня есть аналогичный код

    public IEnumerable<T> ExecuteReader<T>(string sql, Func<DatabaseManager, T> conversionBlock)
    {
        Initialize();
        using (_connection)
        {
            _connection.Open();
            _command.CommandText = sql;
            _command.CommandType = CommandType.Text;

            if (_parameters.Count > 0)
                AddParameters(_command, _parameters);

            _parameters.Clear();

            using (_reader = _command.ExecuteReader())
            {
                while (_reader.Read())
                {
                    yield return conversionBlock(this);
                }
            }
        }
    }
    private static void AddParameters(DbCommand command, Dictionary<string, object> parameters)
    {
        foreach (var param in parameters)
        {
            command.Parameters.Add(CreateParameter(command, param.Key, param.Value));
        }
    }

    private static DbParameter CreateParameter(DbCommand command, string key, object value)
    {
        var parameter = command.CreateParameter();
        parameter.ParameterName = key;
        parameter.Value = value;

        return parameter;
    }

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

Я скажу, что я получил ту же ошибку, когда, по сути, дважды запускал один и тот же код, как показано в этом коде.

    public static void IndendedPrintForEach<T>(this IEnumerable<T> array, string header, Func<T, string> consoleStringConverterMethod)
    {
        var list = array.ToList();
        var color = Console.ForegroundColor;
        Console.ForegroundColor = ConsoleColor.Magenta;
        Console.WriteLine($"<<<{header}>>>");
        Console.ForegroundColor = color;

        if (!list.Any())
        {
            Console.ForegroundColor = ConsoleColor.DarkRed;
            Console.WriteLine("    ************NoItemsFound************");
            Console.ForegroundColor = color;
        }
        else
        {
            foreach (var item in list)
                Console.WriteLine($"    {consoleStringConverterMethod(item)}");
        }
    }

в строке 3 var list = array.ToList() было исправление проблемы, которую вы видели для меня. раньше у меня был if (!array.Any()), который запускал бы запрос и использовал параметры (которые я очищаю перед выполнением запроса), а затем, когда я перехожу к перечислению и печати каждого элемента в массиве, я получал ошибку. Для меня проблема заключалась в том, что он повторно запускал запрос, у которого больше не было параметров. Исправление заключалось в том, чтобы перечислить запрос с помощью ToList (), а затем выполнить проверку и распечатать список.

person Robert Snyder    schedule 10.12.2015