2013-08-30 156 views
2

環境條款:參數化WHERE在查詢

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

我能參數,其中在SQL Server中的條款?

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

例如,

  • 用戶輸入 「[CookingTime] < 30和[費用] < 20」
  • 應用程序創建的查詢「從[RecipeID] [配方]其中[CookingTime] < 30和[成本] < 20「並在SQL Server中執行。
  • 應用程序返回結果給用戶。

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

在此先感謝。

+0

您可以創建一個動態查詢 – Dhaval

+1

動態查詢是不是在這裏一個很好的解決方案,您使用的用戶輸入來創建查詢,這是非常糟糕的主意,並開放給SQL注入 –

+0

@rs根本不符合事實,您可以創建一個參數化的動態查詢字符串。 – Phill

回答

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

這並不解決原來的問題,因爲OP需要參數化整個where子句 –

0

我想原來的問題是想知道如何從用戶的輸入中動態生成它,然後使用適當的sql參數來執行查詢。

的SQL參數的用法,通常我做的是使用一個通用的helper方法,一個簡單的例子(未測試):

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。

構建委託類來完成這個工作,如果它非常複雜的話。