【问题标题】:C# SQL, Multiple CommandsC# SQL,多个命令
【发布时间】:2015-08-10 10:33:59
【问题描述】:

我正在尝试编写一个应该与数据库通信的方法,但我不确定我的方法是否正确。

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) 这样写并用关键字new开始每个SqlCommand可以吗?或者我应该这样做:

SqlCommand command = new SqlCommand(null, conn);
command = ...;

然后回收变量'command'还是这样?

using(SqlCommand cmd = new SqlCommand("COMMAND", conn);

2) 我的程序能正常工作还是应该使用 SqlCommand.Prepare() 函数将我的数据转换为正确的数据类型?例如。 workerNumber 是 int,但在数据库中存储为十进制。

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 注入。有人可以评论这个问题吗?再次感谢您的帮助!

【问题讨论】:

  • 试一试并发布错误
  • dbWorkerLogin 容易受到 SQL Injection 的攻击,因为您在此处允许未经处理的输入 "Some Command WHERE id=" +workernumber"

标签: c# sql stored-procedures prepared-statement


【解决方案1】:

1) 最好尽可能使用USING 块。这包括SqlConnectionSqlCommandSqlReader 和其他实现 IDisposable 的对象。 USING 块会自动关闭并处理对象,因此您不必这样做。

2) 我认为您在错误的地方使用了 Prepare() 方法。查看以下 StackOverflow 文章以了解正确用法: 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();
    }
}

【讨论】:

    【解决方案2】:

    您实际上可以在一个 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 注入的攻击,因为您处理的不是字符串,而是整数。

    【讨论】:

      猜你喜欢
      • 2018-11-21
      • 1970-01-01
      • 1970-01-01
      • 2016-09-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多