我使用this库作为Mailchimp API v3.0的包装器
现在我遇到了从该库的方法中捕获异常的问题。
这些方法正在抛出从MailChimpException
扩展的Exception
。
问题是如果我从Console APP运行它然后我能够捕获异常,但是当我从我的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);
}
}
}
}
有没有人知道为什么我能够在Console APP中触发异常而不是在Web项目中触发异常。有什么建议要检查吗?
更新
如果我将代码更改为使用Task.Run
,请执行以下操作:
var task = Task.Run(async () => { await api.Members.AddOrUpdateAsync(listId, profile); });
task.Wait();
SO:
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块中捕获Exception,但是当一切正常时我如何从任务中获得结果???
答案 0 :(得分:4)
正确的解决方案是让这个“异步”,正如我在async best practices上的MSDN文章中所描述的那样。直接调用Result
的问题是它会导致死锁(正如我在我的博客文章Don't block on async code中所描述的那样)。 (在您的情况下,来自Result
的次要问题是它会将MailChimpException
包裹在AggregateException
中,从而使错误处理更加尴尬。)
将呼叫包裹到Task.Run
,然后调用Result
是一种破坏,降低了Web服务的可伸缩性(并且不会处理异常包装的次要问题)。如果您不想使用异步代码,请使用同步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;
}
}
答案 1 :(得分:1)
这是对更新后问题的回答。
您可以通过在可能引发异常的任务上调用Task.Result
来获得结果。 lambda表达式必须使用Task<T>
返回被调用方法的return await
结果。否则整个表达式的结果将是一个非泛型任务,它表示一个不返回值且没有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
参考页:
访问属性的get访问器会阻塞调用线程,直到 异步操作完成;它等同于召唤 等待方法。
代码段将显示catch
块中的文字。如果从AsyncMethod
中删除第一行,则会显示结果。
答案 2 :(得分:0)
这:https://blogs.msdn.microsoft.com/ptorr/2014/12/10/async-exceptions-in-c/
简而言之,您永远不会访问异步方法的结果,也永远不会看到异常。如果您不想使父方法异步,那么您需要从AddOrUpdateAsync方法返回的任务对象中显式获取.Result
属性的值。