C# ADO.NET IBM DB2 命名参数同名抛出没有足够的参数指定异常

Posted

技术标签:

【中文标题】C# ADO.NET IBM DB2 命名参数同名抛出没有足够的参数指定异常【英文标题】:C# ADO.NET IBM DB2 named parameters with same name throws Not enough parameters specified Exception 【发布时间】:2014-07-27 01:52:27 【问题描述】:

我有一个相当不可知的 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 代码。

谢谢,感谢您的帮助。

【问题讨论】:

你有没有找到解决这个问题的方法?我们遇到了同样的行为 很遗憾,我还没有找到解决方案。我必须创建另一个命名参数并为其分配相同的值。 有趣。作为 ADO.NET 的新手,我还没有意识到 .NET 提供程序可以允许您在 DB2 for i 中使用命名参数。 IBM i 上的 DB2 使用 ? parameter markers。我假设 Windows 端的某个层在将请求发送到服务器之前将命名参数转换为问号。 @WarrenT 您提供的链接提到了命名参数,但它们似乎无法正常工作。这很臭,因为我也遇到了和帖子一样的问题。 【参考方案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&lt;string,object&gt;,我在设置查询时也添加了它。要使用你的例子,我会有这个

    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() 枚举查询,然后在列表上进行检查和打印。

【讨论】:

以上是关于C# ADO.NET IBM DB2 命名参数同名抛出没有足够的参数指定异常的主要内容,如果未能解决你的问题,请参考以下文章

C# ADO.NET参数查询

IBM.Data.DB2.Core DataReader.GetSchemaTable() 方法抛出 NotSupportedException C# .NET 标准

VS2019 C# 连接IBM DB2数据库并查询数据表

C# SQL 选择查询表参数

ADO.NET 中参数化 SQL 命令的最终形式

C# - 无法将类型“IBM.Data.DB2.iSeries.iDB2DataReader”隐式转换为“System.Data.SqlClient.SqlDataReader”