3

在 asp.net 应用程序中,我想尽可能高效地登录到数据库。我正在使用基本的 ADO.NET 写入日志数据库,并且我想异步执行此操作,所以这就是我所做的:

        using (var conn = new SqlConnection(_connectionString)) {
            using (var cmd = new SqlCommand("INSERT INTO dbo.Logs (TimeStamp,ThreadId,Level,Message,Exception) VALUES (@TimeStamp,@ThreadId,@Level,@Message,@Exception)", conn)) {
                cmd.Parameters.AddWithValue("@TimeStamp", DateTime.UtcNow);
                cmd.Parameters.AddWithValue("@ThreadId", Thread.CurrentThread.ManagedThreadId);
                cmd.Parameters.AddWithValue("@Level", level);
                cmd.Parameters.AddWithValue("@Message", msg);
                cmd.Parameters.AddWithValue("@Exception", ex == null ? "" : ex.ToString());
                conn.Open();
                cmd.ExecuteNonQueryAsync();
            }
        }

现在我的问题是我是否应该对 cmd.ExecuteNonQueryAsync() 语句执行等待,或者是否可以省略等待,因为我基本上只需要执行一次即发即弃。

4

3 回答 3

8

仅仅触发并忘记异步操作是不行的——它可能会因异常而失败,在这种情况下,您几乎肯定想要做一些事情(通知用户/重试/炸毁)。(此外,如果有人使用特定选项,任务中的异常最终会导致进程中断)。

另外:谁负责关闭连接?

编辑:为了使连接关闭的问题更清楚,我将用try/重写 using 语句finally(我知道它不是完全相同的 IL,但它足够接近以查看问题所在) - 在这种情况下,代码大致变成:

SqlConnection conn;
try {
    conn = new SqlConnection("connString");
    SqlCommand cmd;
    try {
        cmd = new SqlCommand("INSERT INTO dbo.Logs (TimeStamp,ThreadId,Level,Message,Exception) VALUES (@TimeStamp,@ThreadId,@Level,@Message,@Exception)", conn);
        cmd.AddParameters();
        conn.Open();
        cmd.ExecuteNonQueryAsync();
    } finally {
        if (cmd != null) cmd.Dispose();
 } finally {
    if (conn != null) conn.Close();
 }

您可以看到它cmd.Dispose()是在 之后调用的cmd.ExecuteNonQueryAsync()。这怎么能行?我看到两种可能性:

  1. 要么cmd.Dispose()(通过设计或意外)在能够完成工作之前不会返回cmd.ExecuteNonQueryAsync- 这意味着您实际上只是从cmd.Disposeafter回来cmd.ExecuteNonQueryAsync;在这种情况下,代码可以工作,但您不会从等待/异步中受益;
  2. 或者以阻止完成cmd.Dispose()的方式执行;cmd.ExecuteNonQueryAsync()在这种情况下,代码不起作用;

在这两种情况下,程序都是错误的——你需要或者await调用Wait()任务以确保它的行为正确。

于 2013-01-05T20:00:48.727 回答
3

正如其他人所提到的,强烈考虑一个稳定的、经过良好测试的库,例如 ELMAH 或 log4net。如果您决定自己推出,请考虑使用基于 ETW 的解决方案,该解决方案的开销要少得多,并且可以轻松包含相关的 OS/.NET/IIS/ASP.NET 事件。我已经完成了对数据库的日志记录;这听起来像是个好主意,但你会遇到后勤问题。

要回答您的实际问题,await如果您需要对错误做出响应,则最好这样做。如果这真的是一劳永逸,那么您可以忽略Task结果。如果你的方法是async,编译器会在你忽略 a 时警告你Task;为避免警告,您可以将其分配给未使用的变量:

var _ = cmd.ExecuteNonQueryAsync();
于 2013-01-06T00:10:51.243 回答
2

最简单的方法是对同步操作使用 Task.Run() 进行即发即弃。

例如,

Task.Run(() =>
{
     LogDatabase();

});

其中 LogDatabase() 是与 ExecuteNonQuery() 同步的标准 ADO.NET。

于 2013-06-25T03:06:16.000 回答