简体   繁体   English

在查询中参数化WHERE子句

[英]Parameterize WHERE Clause in Query

Environment: 环境:

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

Hi 你好

Could I parameterize where clause in SQL Server? 我可以在SQL Server中参数where子句吗?

In my scenario, once a WHERE clause String is input, application will concatenate it to other part of query and execute in SQL Server then return the result. 在我的场景中,一旦输入WHERE子句字符串,应用程序会将其连接到查询的其他部分并在SQL Server中执行,然后返回结果。

For example, 例如,

  • User inputs "[CookingTime] < 30 and [Cost] < 20" 用户输入“ [CookingTime] <30和[Cost] <20”
  • Application creates query "select [RecipeID] from [Recipes] where [CookingTime] < 30 and [Cost] < 20" and executes in SQL Server. 应用程序创建查询“从[食谱]中选择[食谱ID],其中[烹饪时间] <30且[成本] <20”,并在SQL Server中执行。
  • Application returns result to user. 应用程序将结果返回给用户。

For security reason, I would like to make whole WHERE CLAUSE as parameter. 出于安全原因,我想将整个WHERE CLAUSE作为参数。 But I have no idea how to achieve. 但是我不知道如何实现。

Thanks in advance. 提前致谢。

This is how it can be done 是可以做到的

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);
    }
}

The whole WHERE clause as parameter will be a victim of sql injection in any way. 整个WHERE子句作为参数将以任何方式成为sql注入的受害者。 To prevent this you'd better to: 为避免这种情况,您最好:

Setup proper permissions. 设置适当的权限。 So even in case of sql injected user can't access anything not granted. 因此,即使在注入sql的情况下,用户也无法访问未授予的任何内容。 In this case sample of @Dhaval is better, because dymanic sql generation incapsulated in stored procedure requires less permissions to execute. 在这种情况下,@ Dhaval的示例更好,因为封装在存储过程中的动态sql生成需要较少的执行权限。

Check the statement for sql injection. 检查语句以进行sql注入。 The simplest way is to check for semicolons in order to avoid another statements in the batch. 最简单的方法是检查分号,以避免在批处理中出现其他语句。 More complex and more precise way is to use t-sql DOM parser. 更复杂,更精确的方法是使用t-sql DOM解析器。 For example: 例如:

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");
    }
}

You can create a dynamic query in sql server and pass the parameter from C# 您可以在sql server中创建动态查询并从C#传递参数

Something like this 像这样

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# Code to execute the procedure 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);
     }

I guess the original question wanted to find out how to make it dynamically from user's input and then use proper sql parameter to do the query. 我想原来的问题想找出如何从用户的输入动态地使它,然后使用适当的sql参数来执行查询。

For the usage of sql parameter, normally what I do is to use a generic helper method, a quick example (not tested): 对于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());
        }
    }
}

of course, this is only Reading, for Insert/Update, similar methods could be created too. 当然,这仅是读取,对于插入/更新,也可以创建类似的方法。

But the complicated part is how to make it dynamic with unknown number of conditions and the relationship between them. 但是,复杂的部分是如何在未知数量的条件以及它们之间的关系的情况下使其动态化。 So a quick suggestion is use a delegated method or class to do the work. 因此,一个快速的建议是使用委托的方法或类来完成工作。 sample (not tested): 样品(未测试):

    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());
        }
    }

for the static function GetParamsFromInputString, it's just a sample. 对于静态函数GetParamsFromInputString,这只是一个示例。 actually it could be very complicated depending on your needs. 实际上,这可能非常复杂,具体取决于您的需求。

for example, you might want to include the operator (whether it's >, < or <>,...). 例如,您可能要包括运算符(无论是>,<还是<>,...)。

and you might also want to include the conjunctions between the conditions, whether it's AND or OR. 并且您可能还希望包括条件之间的连接,无论是AND还是OR。

Build delegated classes to do the job if it's very complicated. 如果非常复杂,则构建委托类来完成这项工作。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM