Отправка асинхронного электронного письма без ожидания от основного веб-сервиса.Net

У меня есть веб-сервис.Net core2, в котором есть определенные методы отправки электронной почты. У меня это работает нормально, используя smtpclient.sendemailasync,

    public async Task<bool> SendEmailAsync(MailMessage email)
    {
        try
        {
            if (string.IsNullOrWhiteSpace(emailFrom)) email.From = new MailAddress(emailFrom);
            using (SmtpClient client = getSMTPClientInstance())
            {
                await client.SendMailAsync(email);

            }
            return true;
        }
        catch (Exception ex)
        {
            Log.Error(ex, "Error sending email in EmailService.SendEmailAsync");
            return false;
        }
    }

Единственная проблема заключается в том, что некоторые SMTP-серверы реагируют слишком долго. Я хочу настроить электронную почту, поставить ее в очередь и вернуться, не дожидаясь результата.

Простое использование незапланированной асинхронности не доступно по двум причинам;

  1. Не надежно продолжать метод вне контекста запроса в asp

  2. Мне нужен доступ к контексту базы данных моей структуры сущностей, чтобы написать журнал

Я должен разрешить внешний или внутренний SMTP (мой клиент указывает), поэтому папка коллекции не возможна - по крайней мере, без службы, которая управляет ею.

Как я мог этого добиться? Мне нужно написать сервис, который управляет этим? Если да, то как мне это сделать в моем.Net Core App, имея в виду, что службе также необходим доступ к контексту EF для записи журнала

ОБНОВИТЬ

Специально для этого в.NetCore DI есть сантехника. Обратитесь к моему дополнительному ответу ниже. Используйте IServiceScopeFactory

2 ответа

Решение

Вы можете позвонить RegisterAsyncTask метод на объекте Page. Это будет сигнализировать время выполнения ASP.NET, что вы хотите убедиться, что они завершены, прежде чем завершать контекст запроса:

Пример:

public void Page_Load(object sender, EventArgs e)
{
    RegisterAsyncTask(new PageAsyncTask(LoadSomeData));
}

public async Task LoadSomeData()
{

    var clientcontacts = Client.DownloadStringTaskAsync("api/contacts");
    var clienttemperature = Client.DownloadStringTaskAsync("api/temperature");
    var clientlocation = Client.DownloadStringTaskAsync("api/location");

    await Task.WhenAll(clientcontacts, clienttemperature, clientlocation);

    var contacts = Newtonsoft.Json.JsonConvert.DeserializeObject<List<Contact>>(await clientcontacts);
    var location = Newtonsoft.Json.JsonConvert.DeserializeObject<string>(await clientlocation);
    var temperature = Newtonsoft.Json.JsonConvert.DeserializeObject<string>(await clienttemperature);

    listcontacts.DataSource = contacts;
    listcontacts.DataBind();
    Temparature.Text = temperature;
    Location.Text = location;
}

https://www.hanselman.com/blog/TheMagicOfUsingAsynchronousMethodsInASPNET45PlusAnImportantGotcha.aspx

Итак, хотя я отметил ответ, есть пара вариантов, которые являются лучшими решениями для моего конкретного примера. Во-первых, это возможность использовать библиотеку типа hangfire для планирования задач, хотя технически это не является ответом на вопрос.

Лучшее решение в ядре.net - использовать IServiceScopeFactory

С IServiceScopeFactory вы можете изменить объем задачи, чтобы она не выходила за рамки, когда запрос завершен. Я сделал следующее непосредственно в контроллере (позже я перешел к использованию подхода hangfire, но это работает). Как вы можете видеть, асинхронная задача запускается в новом нежелательном потоке, пока код контроллера продолжается.

        var task = Task.Run(async () =>
        {
            using (var scope = _serviceScopeFactory.CreateScope())
            {
                var service = scope.ServiceProvider.GetRequiredService<ApprovalService>();
                await service.sendResponseEmailAsync(approvalInfo.ApprovalId, userID, approvalInfo.emailTo, approvalInfo.ccTo);
            }
        });
Другие вопросы по тегам