我做了这个hacky解决方法,为我的一些异步api调用添加前/后日志记录。
是否有更简洁/更好的方法为void Task编写重载?
这种方法总体上有什么问题吗?任何建议的更改?
public static Task<TResult> LogAsyncTask<TResult>
(this Task<TResult> task, string messageTemplate,
LogEventLevel level = LogEventLevel.Information)
{
Log.Write(level, "Async Starting: " + messageTemplate);
return task.ContinueWith(antecedent =>
{
var result = antecedent.GetAwaiter().GetResult();
Log.Write(level, "Async Finished: " + messageTemplate);
return result;
});
}
public static Task<object> LogAsyncTask
(this Task task, string messageTemplate,
LogEventLevel level = LogEventLevel.Information)
=> Task<object>.Factory.StartNew(() =>
{
task.GetAwaiter().GetResult();
return null;
}).LogAsyncTask(messageTemplate, level);
以下是您的称呼方式:
await apiClient.MySoapMethodAsync().LogAsyncTask("Doing MySoapMethod...");
答案 0 :(得分:0)
正如Damien正确指出的那样,你的代码有一个问题,它只能记录&#34; Async Starting&#34; (并且可能启动计时器)在异步方法的同步部分完成后(并返回任务)。一个适当的解决方案将使用面向方面的框架,如Fody或PostSharp;你可以开始使用my AsyncDiagnostics project,它可以做一些非常相似的事情。
如果您确实采用当前的方法,则不应使用ContinueWith
或StartNew
。此外,无论例外如何,都应确保写入最终日志:
public static async Task<TResult> LogAsyncTask<TResult>(
this Task<TResult> task, string messageTemplate,
LogEventLevel level = LogEventLevel.Information)
{
Log.Write(level, "Async Starting: " + messageTemplate);
try
{
return await task.ConfigureAwait(false);
}
finally
{
Log.Write(level, "Async Finished: " + messageTemplate);
}
}
public static Task LogAsyncTask(
this Task task, string messageTemplate,
LogEventLevel level = LogEventLevel.Information)
{
return LogAsyncTask<object>(async () =>
{
await task.ConfigureAwait(false);
return null;
}, messageTemplate, level);
}
答案 1 :(得分:0)
正如其他答案/评论所提到的那样,您的当前代码存在核心问题,因为您没有表明它在启动后的某个时间开始,甚至可能在它完成之后。但是有一个简单的解决方案,那就是接受一个返回Task
而不是Task
本身的方法,这样调用该方法就会启动异步操作。
斯蒂芬指出,你真的不应该在这里使用ContinueWith
。传播任务的状态很难得到正确的语义(你没有完全正确),使用await
只是远更容易和更清晰。
请注意,尝试使用另一个方法实现一个方法(您可以根据另一个方法实现)并不值得付出努力。这两种方法都只是所以简单,对于通用和非通用版本来说,从头开始正确实现它就像调用另一个重载一样容易,如果不是更容易,那么我就不会打扰。另外,如果你经常调用这样的方法(看起来你会这么做),不考虑不必要的代码的非常小的性能优势可能是一个有价值的好处,因为成本太低了。
public static async Task<TResult> LogAsyncTask<TResult>(
Func<Task<TResult>> asyncMethod, string messageTemplate,
LogEventLevel level = LogEventLevel.Information)
{
Log.Write(level, "Async Starting: " + messageTemplate);
try
{
return await asyncMethod().ConfigureAwait(false);
}
finally
{
Log.Write(level, "Async Finished: " + messageTemplate);
}
}
public static async Task LogAsyncTask(
Func<Task> asyncMethod, string messageTemplate,
LogEventLevel level = LogEventLevel.Information)
{
Log.Write(level, "Async Starting: " + messageTemplate);
try
{
await asyncMethod().ConfigureAwait(false);
}
finally
{
Log.Write(level, "Async Finished: " + messageTemplate);
}
}
答案 2 :(得分:0)
想法看起来很有趣。 您还可以查看面向方面编程来解决您的问题。 我试着做一些非常相似的事情。
查看文章Aspect-Oriented Programming : Aspect-Oriented Programming with the RealProxy Class,然后查看我的文章Aspect Oriented Programming in C# with RealProxy如何修复第一篇文章中的示例来处理任务结果。即使您决定不使用AOP,也可以将第二篇文章中的代码用于您的解决方案。