简体   繁体   English

来自异步方法的Mvc .Net Catch异常

[英]Mvc .Net Catch Exception from async method

I am using this library as wrapper for Mailchimp API v3.0 我使用这个库作为Mailchimp API v3.0的包装器

Now I am having issue to catch Exceptions from methods of this library. 现在我有问题从这个库的方法中捕获异常。

The methods are throwing MailChimpException which is extended from Exception . 这些方法抛出了从Exception扩展的MailChimpException

The thing is if I run it from Console APP then I am able to catch exception, but when I run it from my Web project, I cannot catch Exception and the code just stuck when exception ocurred. 问题是如果我从Console APP运行它然后我能够捕获异常,但是当我从我的Web项目运行它时,我无法捕获异常并且代码在异常发生时卡住了。

Here is example of My call: 以下是我的电话示例:

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;
    }
}

And here is the method from the library which I call: 以下是我称之为库的方法:

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);
            }
        }
    }
}

Does anyone has idea why I am able to trigger exception in Console APP but not in Web project. 有谁知道为什么我能够在控制台APP中触发异常但不能在Web项目中触发异常。 Any suggestion what to check? 有什么建议要检查吗?

UPDATE: If I change my code to use Task.Run like: 更新:如果我更改我的代码使用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;
    }
}

Then I catch Exception in catch block, but then How I can get result from task when everything is fine??? 然后我在catch块中捕获Exception,但是当一切都很好时,我怎么能从任务中得到结果?

The proper solution is to make this "async all the way", as I describe in my MSDN article on async best practices . 正确的解决方案就是让这个“异步”,正如我在MSDN上关于异步最佳实践的文章中所描述的那样。 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 ). 直接调用Result的问题是它会导致死锁(正如我在博客文章中描述的那样, 不要阻止异步代码 )。 (In your case, a secondary problem from Result is that it will wrap MailChimpException within an AggregateException , making error handling more awkward). (在您的情况下, Result的第二个问题是它会在AggregateException包装MailChimpException ,使得错误处理更加尴尬)。

Wrapping the call to Task.Run and then calling Result is a hack that decreases the scalability of your web service (and doesn't take care of the secondary problem of exception wrapping, either). 将调用包装到Task.Run然后调用Result是一种Task.Run Web服务的可伸缩性(并且不会处理异常包装的次要问题)。 If you don't want to use asynchronous code, then use a synchronous API. 如果您不想使用异步代码,请使用同步API。 Since this is an I/O operation, the most natural approach is asynchronous code, which would look like this: 由于这是一个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 is an answer to the updated question. 这是更新问题的答案。

You can get the result simply by calling Task.Result on the task which may throw an exception. 您可以通过在可能引发异常的任务上调用Task.Result来获得结果。 You lambda expression has to return the called method's Task<T> result using return await . lambda表达式必须使用return await返回被调用方法的Task<T>结果。 Otherwise the result of the the whole expression will be a non-generic task, which represents an operation that does not return a value and doesn't have a Result member. 否则,整个表达式的结果将是一个非泛型任务,它表示不返回值且没有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;
}

Notice that I've removed the call to Task.Wait , because it is unnecessary when you also fetch the result. 请注意,我已经删除了对Task.Wait的调用,因为在获取结果时也没有必要。

Quote from MSDN's Task<TResult>.Result reference page: 引自MSDN的Task<TResult>.Result参考页面:

Accessing the property's get accessor blocks the calling thread until the asynchronous operation is complete; 访问属性的get访问器会阻塞调用线程,直到异步操作完成; it is equivalent to calling the Wait method. 它等同于调用Wait方法。

The code snippet will display the text from catch block. 代码片段将显示catch块中的文本。 If you remove the first line from AsyncMethod , you will get the result displayed. 如果从AsyncMethod删除第一行,则会显示结果。

This: https://blogs.msdn.microsoft.com/ptorr/2014/12/10/async-exceptions-in-c/ 这: https//blogs.msdn.microsoft.com/ptorr/2014/12/10/async-exceptions-in-c/

To cut a long story short, you never access the result of your async method, and never see the exception. 简而言之,您永远不会访问异步方法的结果,也永远不会看到异常。 If you dont want to make the parent method async, then you need to explicitly get the value of the .Result property from the task object returned by AddOrUpdateAsync method. 如果你不想让父母异步方法,那么你需要明确获得的价值.Result从AddOrUpdateAsync方法返回的任务对象的属性。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM