具有无限参数的参数化SQL查询

时间:2014-01-05 04:22:55

标签: c# sql database vb.net parameterized

我很久以前就听说过参数化的sql查询,但我从来没有真正关注它,因为我习惯于编写完整的sql语句。我知道它提高了sql注入的安全性,所以现在可能是适应变化的好时机,即使为时已晚。我从这个网站http://www.codinghorror.com/blog/2005/04/give-me-parameterized-sql-or-give-me-death.html得到了这个。因此所有学分都归作者所有。我注意到参数化sql查询的所有示例都有paremeter计数限制。一个例子如下所示。

SqlConnection conn = new SqlConnection(_connectionString);
conn.Open();
string s = "SELECT email, passwd, login_id, full_name FROM members WHERE email = @email";
SqlCommand cmd = new SqlCommand(s);
cmd.Parameters.Add("@email", email);
SqlDataReader reader = cmd.ExecuteReader();

这相当于

SELECT email, passwd, login_id, full_name FROM members WHERE email = 'x';

我通过阅读在线教程自学了所有编程,我真的没有人帮助我。我陷入完整的sql语句查询的原因是我无法弄清楚如何使用无限参数进行参数化sql查询。有没有办法做到这一点?该示例仅接受1个参数,即“email”字段。它从给定的sql语句中选择4个字段。我的问题是......有没有什么方法可以用5,6,7或100个选定字段进行参数化sql查询,以及WHERE子句下的条件?如果可以,则在使用INSERT语句时特别有用。非常感谢你。

这个例子在C#中,但是非常感谢任何VB.NET或相同的C#实现。感谢。

3 个答案:

答案 0 :(得分:4)

一种可能的解决方案是使用Dictionary object传递参数名称和值。 Dictionary对象包含的集合,这正是单个SqlParameter的形式 - 表单中的单个键/值容器:< / p>

Key = @Value

集合可以包含任意数量的项目,例如:

new Dictionary<String, Object> 
{
    { "@Name", "Anonymous" },
    { "@Age", 25 },
    { "@Street", "Highway" },
    { "@Number", "1001" },
    { "@City", "NoName" }
}

在上面的示例中,的类型为String,而的类型为Object。 Object允许任意类型的参数值(稍后在代码示例中进行说明)。

创建动态SQL语句的一种可能性是:

  • 提取重复性任务,例如创建SqlCommand并将参数传递给SqlCommand变量
  • 为所需的SQL命令创建参数化动态 SQL查询字符串

代码可能如下所示:

// extract all repetitive tasks

// Create an array of SqlParameters from the given Dictionary object.
// The parameter value is of type Object in order to allow parameter values of arbitrary types!
// The .NET Framework converts the value automatically to the correct DB type. 
// MSDN info: http://msdn.microsoft.com/en-us/library/0881fz2y%28v=vs.110%29.aspx 
private SqlParameter[] dictionaryToSqlParameterArray(Dictionary<string, object> parameters)
{
    var sqlParameterCollection = new List<SqlParameter>();
    foreach (var parameter in parameters)
    {
        sqlParameterCollection.Add(new SqlParameter(parameter.Key, parameter.Value));
    }
    return sqlParameterCollection.ToArray();
}

// sqlQuery is the complete parametrised query
// for example like: INSERT INTO People(Name, Age, Street) VALUES(@Name, @Age, @Street)
private SqlCommand createSqlCommand(String sqlQuery, Dictionary<String, object> parameters)
{
    SqlCommand command = new SqlCommand(sqlQuery);
    command.Parameters.AddRange(dictionaryToSqlParameterArray(parameters));
    return command;
}

现在,带有动态参数计数的调用将如下所示:

SqlConnection connection = new SqlConnection(connectionString);
connection.Open();
string sqlQuery = "SELECT email, passwd, login_id, full_name FROM members WHERE email = @email AND name = @name";
// using the newly created method instead of adding/writing every single parameter manually
SqlCommand command = createSqlCommand(sqlQuery, new Dictionary<String, Object>
                                                    {
                                                        { "@email", "test@test.com" },
                                                        { "@name", "test" }
                                                    });
SqlDataReader reader = command.ExecuteReader();

这是一个好的开始,但这仍然不是很动态,因为我们每次都需要编写完整的查询(我们只保存 command.Parameters.Add 的输入)。让我们改变这个以便输入更少:

