在Query中参数化WHERE子句

时间:2013-08-30 04:06:21

标签: c# .net sql sql-server

环境:

  • C#
  • Visual Studio 2012
  • .NET Framework 3.5

您好

我可以参数化SQL Server中的where子句吗?

在我的场景中,一旦输入WHERE子句String,应用程序就会将它连接到查询的其他部分并在SQL Server中执行,然后返回结果。

例如,

  • 用户输入“[CookingTime]< 30和[Cost]< 20”
  • 应用程序创建查询“从[食谱]中选择[RecipeID],其中[CookingTime]< 30和[Cost]< 20”并在SQL Server中执行。
  • 应用程序将结果返回给用户。

出于安全原因,我想将整个WHERE CLAUSE作为参数。 但我不知道如何实现。

提前致谢。

4 个答案:

答案 0 :(得分:2)

This是如何做到的

string commandText = "UPDATE Sales.Store SET Demographics = @demographics "
    + "WHERE CustomerID = @ID;";

using (SqlConnection connection = new SqlConnection(connectionString))
{
    SqlCommand command = new SqlCommand(commandText, connection);
    command.Parameters.Add("@ID", SqlDbType.Int);
    command.Parameters["@ID"].Value = customerID;

    // Use AddWithValue to assign Demographics. 
    // SQL Server will implicitly convert strings into XML.
    command.Parameters.AddWithValue("@demographics", demoXml);

    try
    {
        connection.Open();
        Int32 rowsAffected = command.ExecuteNonQuery();
        Console.WriteLine("RowsAffected: {0}", rowsAffected);
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);
    }
}

答案 1 :(得分:1)

作为参数的整个WHERE子句将以任何方式成为sql注入的受害者。为了防止这种情况,你最好:

设置适当的权限。因此,即使在sql注入的情况下,用户也无法访问未授予的任何内容。在这种情况下,@ Dhaval的样本更好,因为在存储过程中封装的dymanic sql生成需要较少的执行权限。

检查语句是否为sql注入。最简单的方法是检查分号以避免批处理中的其他语句。更复杂,更精确的方法是使用t-sql DOM解析器。例如:

using Microsoft.SqlServer.TransactSql.ScriptDom;

TSql110Parser parser = new TSql110Parser(true);
IList<ParseError> errors = null;
var condition = "a > 100; delete from [Recipes]";
var script = parser.Parse(new StringReader("select [RecipeID] from [Recipes] where " + condition), out errors) as TSqlScript;

if (errors.Count > 0)
{
    throw new Exception(errors[0].Message);
}

foreach (var batch in script.Batches)
{
    if (batch.Statements.Count == 1)
    {
        var select = batch.Statements[0] as SelectStatement;
        if (select != null)
        {
            QuerySpecification query = select.QueryExpression as QuerySpecification;
            if (query.WhereClause is BooleanBinaryExpression)
            {
                ...
            }
        }
        else
        {
            throw new Exception("Select statement only allowed");
        }
    }
    else
    {
        throw new Exception("More than one statement detected");
    }
}

答案 2 :(得分:0)

您可以在sql server中创建动态查询,并从C#

传递参数

像这样的东西

Create Procedure usp_Test    
    @WhereCond Varchar(max)
AS
Bgein
    Set NoCount ON
    Declare @SQLQuery AS Varchar(max)
    Set @SQLQuery = 'Select * From tblEmployees where ' + @WhereCond
    Execute sp_Executesql @SQLQuery

End

执行程序的C#代码

DataSet ds = new DataSet();
    using(SqlConnection conn = new SqlConnection("ConnectionString"))
    {               
            SqlCommand sqlComm = new SqlCommand("usp_Test", conn);               
            sqlComm.Parameters.AddWithValue("@WhereCond", WhereCond);

            sqlComm.CommandType = CommandType.StoredProcedure;

            SqlDataAdapter da = new SqlDataAdapter();
            da.SelectCommand = sqlComm;

            da.Fill(ds);
     }

答案 3 :(得分:0)

我想原来的问题想知道如何从用户的输入中动态创建它,然后使用正确的sql参数来进行查询。

对于sql参数的使用,通常我所做的是使用泛型辅助方法,一个快速示例(未经测试):

public static class SqlHelpers
{
    public static IEnumerable<T> ExecuteAdhocQuery<T>(SqlConnection con, string sql, CommandType cmdType, Func<SqlDataReader, T> converter, params SqlParameter[] args)
    {
        try
        {
            using (SqlCommand cmd = new SqlCommand(sql, con) { CommandType = cmdType })
            {
                cmd.Parameters.AddRange(args);

                if (con.State != ConnectionState.Open) { con.Open(); }

                var ret = new List<T>();

                using (SqlDataReader rdr = cmd.ExecuteReader())
                {
                    while (rdr.Read())
                    {
                        ret.Add(converter.Invoke(rdr));
                    }
                }

                return ret;
            }
        }
        catch (Exception e)
        {
            // log error?
            Console.WriteLine(e.Message);
            Console.WriteLine(e.StackTrace);
            throw e; // handle exception...
        }
    }

    public void Test()
    {
        using (SqlConnection con = new SqlConnection("connection string here"))
        {
            var data = ExecuteAdhocQuery(con,
                "SELECT ID, Name FROM tblMyTable WHERE ID = @Id and Status = @Status;",
                CommandType.Text, (x) => new { Id = x.GetInt32(0), Name = x.GetString(1) },
                new SqlParameter("@Id", SqlDbType.Int) { Value = 1 },
                new SqlParameter("@Status", SqlDbType.Bit) { Value = true });
            Console.WriteLine(data.Count());
        }
    }
}

当然,这只是阅读,对于插入/更新,也可以创建类似的方法。

但复杂的部分是如何使它具有未知数量的条件和它们之间的关系。因此,快速建议使用委托方法或类来完成工作。样品(未测试):

    public static Dictionary<string, SqlParameter> GetParamsFromInputString(string inputString)
    {
        var output = new Dictionary<string, SqlParameter>();

        // use Regex to translate the input string (something like "[CookingTime] < 30 and [Cost] < 20" ) into a key value pair
        // and then build sql parameter and return out
        // The key will be the database field while the corresponding value is the sql param with value

        return output;
    }

    public void TestWithInput(string condition)
    {
        var parameters = GetParamsFromInputString(condition);

        // first build up the sql query:
        var sql = "SELECT Id, Name from tblMyTable WHERE " + parameters.Select(m => string.Format("{0}={1}", m.Key, m.Value.ParameterName)).Aggregate((m,n) => m + " AND " + n);
        using (SqlConnection con = new SqlConnection("connection string here"))
        {
            var data = ExecuteAdhocQuery(con,
                sql,
                CommandType.Text,
                (x) => new { Id = x.GetInt32(0), Name = x.GetString(1) },
                parameters.Select(m => m.Value).ToArray());
        }
    }

对于静态函数GetParamsFromInputString,它只是一个示例。实际上,根据您的需要,它可能非常复杂。

例如,您可能希望包含运算符(无论是&gt;,&lt;或&lt;&gt;,...)。

并且您可能还希望在条件之间包含连词,无论是AND还是OR。

如果它非常复杂,那么构建委托类来完成这项工作。