重载泛型和无效任务类型

时间:2017-09-06 12:35:57

标签: c# task overloading extension-methods

我做了这个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...");

3 个答案:

答案 0 :(得分:0)

正如Damien正确指出的那样,你的代码有一个问题,它只能记录&#34; Async Starting&#34; (并且可能启动计时器)异步方法的同步部分完成后(并返回任务)。一个适当的解决方案将使用面向方面的框架,如Fody或PostSharp;你可以开始使用my AsyncDiagnostics project,它可以做一些非常相似的事情。

如果您确实采用当前的方法,则不应使用ContinueWithStartNew。此外,无论例外如何,都应确保写入最终日志:

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,也可以将第二篇文章中的代码用于您的解决方案。