2

环境:

  • C#
  • 视觉工作室 2012
  • .NET 框架 3.5

你好

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

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

例如,

  • 用户输入“[CookingTime] < 30 和 [Cost] < 20”
  • 应用程序创建查询“select [RecipeID] from [Recipes] where [CookingTime] < 30 and [Cost] < 20”并在 SQL Server 中执行。
  • 应用程序将结果返回给用户。

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

提前致谢。

4

4 回答 4

2

是可以做到的

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);
    }
}
于 2013-08-30T04:23:56.300 回答
1

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

设置适当的权限。因此,即使在注入 sql 的情况下,用户也无法访问任何未授予的内容。在这种情况下,@Dhaval 的示例更好,因为封装在存储过程中的动态 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");
    }
}
于 2013-08-30T06:12:59.963 回答
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);
     }
于 2013-08-30T04:14:21.993 回答
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,它只是一个示例。实际上,根据您的需要,它可能会非常复杂。

例如,您可能希望包含运算符(无论是 >、< 还是 <>、...)。

并且您可能还想包括条件之间的连词,无论是 AND 还是 OR。

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

于 2013-08-30T04:48:50.603 回答