在C#中,在执行异步激发时省略wait语句,而忘记使用ado.net写入数据库,安全吗

在C#中,在执行异步激发时省略wait语句,而忘记使用ado.net写入数据库,安全吗,c#,asp.net,asynchronous,ado.net,C#,Asp.net,Asynchronous,Ado.net,在asp.net应用程序中,我希望尽可能高效地登录到数据库。我正在使用基本ADO.NET写入日志数据库,我想异步执行此操作,因此我要做的是: using (var conn = new SqlConnection(_connectionString)) { using (var cmd = new SqlCommand("INSERT INTO dbo.Logs (TimeStamp,ThreadId,Level,Message,Exception) VA

在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()语句执行wait,或者省略wait是否可以,因为我基本上只需要执行一个fire and forget操作。

只执行fire and forget异步操作是不可以的-它可能会因异常而失败,并且您几乎肯定会想做些什么(通知用户/重试/放大)在这种情况下。(此外,如果有人使用特定选项,任务中的异常将最终导致进程停止)

另外:谁负责关闭连接

编辑:为了使关闭连接的问题更清楚,我将用
try
/
重写using语句,最后
(我知道它不是完全相同的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()
之后被调用。这是如何工作的?我看到了两种可能性:

  • cmd.Dispose()
    (出于设计或意外)在
    cmd.ExecuteNonQueryAsync
    能够完成其工作之前不会返回,这意味着您实际上只能在
    cmd.ExecuteNonQueryAsync
    之后从
    cmd.Dispose
    返回;在这种情况下,代码可以工作,但您不能从wait/async中获益
  • 或者执行
    cmd.Dispose()
    的方式会阻止
    cmd.ExecuteNonQueryAsync()
    完成;在这种情况下,代码不起作用

  • 在这两种情况下,程序都是错误的-您需要
    Wait
    或调用
    Wait()在任务中,为了确保它的行为正确。

    < P>正如其他人提到的,强烈地考虑一个稳定的、经过测试的库,例如ELMAH或Log4net。如果你决定自己滚动,考虑一个AN,它开销小得多,并且可以容易地包括相关的OS/NET/IIS/ASP.NET事件。这听起来是个好主意,但你会遇到后勤问题

    为了回答您的实际问题,如果您需要对错误做出响应,最好是等待它。如果这是真正的激发和遗忘,那么您可以忽略任务结果。如果您的方法是异步的,编译器将在您忽略任务时向您发出警告;为了避免警告,您可以将它分配给一个un使用的变量:

    var _ = cmd.ExecuteNonQueryAsync();
    

    最简单的方法是在同步操作上使用Task.Run()执行激发和遗忘操作

    例如:


    其中LogDatabase()是ExecuteOnQuery()的同步标准ADO.NET.

    如果您希望尽可能高效地执行此操作,我可能会使用单独的后台线程将日志存储在内存中,然后偶尔一次发送一组日志,以减少频繁打开/关闭连接的需要,并减少事务数。编辑:或者更好,而不是滚动您自己的日志(这可能充满陷阱),利用一个久经考验的真实库,如Log4Net:它已经为您实现了这一点)ASP.NET将在每个请求结束时为您自动阻止,即使您忘记包含等待。最好的方法是启动另一个工作线程(通过Task.Run或QueueBackgroundWorkItem)当外部using语句超出作用域时,连接关闭。但是
    using
    语句超出作用域,而
    任务
    可能仍在运行,不是吗?或者是这样,或者您使用
    异步
    变量实际上没有获得任何结果。我不确定。我已决定添加一个等待以确保that使用语句的范围按预期工作。很好的解释-它是有意义的。谢谢。
    Task.Run(() =>
    {
         LogDatabase();
    
    });