// create a parametrised SQL insert command with arbitrary count of parameters for the given table
private SqlCommand createSqlInsert(String tableName, Dictionary<String, object> parameters)
{
    // the sql insert command pattern
    var insertQuery = @"INSERT INTO {0}({1}) VALUES({2})";
    // comma separated column names like: Column1, Column2, Column3, etc.
    var columnNames = parameters.Select (p => p.Key.Substring(1)).Aggregate ((h, t) => String.Format("{0}, {1}", h, t));
    // comma separated parameter names like: @Parameter1, @Parameter2, etc.
    var parameterNames = parameters.Select (p => p.Key).Aggregate ((h, t) => String.Format("{0}, {1}", h, t));
    // build the complete query
    var sqlQuery = String.Format(insertQuery, tableName, columnNames, parameterNames);
    // debug
    Console.WriteLine(sqlQuery);
    // return the new dynamic query
    return createSqlCommand(sqlQuery, parameters);
}

// create a parametrised SQL select/where command with arbitrary count of parameters for the given table
private SqlCommand createSqlWhere(String tableName, Dictionary<String, object> parameters)
{
    // the sql select command pattern
    var whereQuery = @"SELECT * FROM {0} WHERE {1}";
    // sql where condition like: Column1 = @Parameter1 AND Column2 = @Parameter2 etc.
    var whereCondition = parameters.Select (p => String.Format("{0} = {1}", p.Key.Substring(1), p.Key)).Aggregate ((h, t) => String.Format("{0} AND {1}", h, t));
    // build the complete condition
    var sqlQuery = String.Format(whereQuery, tableName, whereCondition);
    // debug
    Console.WriteLine(sqlQuery);
    // return the new dynamic query
    return createSqlCommand(sqlQuery, parameters);
}

现在, SELECT 命令的创建将如下所示:

SqlConnection connection = new SqlConnection(connectionString);
connection.Open();
// specify only table name and arbitrary count of parameters
var getPersonSqlCommand = createSqlWhere("People", new Dictionary<String, Object> 
                                                    {
                                                        { "@Name", "J.D." },
                                                        { "@Age", 30 }
                                                    });
SqlDataReader reader = getPersonSqlCommand.ExecuteReader();

createSqlWhere 方法将返回带有查询的初始化SqlCommand:

SELECT * FROM People WHERE Name = @Name AND Age = @Age

INSERT 也会很短:

SqlConnection connection = new SqlConnection(connectionString);
connection.Open();
// specify only table name and arbitrary count of parameters
var addPersonSqlCommand = createSqlInsert("People", new Dictionary<String, Object> 
                                                {
                                                    { "@Name", "Anonymous" },
                                                    { "@Age", 25 },
                                                    { "@Street", "Test" }
                                                });
addPersonSqlCommand.ExecuteNonQuery();

相应的SQL命令如下所示:

INSERT INTO People(Name, Age, Street) VALUES(@Name, @Age, @Street)

可以以相同的方式创建其他SQL命令,如DELETE,UPDATE等。新参数只应添加到一个地方 - 在字典中。

不可否认,最初的努力不仅仅是编写一种方法,但如果新方法被多次使用(几次),它将获得回报 - 例如五种参数化选择和/或者在具有不同参数的五个不同表上插入,在小型和平均大小的软件项目中总是如此。

答案 1 :(得分:1)

只需创建一个方法:

    public void unlimQuery(string query,params object[] args)
       {
          SqlConnection conn = new SqlConnection(_connectionString);
          conn.Open();
          string s =query;
          SqlCommand cmd = new SqlCommand(s);
          For(int i=0;i<  args.Length;i++)
          cmd.Parameters.Add("@param"+i, args[i]);
          SqlDataReader reader = cmd.ExecuteReader();
        }

示例:

     unlimQuery("INSERT INTO CUSTOMERS(ID,NAME,AGE,ADRESS,COUNTRY) VALUES(@param0,@param1,@param2,@param3,@param4)",5,"Ali",27,"my City","England");

说明:

c#中的params关键字使您可以插入指定类型的无限参数,因此添加的参数(5,“Ali”,27,“my city”,“England”)将被转换为数组然后将对象传递给Method

在方法内你将得到一个对象数组,所以对于你创建参数的每个对象,他的别名是@paramX,其中X是参数的索引(在params数组中),然后sqlCommad将用cmd.Parameters.Add("@param"+i, args[i])子句

中定义的值替换每个别名

所以@ param0 =&gt; 5,.....

答案 2 :(得分:0)

我想你只需要改变这个

cmd.Parameters.Add("@email", email);

cmd.Parameters.AddWithValue("@email", email);