Mvc .Net 从异步方法中捕获异常
Mvc .Net Catch Exception from async method
我正在使用 this 库作为 Mailchimp API v3.0
的包装器
现在我无法从该库的方法中捕获异常。
这些方法正在抛出 MailChimpException
,它是从 Exception
扩展而来的。
问题是,如果我从控制台应用程序 运行 它然后我能够捕获异常,但是当我从我的 Web 项目 运行 它时,我无法捕获异常并且代码只是卡住了发生异常时。
这是我的调用示例:
public bool ListSubscribe(string emailAddress, string apiKey, string listId)
{
try
{
var api = new MailChimpManager(apiKey);
var profile = new Member();
profile.EmailAddress = emailAddress;
var output = api.Members.AddOrUpdateAsync(listId, profile).Result;
return true;
}
catch (Exception ex)
{
logger.Error("An error ocurred in trying to Subscribe to Mailchimp list. {0}", ex.Message);
return false;
}
}
这是我调用的库中的方法:
namespace MailChimp.Net.Logic
{
/// <summary>
/// The member logic.
/// </summary>
internal class MemberLogic : BaseLogic, IMemberLogic
{
private const string BaseUrl = "lists";
/// <summary>
/// Initializes a new instance of the <see cref="MemberLogic"/> class.
/// </summary>
/// <param name="apiKey">
/// The api key.
/// </param>
public MemberLogic(string apiKey)
: base(apiKey)
{
}
/// <summary>
/// The add or update async.
/// </summary>
/// <param name="listId">
/// The list id.
/// </param>
/// <param name="member">
/// The member.
/// </param>
/// <returns>
/// The <see cref="Task"/>.
/// </returns>
/// <exception cref="ArgumentNullException"><paramref>
/// <name>uriString</name>
/// </paramref>
/// is null. </exception>
/// <exception cref="UriFormatException">In the .NET for Windows Store apps or the Portable Class Library, catch the base class exception, <see cref="T:System.FormatException" />, instead.<paramref name="uriString" /> is empty.-or- The scheme specified in <paramref name="uriString" /> is not correctly formed. See <see cref="M:System.Uri.CheckSchemeName(System.String)" />.-or- <paramref name="uriString" /> contains too many slashes.-or- The password specified in <paramref name="uriString" /> is not valid.-or- The host name specified in <paramref name="uriString" /> is not valid.-or- The file name specified in <paramref name="uriString" /> is not valid. -or- The user name specified in <paramref name="uriString" /> is not valid.-or- The host or authority name specified in <paramref name="uriString" /> cannot be terminated by backslashes.-or- The port number specified in <paramref name="uriString" /> is not valid or cannot be parsed.-or- The length of <paramref name="uriString" /> exceeds 65519 characters.-or- The length of the scheme specified in <paramref name="uriString" /> exceeds 1023 characters.-or- There is an invalid character sequence in <paramref name="uriString" />.-or- The MS-DOS path specified in <paramref name="uriString" /> must start with c:\.</exception>
/// <exception cref="MailChimpException">
/// Custom Mail Chimp Exception
/// </exception>
/// <exception cref="TargetInvocationException">The algorithm was used with Federal Information Processing Standards (FIPS) mode enabled, but is not FIPS compatible.</exception>
/// <exception cref="ObjectDisposedException">
/// The object has already been disposed.
/// </exception>
/// <exception cref="EncoderFallbackException">
/// A fallback occurred (see Character Encoding in the .NET Framework for complete explanation)-and-<see cref="P:System.Text.Encoding.EncoderFallback"/> is set to <see cref="T:System.Text.EncoderExceptionFallback"/>.
/// </exception>
/// <exception cref="ArgumentOutOfRangeException">
/// Enlarging the value of this instance would exceed <see cref="P:System.Text.StringBuilder.MaxCapacity"/>.
/// </exception>
/// <exception cref="FormatException">
/// <paramref>
/// <name>format</name>
/// </paramref>
/// includes an unsupported specifier. Supported format specifiers are listed in the Remarks section.
/// </exception>
public async Task<Member> AddOrUpdateAsync(string listId, Member member)
{
using (var client = this.CreateMailClient($"{BaseUrl}/"))
{
var response =
await
client.PutAsJsonAsync($"{listId}/members/{this.Hash(member.EmailAddress.ToLower())}", member, null).ConfigureAwait(false);
await response.EnsureSuccessMailChimpAsync().ConfigureAwait(false);
return await response.Content.ReadAsAsync<Member>().ConfigureAwait(false);
}
}
}
}
有没有人知道为什么我能够在控制台应用程序中触发异常,但在 Web 项目中却不能。有什么建议要检查什么吗?
更新:
如果我更改代码以使用 Task.Run
,例如:
var task = Task.Run(async () => { await api.Members.AddOrUpdateAsync(listId, profile); });
task.Wait();
所以:
public bool ListSubscribe(string emailAddress, string apiKey, string listId)
{
try
{
var api = new MailChimpManager(apiKey);
var profile = new Member();
profile.EmailAddress = emailAddress;
var task = Task.Run(async () => { await api.Members.AddOrUpdateAsync(listId, profile); });
task.Wait();
return true;
}
catch (Exception ex)
{
logger.Error("An error ocurred in trying to Subscribe to Mailchimp list. {0}", ex.Message);
return false;
}
}
然后我在 catch 块中捕获异常,但是当一切正常时我如何从任务中获取结果???
这个:https://blogs.msdn.microsoft.com/ptorr/2014/12/10/async-exceptions-in-c/
长话短说,您永远不会访问异步方法的结果,也永远不会看到异常。如果不想让父方法异步,则需要从 AddOrUpdateAsync 方法返回的任务对象中显式获取 .Result
属性 的值。
这是对更新问题的回答。
在可能抛出异常的任务上调用Task.Result
即可得到结果。您的 lambda 表达式必须使用 return await
return 被调用方法的 Task<T>
结果。否则整个表达式的结果将是一个非泛型任务,它表示一个没有 return 值且没有 Result
成员的操作。
static void Main(string[] args)
{
try
{
var task = Task.Run(async () => { return await AsyncMethod(); });
Console.WriteLine("Result: " + task.Result);
}
catch
{
Console.WriteLine("Exception caught!");
}
Console.ReadKey();
}
static async Task<string> AsyncMethod()
{
throw new ArgumentException();
var result = await Task.Run(() => { return "Result from AsyncMethod"; });
return result;
}
请注意,我已经删除了对 Task.Wait
的调用,因为当您还获取结果时它是不必要的。
引自 MSDN 的 Task<TResult>.Result
参考页:
Accessing the property's get accessor blocks the calling thread until
the asynchronous operation is complete; it is equivalent to calling
the Wait method.
代码片段将显示来自 catch
块的文本。如果您从 AsyncMethod
中删除第一行,您将得到显示的结果。
正确的 解决方案是"async all the way",正如我在 async best practices. The problem with calling Result
directly is that it causes a deadlock (as I describe on my blog post Don't block on async code 上的 MSDN 文章中所描述的那样。 (在你的情况下,Result
的第二个问题是它将 MailChimpException
包装在 AggregateException
中,使错误处理更加尴尬)。
包装对 Task.Run
的调用,然后调用 Result
是一种降低 Web 服务可扩展性的 hack(并且也没有处理异常包装的次要问题) .如果你不想使用异步代码,那么使用一个同步的API。由于这是一个 I/O 操作,最自然的方法是异步代码,如下所示:
public async Task<bool> ListSubscribeAsync(string emailAddress, string apiKey, string listId)
{
try
{
var api = new MailChimpManager(apiKey);
var profile = new Member();
profile.EmailAddress = emailAddress;
var output = await api.Members.AddOrUpdateAsync(listId, profile);
return true;
}
catch (Exception ex)
{
logger.Error("An error ocurred in trying to Subscribe to Mailchimp list. {0}", ex.Message);
return false;
}
}
我正在使用 this 库作为 Mailchimp API v3.0
的包装器现在我无法从该库的方法中捕获异常。
这些方法正在抛出 MailChimpException
,它是从 Exception
扩展而来的。
问题是,如果我从控制台应用程序 运行 它然后我能够捕获异常,但是当我从我的 Web 项目 运行 它时,我无法捕获异常并且代码只是卡住了发生异常时。
这是我的调用示例:
public bool ListSubscribe(string emailAddress, string apiKey, string listId)
{
try
{
var api = new MailChimpManager(apiKey);
var profile = new Member();
profile.EmailAddress = emailAddress;
var output = api.Members.AddOrUpdateAsync(listId, profile).Result;
return true;
}
catch (Exception ex)
{
logger.Error("An error ocurred in trying to Subscribe to Mailchimp list. {0}", ex.Message);
return false;
}
}
这是我调用的库中的方法:
namespace MailChimp.Net.Logic
{
/// <summary>
/// The member logic.
/// </summary>
internal class MemberLogic : BaseLogic, IMemberLogic
{
private const string BaseUrl = "lists";
/// <summary>
/// Initializes a new instance of the <see cref="MemberLogic"/> class.
/// </summary>
/// <param name="apiKey">
/// The api key.
/// </param>
public MemberLogic(string apiKey)
: base(apiKey)
{
}
/// <summary>
/// The add or update async.
/// </summary>
/// <param name="listId">
/// The list id.
/// </param>
/// <param name="member">
/// The member.
/// </param>
/// <returns>
/// The <see cref="Task"/>.
/// </returns>
/// <exception cref="ArgumentNullException"><paramref>
/// <name>uriString</name>
/// </paramref>
/// is null. </exception>
/// <exception cref="UriFormatException">In the .NET for Windows Store apps or the Portable Class Library, catch the base class exception, <see cref="T:System.FormatException" />, instead.<paramref name="uriString" /> is empty.-or- The scheme specified in <paramref name="uriString" /> is not correctly formed. See <see cref="M:System.Uri.CheckSchemeName(System.String)" />.-or- <paramref name="uriString" /> contains too many slashes.-or- The password specified in <paramref name="uriString" /> is not valid.-or- The host name specified in <paramref name="uriString" /> is not valid.-or- The file name specified in <paramref name="uriString" /> is not valid. -or- The user name specified in <paramref name="uriString" /> is not valid.-or- The host or authority name specified in <paramref name="uriString" /> cannot be terminated by backslashes.-or- The port number specified in <paramref name="uriString" /> is not valid or cannot be parsed.-or- The length of <paramref name="uriString" /> exceeds 65519 characters.-or- The length of the scheme specified in <paramref name="uriString" /> exceeds 1023 characters.-or- There is an invalid character sequence in <paramref name="uriString" />.-or- The MS-DOS path specified in <paramref name="uriString" /> must start with c:\.</exception>
/// <exception cref="MailChimpException">
/// Custom Mail Chimp Exception
/// </exception>
/// <exception cref="TargetInvocationException">The algorithm was used with Federal Information Processing Standards (FIPS) mode enabled, but is not FIPS compatible.</exception>
/// <exception cref="ObjectDisposedException">
/// The object has already been disposed.
/// </exception>
/// <exception cref="EncoderFallbackException">
/// A fallback occurred (see Character Encoding in the .NET Framework for complete explanation)-and-<see cref="P:System.Text.Encoding.EncoderFallback"/> is set to <see cref="T:System.Text.EncoderExceptionFallback"/>.
/// </exception>
/// <exception cref="ArgumentOutOfRangeException">
/// Enlarging the value of this instance would exceed <see cref="P:System.Text.StringBuilder.MaxCapacity"/>.
/// </exception>
/// <exception cref="FormatException">
/// <paramref>
/// <name>format</name>
/// </paramref>
/// includes an unsupported specifier. Supported format specifiers are listed in the Remarks section.
/// </exception>
public async Task<Member> AddOrUpdateAsync(string listId, Member member)
{
using (var client = this.CreateMailClient($"{BaseUrl}/"))
{
var response =
await
client.PutAsJsonAsync($"{listId}/members/{this.Hash(member.EmailAddress.ToLower())}", member, null).ConfigureAwait(false);
await response.EnsureSuccessMailChimpAsync().ConfigureAwait(false);
return await response.Content.ReadAsAsync<Member>().ConfigureAwait(false);
}
}
}
}
有没有人知道为什么我能够在控制台应用程序中触发异常,但在 Web 项目中却不能。有什么建议要检查什么吗?
更新:
如果我更改代码以使用 Task.Run
,例如:
var task = Task.Run(async () => { await api.Members.AddOrUpdateAsync(listId, profile); });
task.Wait();
所以:
public bool ListSubscribe(string emailAddress, string apiKey, string listId)
{
try
{
var api = new MailChimpManager(apiKey);
var profile = new Member();
profile.EmailAddress = emailAddress;
var task = Task.Run(async () => { await api.Members.AddOrUpdateAsync(listId, profile); });
task.Wait();
return true;
}
catch (Exception ex)
{
logger.Error("An error ocurred in trying to Subscribe to Mailchimp list. {0}", ex.Message);
return false;
}
}
然后我在 catch 块中捕获异常,但是当一切正常时我如何从任务中获取结果???
这个:https://blogs.msdn.microsoft.com/ptorr/2014/12/10/async-exceptions-in-c/
长话短说,您永远不会访问异步方法的结果,也永远不会看到异常。如果不想让父方法异步,则需要从 AddOrUpdateAsync 方法返回的任务对象中显式获取 .Result
属性 的值。
这是对更新问题的回答。
在可能抛出异常的任务上调用Task.Result
即可得到结果。您的 lambda 表达式必须使用 return await
return 被调用方法的 Task<T>
结果。否则整个表达式的结果将是一个非泛型任务,它表示一个没有 return 值且没有 Result
成员的操作。
static void Main(string[] args)
{
try
{
var task = Task.Run(async () => { return await AsyncMethod(); });
Console.WriteLine("Result: " + task.Result);
}
catch
{
Console.WriteLine("Exception caught!");
}
Console.ReadKey();
}
static async Task<string> AsyncMethod()
{
throw new ArgumentException();
var result = await Task.Run(() => { return "Result from AsyncMethod"; });
return result;
}
请注意,我已经删除了对 Task.Wait
的调用,因为当您还获取结果时它是不必要的。
引自 MSDN 的 Task<TResult>.Result
参考页:
Accessing the property's get accessor blocks the calling thread until the asynchronous operation is complete; it is equivalent to calling the Wait method.
代码片段将显示来自 catch
块的文本。如果您从 AsyncMethod
中删除第一行,您将得到显示的结果。
正确的 解决方案是"async all the way",正如我在 async best practices. The problem with calling Result
directly is that it causes a deadlock (as I describe on my blog post Don't block on async code 上的 MSDN 文章中所描述的那样。 (在你的情况下,Result
的第二个问题是它将 MailChimpException
包装在 AggregateException
中,使错误处理更加尴尬)。
包装对 Task.Run
的调用,然后调用 Result
是一种降低 Web 服务可扩展性的 hack(并且也没有处理异常包装的次要问题) .如果你不想使用异步代码,那么使用一个同步的API。由于这是一个 I/O 操作,最自然的方法是异步代码,如下所示:
public async Task<bool> ListSubscribeAsync(string emailAddress, string apiKey, string listId)
{
try
{
var api = new MailChimpManager(apiKey);
var profile = new Member();
profile.EmailAddress = emailAddress;
var output = await api.Members.AddOrUpdateAsync(listId, profile);
return true;
}
catch (Exception ex)
{
logger.Error("An error ocurred in trying to Subscribe to Mailchimp list. {0}", ex.Message);
return false;
}
}