簡體   English   中英

在查詢中參數化WHERE子句

[英]Parameterize WHERE Clause in Query

環境:

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

你好

我可以在SQL Server中參數where子句嗎?

在我的場景中,一旦輸入WHERE子句字符串,應用程序會將其連接到查詢的其他部分並在SQL Server中執行,然后返回結果。

例如,

  • 用戶輸入“ [CookingTime] <30和[Cost] <20”
  • 應用程序創建查詢“從[食譜]中選擇[食譜ID],其中[烹飪時間] <30且[成本] <20”,並在SQL Server中執行。
  • 應用程序將結果返回給用戶。

出於安全原因,我想將整個WHERE CLAUSE作為參數。 但是我不知道如何實現。

提前致謝。

是可以做到的

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

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

您可以在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);
     }

我想原來的問題想找出如何從用戶的輸入動態地使它,然后使用適當的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。

如果非常復雜,則構建委托類來完成這項工作。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM