在C#中,在执行异步激发时省略wait语句,而忘记使用ado.net写入数据库,安全吗
在asp.net应用程序中,我希望尽可能高效地登录到数据库。我正在使用基本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
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();
});