2016-06-01 2 views
0

Я пытаюсь узнать и освоить ASYNC и AWAIT, но неясно, правильно ли я это делаю тогда. Я имею в виду, что код работает, и я взял два браузера/экземпляры одной формы и щелкнул, хорошо , почти в то же время, и все прошло хорошо. Я получил 2 письма, но это правильный способ сделать это? и делает это действительно дает значение плюс, чтобы сделать это ASync вместо синхронизации ...Является ли это ДЕЙСТВИТЕЛЬНО асинхронной электронной почтой и лучшей практикой?

Это мой код:

[HttpPost] 
    [AllowAnonymous] 
    [ValidateAntiForgeryToken] 
    public async Task<ActionResult> Register(RegisterViewModel model) 
    { 
     bla bla do some sync simple stuffs etc... 
     ... 
     await Task.Run(() => Utility.MailUtility.SendEmail(user.Email, user.To, msg, subj)); 
     return RedirectToAction("Index", "Home"); 
    } 

, а затем код рассылки выглядит так, как и в образце кода, взятого из StackOverflow:

public static async Task SendEmail(params etc...) 
    { 
     await SendMailForTemplateID(params etc...); 
    } 

    private static Task SendMailForTemplateID(params etc...) 
    { 


      var task = Task.Factory.StartNew(() => 
      { 
       MailMessage message = new MailMessage(); 
       MailMessage mail = new MailMessage(); 
       //SmtpClient SmtpServer = new SmtpClient(ConfigurationManager.AppSettings["SMTPHost"]); 
       SmtpClient SmtpServer = new SmtpClient("localhost"); // for test purpose only 
       mail.From = new MailAddress(from); 
       mail.To.Add(to); 
       mail.Subject = GetSubjectForTemplateId(templateID, extraMsg, dataInfo, fromName, toName, isFr); 
       mail.Body = GetBodyForTemplateId(templateID, extraMsg, dataInfo, fromName, toName, isFr); 
       mail.IsBodyHtml = false; 
       SmtpServer.Port = 81; // for test purpose only 
       SmtpServer.Credentials = new System.Net.NetworkCredential 
        (ConfigurationManager.AppSettings["CredentialUser"], 
        ConfigurationManager.AppSettings["CredentialPassword"]); 

       bool result; 
       bool.TryParse(ConfigurationManager.AppSettings["EnableSSL"], out result); 
       SmtpServer.EnableSsl = result; 

       SmtpServer.Send(mail); 

       //* make sure we leave nothing in memory 
       message.Dispose(); 
       mail.Dispose(); 
       SmtpServer.Dispose(); 


      }); 

      return task; 

     } 

я извиняюсь, ребята, но я не знаю, как поставить возвращение строки (Enter) между моими ответами. Является ли это возможным? nway, в отношении лучшей практики ASYNC-EMAIL.

Моя новая версия кода выглядит так, она работает более простое кодирование, но верно? Я имею в виду никаких новых потоков, дает ли это значение плюс VS sync sendmail и т. Д.

Я называю это действием MVC. Код:

 do stuff... 
    await Utility.MailUtility.SendMailForTemplateID(params); 
    do stuff... 

    public static async Task SendMailForTemplateID(params...) 
    { 
     try 
     { 
      email code etc... 

      await smtpClient.SendMailAsync(mail); 

      mail.Dispose(); 
      smtpClient.Dispose(); 


     } 
     catch (Exception) 
     { 
      // Log ex 
     } 
    } 
+6

No. Вы создаете темы, что собирается сделать это использовать больше ресурсов процессора, а не меньше. Ваша цель - «ожидание» операций ввода-вывода. Лучшей практикой было бы использовать метод SendMailAsync, который уже существует в классе «SmtpClient». Это заставит IO «ждать», но не создавать новые потоки. – dman2306

+0

Следует также упомянуть, что вы вызываете 'Task.Factory.StartNew', который создает поток, затем вы вызываете' Task.Run', который создает поток. Вы выполняете это через 3 потока, когда вам нужно только 1. ('Task.Run' вызывает' Task.Factory.StartNew') – dman2306

ответ

4

Нет, упаковка асинхронных звонков в Task.Run неправильно способ написания async/await кода.

Одна конкретная проблема с вашим текущим кодом: Task.Run и Task.Factory.StartNew начать новые потоки для выполнения операции - так что код запускает 2 дополнительных потока для выполнения операции.

Правильный путь - удалите все Task.Run/Task.Factory.StartNew звонки и await асинхронные звонки до конца. В вашем случае SmtpClient.SendMailAsync следует использовать на нижнем уровне, а не SmtpServer.Send(mail);.

В более общем случае, когда нет задач на основе API, но есть некоторые альтернативные асинхронные интерфейсы (например, на основе событий SmtpClient.SendAsync) вы, возможно, потребуется предоставить оберточной вспомогательные методы для преобразования таких вызовов на основе задач async методов. Некоторые подходы показаны на Convert Event based pattern to async CTP pattern


Приблизительный код:

public async Task<ActionResult> Register(RegisterViewModel model) 
{ 
    ... 
    await Utility.MailUtility.SendEmailAsync(user.Email, user.To, msg, subj)); 
    return RedirectToAction("Index", "Home"); 
} 

// No need to add `async` here as there is no `await` inside, 
// possibly can be removed altogether. 
public static Task SendEmailAsync(params etc...) 
{ 
    return SendMailForTemplateID(params etc...); 
} 

private async static Task SendMailForTemplateID(params etc...) 
{ 
    // not Task.Run/Task.Factory.StartNew 

    using (MailMessage message = ...) 
    using (SmtpClient smtp = ....) 
    { 
      .... // setup message/smtp 

      await smtp.SendMailAsync(mail); // Task-based Send 
    } 
    // no need to return anything for `Task` (essentially `void`) method 
    } 
+1

Существует уже основанный на задаче метод async на 'SmtpClient', как я упоминал в своем комментарии,' SendMailAsync'. Не требуется упаковка. – dman2306

+2

Также стоит отметить, что использование 'Task.Run' в ASP.Net - это плохая практика. Прочитайте некоторые статьи, которые Стивен Клири написал в своем блоге: http://blog.stephencleary.com/2013/11/taskrun-etiquette-examples-dont-use.html – Cameron

+0

@ dman2306 - спасибо - пропустил этот однострочный к ответу. –

Смежные вопросы