相关知识:
- 在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
- 代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Data;
using System.Data.SqlClient; namespace ConsoleApplication12
{
class Program
{
static string strConn = @"server=Joe-PC;database=AccountDBforSQLInjection;uid=sa;pwd=root";
static SqlConnection conn = new SqlConnection(strConn);
static void Main(string[] args)
{
//调用不带参数的存储过程
//CallProcedureGetAll();
//调用带输入参数的存储过程
//CallProcedureInsert();
//调用带输入和输出参数的存储过程
//CallProcedureLogin();
} static void CallProcedureGetAll()
{
string sql = "forAccountGetAll";//存储过程名字 SqlDataAdapter da = new SqlDataAdapter(sql, conn);
da.SelectCommand.CommandType = CommandType.StoredProcedure;//指定调用存储过程(默认是SQL文本) DataSet ds = new DataSet();
da.Fill(ds, "AccountGetAll"); DataTable dt = ds.Tables["AccountGetAll"];
DataView dv = new DataView(dt); dv.Sort = "AccountID ASC"; Console.WriteLine("调用存储过程 forAccountGetAll:"); foreach (DataRowView drv in dv)
{
Console.WriteLine("{0}:{1},{2}", drv["AccountID"], drv["AccountName"], drv["password"]);
}
} static void CallProcedureInsert()
{
string sql = "forAccountInsert";
SqlCommand cmd = new SqlCommand(sql, conn); cmd.CommandType = CommandType.StoredProcedure;
//设置参数值,并添加到Command对象的参数集合中
cmd.Parameters.AddWithValue("@AccountID", );
cmd.Parameters.AddWithValue("@AccountName", "new");
cmd.Parameters.AddWithValue("@password", ""); conn.Open();
cmd.ExecuteNonQuery();
conn.Close();
} static void CallProcedureLogin()
{
string sql = "forAccountLogin";
SqlCommand cmd = new SqlCommand(sql, conn); cmd.CommandType = CommandType.StoredProcedure;
//设置输入参数值
cmd.Parameters.AddWithValue("@AccountName", "Joe");
cmd.Parameters.AddWithValue("@password", "");
//准备输出参数
SqlParameter param = new SqlParameter("@AccountID", SqlDbType.Int);
param.Direction = ParameterDirection.Output;//指定是输出参数 cmd.Parameters.Add(param); // 将输出参数添加到Command对象的参数集合中 conn.Open();
cmd.ExecuteNonQuery();
//如果没有满足条件的用户名和密码,那么输出参数@AccountID的值将为DBNull.Value
if (param.Value == DBNull.Value)
{
Console.WriteLine("failed");
}
else
{
Console.WriteLine("Done");
}
conn.Close();
}
}
}