【问题标题】:Handling Database Connection in C#在 C# 中处理数据库连接
【发布时间】:2018-09-05 13:53:56
【问题描述】:

我正在使用 C# 和 SqlServer 创建 WinForm 应用程序。我必须在上面处理许多数据库 CRUD 查询。而且还有很多表格和很多控制器。

现在我想知道的是,如果我使用许多方法创建用于处理数据库连接的通用类,用于打开连接、关闭连接、执行 Sql 命令或进行任何其他数据检索。这种方法是好是坏?

或以下运行每个查询的方法是好是坏?

using (SqlConnection connection = new SqlConnection("Integrated Security=SSPI;Initial Catalog=MYDB"))  
{  
    connection.Open();        
    // Pool A is created.  
}  

哪种方法的性能和安全性更好?

【问题讨论】:

标签: c# sqlconnection


【解决方案1】:

使用连接时需要考虑以下几点。

1) 使用 using 语句在不再需要连接对象时立即处理它:

using (var conn = new SqlConnection(connectionstring))  
{  
    // your sql magic goes here
} 

2) 如果您不立即释放对象,您可以使用 try-finally 语句确保连接已关闭:

var conn = new SqlConnection(connectionstring);
try
{
// do sql shizzle
}
finally
{
    conn.Close();
}           

3) 为防止 SQL 注入,使用参数化查询,切勿串联字符串

using (var conn = new SqlConnection(connectionstring))  
{  
    conn.Open();
    using(var comm = new SqlCommand("select * from FooBar where foo = @foo", conn))
    {
        comm.Parameters.Add(new SqlParameter("@foo", "bar"));
        // also possible:
        // comm.Parameters.AddWithValue("@foo", "bar");
        using(var reader = comm.ExecuteReader())
        {
            // Do stuff with the reader;
        }
    }
} 

4) 如果您正在执行多个更新、插入或删除语句,并且它们都需要一次成功,请使用事务:

using (var conn = new SqlConnection(connectionstring))  
{  
    conn.Open();
    using(var trans = conn.BeginTransaction())
    {
        try 
        {
            using(var comm = new SqlCommand("delete from FooBar where fooId = @foo", conn, trans))
            {
                comm.Parameters.Add(new SqlParameter { ParameterName = "@foo", DbType = System.Data.DbType.Int32 });
                for(int i = 0; i < 10 ; i++)
                {
                    comm.Parameters["@foo"].Value = i;
                    comm.ExecuteNonQuery();
                }
            }
            trans.Commit();
        }
        catch (Exception exe)
        {
            trans.Rollback();
            // do some logging
        }
    }
} 

5) 存储过程的用法类似:

using (var conn = new SqlConnection(connectionstring))
{
    conn.Open();
    using (var comm = new SqlCommand("FooBarProcedure", conn) { CommandType = CommandType.StoredProcedure }) 
    {
         comm.Parameters.Add(new SqlParameter("@FooBar", "shizzle"));
         comm.ExecuteNonQuery();
    }
}

(源存储过程:this Answer

多线程:使用多线程和 SQL 连接的最安全方法是始终关闭并释放连接对象。这是 SqlConnection 设计的行为。 (来源:Answer John Skeet

【讨论】:

  • 你是个好人。在这个答案中,我必须获得比我整个学期的知识更多的知识。谢谢你,兄弟。但我有一些问题。 1)如果使用存储过程来处理CRUD,是好是坏? 2) 如果这种连接池方法可以帮助我处理多线程?
【解决方案2】:

最佳实践是创建一个通用 DBHelper 类并在该类中创建 CRUD 方法。 我正在添加代码 sn-p。这可能会对您有所帮助。

web.config

<connectionStrings>
    <add name="mssqltips"
         connectionString="data source=localhost;initial catalog=mssqltips;Integrated Security=SSPI;"
         providerName="System.Data.SqlClient" />
  </connectionStrings>

DBHelper.cs

//Opening Connection
public SqlConnection GetConnection(string connectionName)
{
  string cnstr = ConfigurationManager.ConnectionStrings[connectionName].ConnectionString;
  SqlConnection cn = new SqlConnection(cnstr);
  cn.Open();
  return cn;
}
//for select 
public DataSet ExecuteQuery(
  string connectionName,
  string storedProcName,
  Dictionary<string, sqlparameter=""> procParameters
)
{
  DataSet ds = new DataSet();
  using(SqlConnection cn = GetConnection(connectionName))
  {
      using(SqlCommand cmd = cn.CreateCommand())
      {
          cmd.CommandType = CommandType.StoredProcedure;
          cmd.CommandText = storedProcName;
          // assign parameters passed in to the command
          foreach (var procParameter in procParameters)
          {
            cmd.Parameters.Add(procParameter.Value);
          }
          using (SqlDataAdapter da = new SqlDataAdapter(cmd))
          {
            da.Fill(ds);
          }
      }
 }
 return ds;
}

//for insert,update,delete

public int ExecuteCommand(
  string connectionName,
  string storedProcName,
  Dictionary<string, SqlParameter> procParameters
)
{
  int rc;
  using (SqlConnection cn = GetConnection(connectionName))
  {
    // create a SQL command to execute the stored procedure
    using (SqlCommand cmd = cn.CreateCommand())
    {
      cmd.CommandType = CommandType.StoredProcedure;
      cmd.CommandText = storedProcName;
      // assign parameters passed in to the command
      foreach (var procParameter in procParameters)
      {
        cmd.Parameters.Add(procParameter.Value);
      }
      rc = cmd.ExecuteNonQuery();
    }
  }
  return rc;
}

【讨论】:

  • 让公共方法返回一个打开的 SqlConnection 几乎不是最佳实践...
【解决方案3】:

如果您不想每次都释放上下文,您可以创建存储库类并在其中注入 SqlConnection。

using (SqlConnection connection = new SqlConnection("Integrated Security=SSPI;Initial Catalog=MYDB"))  
{  
    repository.SetConnection(connection);
    var values = repository.GetSomething();
} 

并创建类:

public Class Repository
{
   private SqlConnection _connection {get; set;}

   public void SetConnection(SetConnection connection)
   {
        _connection = connection;
   }

   public string GetSomething()
   {
       _connection.Open();
       //do stuff with _connection
       _connection.Close();
   }
}

无论如何,我建议您阅读有关 ORM(实体框架或 Dapper)和 SQL 注入攻击的信息。

【讨论】:

  • 这个存储库是单例还是每个依赖?它是线程安全的吗?
  • 这很好,但如果它不仅仅是一个大学项目,请先阅读有关 SQL 注入攻击的信息。 @Vishal Pawar 提出了非常好的解决方案,他通过存储过程准备数据,这是从 db 获取信息的好方法。他还从web.config中获取了连接字符串,这也是一件好事(比我的解决方案好)
  • @John 这是我的答案,不是生产代码 :) 对于 WinForm 单例存储库确实应该是一个不错的选择
  • 我的意思是这在多线程环境中会失败。它是一个 WinForms 应用程序并不意味着它不是一个多线程环境。
  • @Arkadiusz 这只是一个大学项目,有一些多线程,还想一次从多个表中获取数据。进行连接的最佳方式是什么?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-12-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-03-16
相关资源
最近更新 更多