C# SQL,多个命令
C# SQL, Multiple Commands
我正在尝试编写一个应该与数据库通信的方法,但我不确定我的方法是否正确。
public void dbWorkerLogin(int workerNumber) {
// Connection string stored in "conn"
if (!new SqlCommand("Some Command WHERE id=" +workernumber,conn).executeReader().HasRows)
{
new SqlCommand("exec STORED_PROCEDURE1 " + workerNumber, conn).ExecuteNonQuery();
new SqlCommand("exec STORED_PROCEDURE2 " + workerNumber, conn).ExecuteNonQuery();
}
else
{
new SqlCommand("exec STORED_PROCEDURE3 " + workerNumber,conn).ExecuteNonQuerry();
}
1)这样写,每个SqlCommand都以关键字new开始,这样可以吗?或者我应该做类似的事情:
SqlCommand command = new SqlCommand(null, conn);
command = ...;
然后回收变量'command'还是这样?
using(SqlCommand cmd = new SqlCommand("COMMAND", conn);
2) 我的程序是否有效,或者我应该使用 SqlCommand.Prepare() 函数将我的数据转换为正确的数据类型?例如。 workerNumber 是整数,但在数据库中它存储为十进制。
using (SqlCommand cmd = new SqlCommand("STORED_PROCEDURE", conn))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parametres.Add("@id", SqlDbType.Decimal).Value = workNumber;
cmd.Prepare();
cmd.ExecuteNonQuery();
}
能否请您总结一下使用什么,最好不要使用什么?不幸的是,由于对数据库的访问受限,我无法测试第一个代码,所以我不确定它是否可以无错执行。
感谢您对此主题的任何帮助!
编辑:
几个小时后,我到达了这个阶段:
public int getWorkerNumber(string uniqueID)
{
using (conn = new SqlConnection(ConfigurationManager.ConnectionStrings["dbConnect"].ConnectionString))
{
conn.Open();
using (SqlCommand cmd = new SqlCommand("SELECT number FROM worker WHERE workerID = @id",conn))
{
cmd.Parameters.Add("@id", SqlDbType.Decimal).Value = uniqueID;
using (SqlDataReader reader = cmd.ExecuteReader())
{
int answer;
while (reader.Read())
{
answer = (int)reader.GetDecimal(0);
}
return answer;
}
}
}
}
还有这个:
public string dbLoginWorker(int workerNumber)
{
SqlCommand cmd;
SqlDataReader reader;
using (conn = new SqlConnection(ConfigurationManager.ConnectionStrings["dbConnect"].ConnectionString))
{
conn.Open();
cmd = new SqlCommand("SELECT column FROM table WHERE id= @workernumber", conn);
cmd.Parameters.Add("@workernumber", SqlDbType.Decimal).Value = workerNumber;
reader = cmd.ExecuteReader();
if (!reader.HasRows)
{
cmd = new SqlCommand("STORED_PROCEDURE1", conn);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("@ID", SqlDbType.Decimal).Value = workerNumber;
cmd.Parameters.Add("@VARCHAR", SqlDbType.VarChar).Value = "text";
cmd.Prepare();
reader.Close();
cmd.ExecuteNonQuery();
cmd.Dispose();
reader.Dispose();
return "procedure 1 executed";
else
{
cmd = new SqlCommand("STORED_PROCEDURE2", conn);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("@ID", SqlDbType.Decimal).Value = workerNumber;
cmd.Parameters.Add("@INT", SqlDbType.SmallInt).Value = 1;
cmd.Parameters.Add("@VARCHAR", SqlDbType.VarChar).Value = "text";
cmd.Prepare();
reader.Close();
cmd.ExecuteNonQuery();
cmd.Dispose();
reader.Dispose();
return "procedure 2 executed";
}
}
}
两种方法都有效(如果我在重写时没有弄错的话:))。我不确定这些方法中的哪一种(第一种或第二种)在稳定性方面更好,以及这种方法是否更好并且更能抵抗 SQL 注入。有人可以对此主题发表评论吗?再次感谢您的帮助!
1) 最好尽可能始终使用 USING
块。这包括 SqlConnection
、SqlCommand
、SqlReader
和其他实现 IDisposable 的对象。 USING
块自动关闭并处理对象,因此您不必这样做。
2) 我相信您在错误的地方使用了 Prepare() 方法。查看以下 Whosebug 文章以了解正确用法:
PrepareMethodInstructions。
3) 在dbLoginWorker()
方法中,第一个查询只是用来判断是否找到行。因此,我建议将SELECT
命令改为SELECT TOP 1 column FROM table WHERE id= @workernumber
,这样查询会更快更高效。
4) 我认为您的命令不会受到 SQL 注入攻击,因为它们已完全参数化。做得很好。
5) 作为一般想法,我建议阅读重构技术。您的 dbLoginWorker()
方法可以变得更具可读性和可维护性,以及自我记录,如果您创建三个额外的方法,一个用于每个 SQL 命令,并适当地命名它们。您还可以设置一种基于连接名称创建连接的方法,并且您不会有那么多重复代码。例如:
public static SqlConnection GetConnection(string connectionName)
{
SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings[connectionName].ConnectionString);
conn.Open();
return conn;
}
public string dbLoginWorker(int workerNumber)
{
using (conn = GetConnection("dbConnect"))
{
if (CanFindWorkerNumber(conn, workerNumber))
ExecuteProcedure1(conn);
else
ExecuteProcedure2(conn);
}
}
public bool CanFindWorkerNumber (SqlConnection conn, int workerNumber)
{
bool success = false;
using (SqlCommand cmd = new SqlCommand("SELECT TOP 1 column FROM table WHERE id= @workernumber", conn))
{
cmd.Parameters.Add("@workernumber", SqlDbType.Decimal);
cmd.Prepare();
cmd.Parameters[0].Value = workerNumber;
success = cmd.ExecuteScalar() != null;
}
return success;
}
public void ExecuteProcedure1(SqlConnection conn)
{
using (SqlCommand cmd = new SqlCommand("STORED_PROCEDURE1", conn))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("@ID", SqlDbType.Decimal);
cmd.Parameters.Add("@VARCHAR", SqlDbType.VarChar);
cmd.Prepare();
cmd.Parameters[0].Value = workerNumber;
cmd.Parameters[1].Value = "text";
cmd.ExecuteNonQuery();
}
}
public void ExecuteProcedure1(SqlConnection conn)
{
using (SqlCommand cmd = new SqlCommand("STORED_PROCEDURE1", conn))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("@ID", SqlDbType.Decimal);
cmd.Parameters.Add("@INT", SqlDbType.SmallInt).Value);
cmd.Parameters.Add("@VARCHAR", SqlDbType.VarChar);
cmd.Prepare();
cmd.Parameters[0] = workerNumber;
cmd.Parameters[1] = 1;
cmd.Parameters[2] = "text";
cmd.ExecuteNonQuery();
}
}
您实际上可以在一个 SQL 推荐中做到这一点。现在你拉回一个结果集只是为了看它是否有行,然后根据它执行不同的命令。您应该能够在一个命令中执行此操作,并适当地处理它和连接:
var sql =
@"
IF EXISTS(Some Command WHERE id=@workernumber)
BEGIN
exec STORED_PROCEDURE1 @workernumber;
exec STORED_PROCEDURE2 @workernumber;
END
ELSE
exec STORED_PROCEDURE3 @workernumber;
";
请注意,您不易受到 SQL 注入攻击,因为您处理的不是字符串,而是整数。
我正在尝试编写一个应该与数据库通信的方法,但我不确定我的方法是否正确。
public void dbWorkerLogin(int workerNumber) {
// Connection string stored in "conn"
if (!new SqlCommand("Some Command WHERE id=" +workernumber,conn).executeReader().HasRows)
{
new SqlCommand("exec STORED_PROCEDURE1 " + workerNumber, conn).ExecuteNonQuery();
new SqlCommand("exec STORED_PROCEDURE2 " + workerNumber, conn).ExecuteNonQuery();
}
else
{
new SqlCommand("exec STORED_PROCEDURE3 " + workerNumber,conn).ExecuteNonQuerry();
}
1)这样写,每个SqlCommand都以关键字new开始,这样可以吗?或者我应该做类似的事情:
SqlCommand command = new SqlCommand(null, conn);
command = ...;
然后回收变量'command'还是这样?
using(SqlCommand cmd = new SqlCommand("COMMAND", conn);
2) 我的程序是否有效,或者我应该使用 SqlCommand.Prepare() 函数将我的数据转换为正确的数据类型?例如。 workerNumber 是整数,但在数据库中它存储为十进制。
using (SqlCommand cmd = new SqlCommand("STORED_PROCEDURE", conn))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parametres.Add("@id", SqlDbType.Decimal).Value = workNumber;
cmd.Prepare();
cmd.ExecuteNonQuery();
}
能否请您总结一下使用什么,最好不要使用什么?不幸的是,由于对数据库的访问受限,我无法测试第一个代码,所以我不确定它是否可以无错执行。 感谢您对此主题的任何帮助!
编辑: 几个小时后,我到达了这个阶段:
public int getWorkerNumber(string uniqueID)
{
using (conn = new SqlConnection(ConfigurationManager.ConnectionStrings["dbConnect"].ConnectionString))
{
conn.Open();
using (SqlCommand cmd = new SqlCommand("SELECT number FROM worker WHERE workerID = @id",conn))
{
cmd.Parameters.Add("@id", SqlDbType.Decimal).Value = uniqueID;
using (SqlDataReader reader = cmd.ExecuteReader())
{
int answer;
while (reader.Read())
{
answer = (int)reader.GetDecimal(0);
}
return answer;
}
}
}
}
还有这个:
public string dbLoginWorker(int workerNumber)
{
SqlCommand cmd;
SqlDataReader reader;
using (conn = new SqlConnection(ConfigurationManager.ConnectionStrings["dbConnect"].ConnectionString))
{
conn.Open();
cmd = new SqlCommand("SELECT column FROM table WHERE id= @workernumber", conn);
cmd.Parameters.Add("@workernumber", SqlDbType.Decimal).Value = workerNumber;
reader = cmd.ExecuteReader();
if (!reader.HasRows)
{
cmd = new SqlCommand("STORED_PROCEDURE1", conn);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("@ID", SqlDbType.Decimal).Value = workerNumber;
cmd.Parameters.Add("@VARCHAR", SqlDbType.VarChar).Value = "text";
cmd.Prepare();
reader.Close();
cmd.ExecuteNonQuery();
cmd.Dispose();
reader.Dispose();
return "procedure 1 executed";
else
{
cmd = new SqlCommand("STORED_PROCEDURE2", conn);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("@ID", SqlDbType.Decimal).Value = workerNumber;
cmd.Parameters.Add("@INT", SqlDbType.SmallInt).Value = 1;
cmd.Parameters.Add("@VARCHAR", SqlDbType.VarChar).Value = "text";
cmd.Prepare();
reader.Close();
cmd.ExecuteNonQuery();
cmd.Dispose();
reader.Dispose();
return "procedure 2 executed";
}
}
}
两种方法都有效(如果我在重写时没有弄错的话:))。我不确定这些方法中的哪一种(第一种或第二种)在稳定性方面更好,以及这种方法是否更好并且更能抵抗 SQL 注入。有人可以对此主题发表评论吗?再次感谢您的帮助!
1) 最好尽可能始终使用 USING
块。这包括 SqlConnection
、SqlCommand
、SqlReader
和其他实现 IDisposable 的对象。 USING
块自动关闭并处理对象,因此您不必这样做。
2) 我相信您在错误的地方使用了 Prepare() 方法。查看以下 Whosebug 文章以了解正确用法: PrepareMethodInstructions。
3) 在dbLoginWorker()
方法中,第一个查询只是用来判断是否找到行。因此,我建议将SELECT
命令改为SELECT TOP 1 column FROM table WHERE id= @workernumber
,这样查询会更快更高效。
4) 我认为您的命令不会受到 SQL 注入攻击,因为它们已完全参数化。做得很好。
5) 作为一般想法,我建议阅读重构技术。您的 dbLoginWorker()
方法可以变得更具可读性和可维护性,以及自我记录,如果您创建三个额外的方法,一个用于每个 SQL 命令,并适当地命名它们。您还可以设置一种基于连接名称创建连接的方法,并且您不会有那么多重复代码。例如:
public static SqlConnection GetConnection(string connectionName)
{
SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings[connectionName].ConnectionString);
conn.Open();
return conn;
}
public string dbLoginWorker(int workerNumber)
{
using (conn = GetConnection("dbConnect"))
{
if (CanFindWorkerNumber(conn, workerNumber))
ExecuteProcedure1(conn);
else
ExecuteProcedure2(conn);
}
}
public bool CanFindWorkerNumber (SqlConnection conn, int workerNumber)
{
bool success = false;
using (SqlCommand cmd = new SqlCommand("SELECT TOP 1 column FROM table WHERE id= @workernumber", conn))
{
cmd.Parameters.Add("@workernumber", SqlDbType.Decimal);
cmd.Prepare();
cmd.Parameters[0].Value = workerNumber;
success = cmd.ExecuteScalar() != null;
}
return success;
}
public void ExecuteProcedure1(SqlConnection conn)
{
using (SqlCommand cmd = new SqlCommand("STORED_PROCEDURE1", conn))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("@ID", SqlDbType.Decimal);
cmd.Parameters.Add("@VARCHAR", SqlDbType.VarChar);
cmd.Prepare();
cmd.Parameters[0].Value = workerNumber;
cmd.Parameters[1].Value = "text";
cmd.ExecuteNonQuery();
}
}
public void ExecuteProcedure1(SqlConnection conn)
{
using (SqlCommand cmd = new SqlCommand("STORED_PROCEDURE1", conn))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("@ID", SqlDbType.Decimal);
cmd.Parameters.Add("@INT", SqlDbType.SmallInt).Value);
cmd.Parameters.Add("@VARCHAR", SqlDbType.VarChar);
cmd.Prepare();
cmd.Parameters[0] = workerNumber;
cmd.Parameters[1] = 1;
cmd.Parameters[2] = "text";
cmd.ExecuteNonQuery();
}
}
您实际上可以在一个 SQL 推荐中做到这一点。现在你拉回一个结果集只是为了看它是否有行,然后根据它执行不同的命令。您应该能够在一个命令中执行此操作,并适当地处理它和连接:
var sql =
@"
IF EXISTS(Some Command WHERE id=@workernumber)
BEGIN
exec STORED_PROCEDURE1 @workernumber;
exec STORED_PROCEDURE2 @workernumber;
END
ELSE
exec STORED_PROCEDURE3 @workernumber;
";
请注意,您不易受到 SQL 注入攻击,因为您处理的不是字符串,而是整数。