我有一个类,包含填充DropDowns,返回DataSet,返回Scalar或只是执行查询的方法。在StackOverflow中的older posts之一,我提交了同一类的错误代码。根据贡献者的建议,我改进了代码并想知道这个类是否适合在高并发环境中使用:
public sealed class reuse
{
public void FillDropDownList(string Query, DropDownList DropDownName)
{
using (TransactionScope transactionScope = new TransactionScope())
{
using (SqlConnection con = new SqlConnection(ConfigurationManager.ConnectionStrings["MyDbConnection"].ConnectionString.ToString()))
{
SqlDataReader dr;
try
{
if (DropDownName.Items.Count > 0)
DropDownName.Items.Clear();
SqlCommand cmd = new SqlCommand(Query, con);
dr = cmd.ExecuteReader();
while (dr.Read())
DropDownName.Items.Add(dr[0].ToString());
dr.Close();
}
catch (Exception ex)
{
CustomErrorHandler.GetScript(HttpContext.Current.Response,ex.Message.ToString());
}
}
}
}
}
我想知道是否同时处理Command和DataReader对象,或者它们也会自动处理USING?
答案 0 :(得分:3)
命令/阅读器:他们将通过“使用”处理,但仅当你使用“使用”时,你应该这样做。
批评:
坦率地说,我只是在这里使用短小精悍,并避免所有这些问题:
using(var connection = Config.OpenConnection()) {
return connection.Query<string>(tsql, args).ToString();
}
(让调用者遍历列表,或使用AddRange或数据绑定,无论如何)
答案 1 :(得分:1)
一般同意Marc的回答,但我还有其他一些评论和不同的角度。希望我的回答对你有用。
首先,只要不需要任何状态信息且不共享数据,在并发环境中使用静态类和方法就没有错误。在你的情况下,填写DropDownList,它是完全正常的,因为你只需要一个字符串列表,一旦完成,你可以忘记所有你如何得到它。如果静态方法不访问任何静态字段,则对静态方法的并发调用之间也不会产生干扰。静态方法在.NET框架中很常见,并且它们是线程安全的。
在下面的示例中,我使用一个静态字段 - log4net记录器。它仍然是线程安全的,因为它不带任何状态,只是log4net库的跳转点,它本身就是线程安全的。建议至少查看log4net - 很棒的日志库。
如果您尝试从两个线程填充相同的下拉列表,那么它可能只是不安全,但如果此类不是静态的,那么它也将是不安全的。确保从一个(主)线程填充下拉列表。
返回您的代码。 混合UI和数据检索不是一种好的做法,因为它使代码的可维护性降低,稳定性降低。将这两者分开。 Dapper库可能是简化事物的好方法。我自己没有使用它,所以我能说的是它看起来非常方便和有效。如果你想/需要了解工作原理,请不要使用它。至少不是一开始。
在一个字符串中进行非参数化查询可能会导致SQL注入攻击,但如果该查询不是基于任何直接用户输入构建的,那么它应该是安全的。当然,你总是可以采用参数化来确定。
使用
处理异常CustomErrorHandler.GetScript(HttpContext.Current.Response, ex.Message.ToString());
对于这个地方感觉片状和太复杂,可能会导致另一个例外。处理另一个异常时的异常意味着恐慌。我会把那个代码移到外面。如果你在这里需要一些东西,那就让它成为一个简单的log4net错误日志并重新抛出异常。
如果您只执行一次数据库读取,则不需要显式事务。 根据连接对象,它在任何情况下都不应该是静态的,并且可以按需创建。没有性能损失,因为.NET保留了一个准备使用连接池并回收那些'设置”。
我相信一个例子总是比解释更好,所以这里是我如何重新安排你的代码。
public static class reuse
{
static public readonly log4net.ILog log = log4net.LogManager.GetLogger("GeneralLog");
public static void FillDropDownList(string query, string[] parms, DropDownList dropDown)
{
dropDown.Items.Clear();
dropDown.DataSource = GetData(query, parms);
dropDown.DataBind();
}
private static IEnumerable<string> GetData(string query, string[] parms)
{
using (SqlConnection con = new SqlConnection(GetConnString()))
{
try
{
List<string> result = new List<string>();
SqlCommand cmd = new SqlCommand(query, con);
cmd.Parameters.AddRange(parms);
SqlDataReader dr = cmd.ExecuteReader();
if (dr.VisibleFieldCount > 0)
{
while (dr.Read())
result.Add(dr[0].ToString());
}
dr.Close();
return result;
}
catch (Exception ex)
{
log.Error("Exception in GetData()", ex);
throw;
}
}
}
private static string GetConnString()
{
return ConfigurationManager.ConnectionStrings["MyDbConnection"].ConnectionString.ToString(CultureInfo.InvariantCulture);
}
}