Асинхронные задачи C# — API MANDRILL

Я настраиваю Mandrill Api для наших внутренних систем.

Я могу заставить API работать нормально и получать от него электронные письма. У меня проблема в том, что когда я пытаюсь получить результат из запроса на отправку, система просто зависает на неопределенный срок.

Мой код выглядит следующим образом:

public async Task<bool> SendEmail(MandrillSendTemplateRequest request)
{
    var result = await _mandrill.SendMessageTemplate(Map(request));
    return result[0].Status.ToString() == "sent";
}

Если я запускаю приведенный выше код БЕЗ возврата, электронное письмо отправляется нормально. Если я запускаю приведенный выше код С возвратом, электронная почта отправляется нормально, но моя программа просто зависает.

Кто-нибудь может пролить свет на этот вопрос?

Изменить. Для справки: я использую Mandrill .Net Wrapper от Shawn Mclean из NuGet Packages. API Mandrill 2.2.7

Изменить. Событие нажатия кнопки, вызывающее метод SendEmail:

  Private Sub btnTest_Click(sender As Object, e As EventArgs) Handles btnTest.Click

    Try
        WaitCursor = True

        Dim mgr As New CustomerCommunicationsManager()
        Dim r As New MandrillSendTemplateRequest()
        Dim m As New MandrillMessage()

        m.FromEmail = "[email protected]"
        m.FromName = "fromName"

        m.Subject = "Test"
        m.ForeName = "Software"
        m.Surname = "Dept"
        m.EmailAddress = "[email protected]"

        r.Message = m
        r.TemplateName = "WelcomeBasic"

        Dim sent As Boolean = mgr.Mandrill.SendEmail(r).Result

    Catch ex As Exception

    Finally
        WaitCursor = False
    End Try


End Sub

Изменить. Код для карты(запрос) (метод SendEmail)

private SendMessageTemplateRequest Map(MandrillSendTemplateRequest obj)
{
    if (obj == null) { return null; }
    return new SendMessageTemplateRequest(Map(obj.Message), obj.TemplateName, Map(obj.TemplateContent));
}

private EmailMessage Map(MandrillMessage obj)
{
    if (obj == null) { return null; }

    return new EmailMessage
    {
        AutoHtml = obj.AutoHtml,
        AutoText = obj.AutoText,
        FromEmail = obj.FromEmail,
        FromName = obj.FromName,
        Important = obj.Important,
        PreserveRecipients = obj.PreserveRecipients,
        Subject = obj.Subject,
        Tags = obj.tags,
        To = new EmailAddress[] { new EmailAddress(obj.EmailAddress, obj.FullName) },
        TrackOpens = obj.TrackOpens,
        TrackClicks = obj.TrackClicks
    };
}

private IEnumerable<TemplateContent> Map(IEnumerable<MandrillTemplateContent> obj)
{
    if (obj == null) { return null; }

    List<TemplateContent> content = new List<TemplateContent>();

    foreach (MandrillTemplateContent c in obj)
    {
        content.Add(new TemplateContent{ Content = c.Content, Name = c.Name });
    }

    return content;
}

person Richard.Gale    schedule 16.11.2015    source источник
comment
Вам также необходимо опубликовать, как вы вызываете этот метод. Вы вызываете Wait или Result для возвращаемой задачи?   -  person Daniel Kelley    schedule 16.11.2015
comment
Я просто вызываю, как указано выше, часть _mandrill на самом деле является ссылкой на их API.   -  person Richard.Gale    schedule 16.11.2015
comment
Так как же называется SendMail? Что это называет?   -  person Daniel Kelley    schedule 16.11.2015
comment
На данный момент, поскольку я просто тестирую, это происходит из события нажатия кнопки формы Windows.   -  person Richard.Gale    schedule 16.11.2015
comment
Либо индексатор result, либо метод доступа Status get, либо метод ToString() зависают. Вы отлаживали с помощью шага в? попробуйте поставить точки останова в indexer/accessor/tostring   -  person Philippe Paré    schedule 16.11.2015
comment
если я поставлю точку останова на возврат, то точка останова не сработает. если точка останова находится на триггере (var result = await...), то ошибки не видно.   -  person Richard.Gale    schedule 16.11.2015
comment
Покажите код, который вызывает SendEmail   -  person i3arnon    schedule 16.11.2015
comment
Я отредактировал исходный пост. См. выше. Спасибо.   -  person Richard.Gale    schedule 16.11.2015


Ответы (1)


У вас тупиковая ситуация, потому что вы блокируете асинхронный метод в контексте потока пользовательского интерфейса. Вы не должны блокировать асинхронный код, вы должны его дождаться:

Private Async Sub btnTest_Click(sender As Object, e As EventArgs) Handles btnTest.Click
...
        Dim sent As Boolean = Await mgr.Mandrill.SendEmail(r)
End Sub

Поток пользовательского интерфейса имеет SynchronizationContext (вы можете видеть этот SynchronizationContext.Current != null), который гарантирует, что код после ожидания отправляется в один поток пользовательского интерфейса.

Поскольку вы блокируете поток пользовательского интерфейса с помощью Task.Result, код после ожидания не может быть запущен, что означает, что задача не может быть завершена, и поэтому поток пользовательского интерфейса будет заблокирован навсегда.

Вы можете смягчить это, сказав await не захватывать SynchronizationContext с помощью ConfigureAwait(false), но, хотя это хорошая практика, вы не должны блокировать с самого начала:

public async Task<bool> SendEmailAsync(MandrillSendTemplateRequest request)
{
    var result = await _mandrill.SendMessageTemplate(Map(request)).ConfigureAwait(false);
    return result[0].Status.ToString() == "sent";
}
person i3arnon    schedule 16.11.2015
comment
Привет! Большое спасибо за ваш ответ, теперь у меня есть проблема со сборкой в ​​том, что выражение имеет тип «логический», который не ожидается. - person Richard.Gale; 17.11.2015
comment
@Richard.Gale Кажется, вы пытаетесь дождаться чего-то, что не возвращает задачу. Вы уверены, что вызываете асинхронный метод? - person i3arnon; 17.11.2015
comment
Да, это то, что я читал в Интернете, что все вызовы в этом API асинхронны. - person Richard.Gale; 17.11.2015
comment
@Richard.Gale Если метод не возвращает задачу, это не часть шаблона асинхронного ожидания. - person i3arnon; 17.11.2015
comment
Как я узнаю, вернет ли он задачу? - person Richard.Gale; 17.11.2015
comment
@Richard.Gale, вы можете использовать Go To Declaration в Visual Studio - person i3arnon; 17.11.2015
comment
он возвращает Task‹List‹EmailResult›› - person Mike W; 16.02.2017