Отправка элемента управления обратно в пользовательский интерфейс и все остальное на стороне сервера без необходимости ждать пользователей - PullRequest
0 голосов
/ 13 мая 2019

У меня есть действие HTTOPOST в моем контроллере API, который получает объект и сохраняет его в БД, а также отправляет электронное письмо получателям, как только возникает постоянство. Проблема заключается в том, что пользователь должен дождаться, пока функция отправки электронной почты не запустится и не завершит работу, и получить свой пользовательский интерфейс. Как я могу делегировать процесс отправки электронной почты на сервер и просто вернуть пользовательский интерфейс после того, как объект будет сохранен? (пользователи не должны ждать отправки электронной почты.)

  [HttpPost("add")]
    public async Task Add(objSaveModel obj)
    {
        Model model = _mapper.Map<Model>(objSaveModel);
        Model createdModel = await _repo.AddModel(model);


        try //sending an email - prior to this action the control needs 
              //to be diverted to the user and the server needs to perform 
              //this asynchronously.
        {
            EmailTemplate emailTemplate = await _repo.GetEmailTemplate();
            MailMessage msg = new 
            ModelNotificationHelper().GenerateNotification(Model, of, 
            emailTemplate);

            if (msg != null)
            {
                new EmailHelper(_emailSettings).Send(msg);
            }
        }
        catch (Exception e)
        {

            throw new Exception(e.Message);
        }

    }

после _repo.AddModel (модель); элемент управления должен быть перенаправлен на пользовательский интерфейс и конечного пользователя, однако он ожидает полной отправки электронной почты, а затем пользовательский интерфейс обновляется. Это вызывает ненужную задержку для пользовательского интерфейса. Как я могу реализовать это асинхронно / параллельно, чтобы обе задачи выполнялись без ожидания со стороны пользователя?

1 Ответ

0 голосов
/ 13 мая 2019

Вы хотите получить поведение, похожее на шаблон «огонь и забыть» (другими словами, начать какое-то действие, не дожидаясь его завершения) - это возможно, но не безопасно.Посмотрите на эти ресурсы, чтобы погрузиться в тему: .NET MVC Fire and Forget Method также сразу возвращает View и Fire and Forget на ASP.NET .

ПрямаяЛинейный способ решения вашей проблемы может опираться на Фоновые задачи ASP.NET Core Queued * .

Контроллер

Нужно просто «запланировать» отправку электронной почты.Задания будут обрабатываться фоновым работником.

public Controller(IEmailJobQueue emailJobQueue) {
  _emailJobQueue = emailJobQueue;
}

[HttpPost("add")]
public async Task Add(objSaveModel obj)
{
    Model model = _mapper.Map<Model>(objSaveModel);
    Model createdModel = await _repo.AddModel(model);

    await _emailJobQueue.EnqueueJobAsync(new EmailJob("user@stack.com", 11 /* template id */));
}

Фоновая служба :

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

public sealed EmailJob {
  public EmailJob(string recipientAddress, int emailTemplateId, ..){
    // ..
  }

  // ..
}

public interface IEmailJobQueue
{
    Task EnqueueJobAsync(EmailJob job);
    Task<EmailJob> DequeueJobAsync(CancellationToken cancellationToken);
}

public class InMemoryEmailJobQueue : IEmailJobQueue
{
    private ConcurrentQueue<EmailJob> _jobs = new ConcurrentQueue<EmailJob>();

    public Task EnqueueJobAsync(EmailJob job)
    {
        if (job == null)
        {
            throw new ArgumentNullException(nameof(workItem));
        }

        _jobs.Enqueue(job);

        return Task.Completed;
    }

    public Task<EmailJob> DequeueJobAsync(CancellationToken cancellationToken)
    {
        if (!_jobs.TryDequeue(out var job){
          return Task.FromResult<EmailJob>(null);
        }

        return Task.FromResult(job);
    }
}

public class EmailSenderService : BackgroundService
{
    private readonly ILogger _logger;

    public EmailSenderService(IEmailJobQueue emailJobQueue, ILoggerFactory loggerFactory)
    {
        _emailJobQueue = emailJobQueue;
        _logger = loggerFactory.CreateLogger<QueuedHostedService>();
    }

    public IEmailJobQueue _emailJobQueue { get; }

    protected async override Task ExecuteAsync(CancellationToken cancellationToken)
    {
        while (!cancellationToken.IsCancellationRequested)
        {
            var job = await _emailJobQueue.DequeueAsync(cancellationToken);

            if (job == null) {
              Task.Delay(512);
              continue;
            }

            try
            {
                // .. sending email based on data from 'job'
                // Don't forget to handle case when sending fails - either use [Polly.RetryPolicy] (https://github.com/App-vNext/Polly#retry) or persist failed job to postponed processing.
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, $"Error occurred executing {nameof(job)}.");
            }
        }
    }
}

Регистрация в Startup.cs

services.AddHostedService<EmailSenderService>();
services.AddSingleton<IEmailJobQueue, InMemoryEmailJobQueue>();
...