相关知识:
- 在ADO.NET访问SQL Server时,鼓励使用存储过程取代常规的SQL语句。
- 存储过程有下列优点:
- 存储过程中的SQL语句将会经过预先的解析和编译,然后存放在数据库服务器上行。调用的时候不必在此解析语法和编译,因此效率比采用常规SQL语句高
- 带参数的存储过程在一定程度上可以降低SQL注入攻击的风险
- 存储过程便于在数据库服务器上统一管理,减少了程序员维护SQL代码的工作量
- 存储过程有利于重用某些数据库的访问逻辑
代码示例:
- 在数据库中创建存储过程(沿用SQLInjection案例描述的数据库。请确保Account表中有若干行数据)
- 不带参数的存储过程:forAccountGetAll
CREATE PROCEDURE forAccountGetAll AS SELECT AccountID, AccountName, Password FROM Account
- 带输入参数的存储过程:forAccountInsert
CREATE PROCEDURE forAccountInsert (@AccountID int, @AccountName nvarchar(50), @Password nvarchar(50) )AS INSERT INTO Account(AccountID, AccountName, password) VALUES(@AccountID, @AccountName, @password)
- 带输入和输出从参数的存储过程:根据用户名和密码,找到匹配的AccountID作为输出参数:forAccountLogin
CREATE PROCEDURE forAccountLogin (@AccountName nvarchar(50), @Password nvarchar(50), @AccountID int output )AS SELECT @AccountID=AccountID FROM Account WHERE AccountName=@AccountName AND password=@password
- 代码
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 using System.Data; 7 using System.Data.SqlClient; 8 9 namespace ConsoleApplication1210 {11 class Program12 {13 static string strConn = @"server=Joe-PC;database=AccountDBforSQLInjection;uid=sa;pwd=root";14 static SqlConnection conn = new SqlConnection(strConn);15 static void Main(string[] args)16 {17 //调用不带参数的存储过程18 //CallProcedureGetAll(); 19 //调用带输入参数的存储过程20 //CallProcedureInsert();21 //调用带输入和输出参数的存储过程22 //CallProcedureLogin();23 }24 25 static void CallProcedureGetAll()26 {27 string sql = "forAccountGetAll";//存储过程名字28 29 SqlDataAdapter da = new SqlDataAdapter(sql, conn);30 da.SelectCommand.CommandType = CommandType.StoredProcedure;//指定调用存储过程(默认是SQL文本)31 32 DataSet ds = new DataSet();33 da.Fill(ds, "AccountGetAll");34 35 DataTable dt = ds.Tables["AccountGetAll"];36 DataView dv = new DataView(dt);37 38 dv.Sort = "AccountID ASC";39 40 Console.WriteLine("调用存储过程 forAccountGetAll:");41 42 foreach (DataRowView drv in dv)43 {44 Console.WriteLine("{0}:{1},{2}", drv["AccountID"], drv["AccountName"], drv["password"]);45 }46 }47 48 static void CallProcedureInsert()49 {50 string sql = "forAccountInsert";51 SqlCommand cmd = new SqlCommand(sql, conn);52 53 cmd.CommandType = CommandType.StoredProcedure;54 //设置参数值,并添加到Command对象的参数集合中55 cmd.Parameters.AddWithValue("@AccountID", 100);56 cmd.Parameters.AddWithValue("@AccountName", "new");57 cmd.Parameters.AddWithValue("@password", "123456");58 59 conn.Open();60 cmd.ExecuteNonQuery();61 conn.Close();62 }63 64 static void CallProcedureLogin()65 {66 string sql = "forAccountLogin";67 SqlCommand cmd = new SqlCommand(sql, conn);68 69 cmd.CommandType = CommandType.StoredProcedure;70 //设置输入参数值71 cmd.Parameters.AddWithValue("@AccountName", "Joe");72 cmd.Parameters.AddWithValue("@password", "123456");73 //准备输出参数74 SqlParameter param = new SqlParameter("@AccountID", SqlDbType.Int);75 param.Direction = ParameterDirection.Output;//指定是输出参数76 77 cmd.Parameters.Add(param); // 将输出参数添加到Command对象的参数集合中78 79 conn.Open();80 cmd.ExecuteNonQuery();81 //如果没有满足条件的用户名和密码,那么输出参数@AccountID的值将为DBNull.Value82 if (param.Value == DBNull.Value)83 {84 Console.WriteLine("failed");85 }86 else87 {88 Console.WriteLine("Done");89 }90 conn.Close();91 }92 }93 }