正确的模式,用于调用包含来自MVC控制器的异步调用的服务

时间:2016-05-24 03:19:49

标签: c# asp.net-mvc asynchronous async-await

我是TAP的新手,并且在 C#中实际上等待/等待,所以我可能会有一些不好的代码气味,所以请保持温和。 : - )

我有一个服务方法,如下所示:

public OzCpAddOrUpdateEmailAddressToListOutput AddOrUpdateEmailAddressToList(
    OzCpAddOrUpdateEmailAddressToListInput aParams)
{
    var result = new OzCpAddOrUpdateEmailAddressToListOutput();
    try
    {
        var mailChimManager = new MailChimpManager(aParams.MailChimpApiKey);
        Task<Member> mailChimpResult =
            mailChimManager.Members.AddOrUpdateAsync(
                aParams.Listid, 
                new Member                                                                                                     
                {
                    EmailAddress = aParams.EmailAddress
                });

        //Poll async task until it completes. 
        //Give it at most 8 seconds to do what it needs to do
        var outOfTime = DateTime.Now.AddSeconds(8);
        while (!mailChimpResult.IsCompleted)
        {
            if (DateTime.Now > outOfTime)
            {
                throw new Exception("Timed out waiting for MailChimp API.");
            }
        }

        //Should there have been a problem with the call then we raise an exception
        if (mailChimpResult.IsFaulted)
        {
            throw new Exception(
                mailChimpResult.Exception?.Message ?? 
                "Unknown mail chimp library error.", 
                mailChimpResult.Exception);
        }
        else
        {
            //Call to api returned without failing but unless we have 
            //the email address subscribed we have an issue
            if (mailChimpResult.Result.Status != Status.Subscribed)
            {
                throw new Exception(
                    $"There was a problem subscribing the email address
                    {aParams.EmailAddress} to the mailchimp list id 
                    {aParams.Listid}");
            }
        }
    }
    catch (Exception ex)
    {
        result.ResultErrors.AddFatalError(PlatformErrors.UNKNOWN, ex.Message);
    }
    return result;
}

但是当我从 MVC控制器操作mailChimpResult.IsCompleted来电话时,总是返回false并最终达到超时。

我意识到这是因为我没有按照HttpClient IsComplete always return false链接异步调用,并且因为不同的线程,这种行为是“预期的”。

但是我希望我的服务方法能够隐藏它正在做的async性质的复杂性,而只是在我的动作方法中执行似乎是同步调用的那个:

 var mailChimpResult =
    _PlatformMailChimpService.AddOrUpdateEmailAddressToList(
        new OzCpAddOrUpdateEmailAddressToListInput                                                                                                                                            
        {
            EmailAddress = aFormCollection["aEmailAddress"],                                                                                                                                           
            Listid = ApplicationSettings.Newsletter.MailChimpListId.Value,                                                                                                                                          
            MailChimpApiKey = ApplicationSettings.Newsletter.MailChimpApiKey.Value
        });

 if (mailChimpResult.Result == true)
 {
    //So something
 }

1 个答案:

答案 0 :(得分:1)

理想情况下,您应该避免.Result.IsFaulted对象的TaskTask<T>属性,这是代码味道第一。当您使用这些对象时,您应该在整个堆栈中使用asyncawait。考虑以这种方式编写的服务:

public async Task<OzCpAddOrUpdateEmailAddressToListOutput> 
    AddOrUpdateEmailAddressToList(
        OzCpAddOrUpdateEmailAddressToListInput aParams)
{
    var result = new OzCpAddOrUpdateEmailAddressToListOutput();
    try
    {
        var mailChimManager = new MailChimpManager(aParams.MailChimpApiKey);
        Member mailChimpResult =
            await mailChimManager.Members.AddOrUpdateAsync(
                aParams.Listid, 
                new Member                                                                                                     
                {
                    EmailAddress = aParams.EmailAddress
                });
    }
    catch (Exception ex)
    {
        result.ResultErrors.AddFatalError(PlatformErrors.UNKNOWN, ex.Message);
    }
    return result;
}

请注意,我能够删除所有不必要的轮询和检查属性。我们将该方法标记为Task<OzCpAddOrUpdateEmailAddressToListOutput>,并使用async关键字对其进行修饰。这允许我们在方法体中使用await关键字。我们await生成.AddOrUpdateAsync的{​​{1}}。

对服务的消费调用看似相似,遵循MemberasyncawaitTask个关键字的相同范例:

Task<T>

最佳做法是将&#34; Async&#34;说方法,表示它是异步的,即; var mailChimpResult = await _PlatformMailChimpService.AddOrUpdateEmailAddressToList( new OzCpAddOrUpdateEmailAddressToListInput { EmailAddress = aFormCollection["aEmailAddress"], Listid = ApplicationSettings.Newsletter.MailChimpListId.Value, MailChimpApiKey = ApplicationSettings.Newsletter.MailChimpApiKey.Value }); if (mailChimpResult.Result == true) { //So something }