2012-08-30 1 views
10

MVCMailer에서 ASP.NET MVC 3을 사용하고 있는데 SendAsync를 사용하여 전자 메일을 보내려고했지만 실제로는 더 오래 걸립니다. MVCMailer가있는 HttpContext를 필요로비동기 전자 메일 보내기

var task1 = Task.Factory.StartNew(
      state => 
      { 
       var mail = new UserMailer(); 
       var msg = mail.Welcome("My Name", "[email protected]"); 
       msg.SendAsync(); 
      }); 

    task1.Wait(); 

문제이지만,이 작업 안에 내가 HttpContext를 널을 가지고 :

그래서 내가 우는 코드처럼 Task.Factory를 사용하는 것을 시도하고있다.

비동기 전자 메일을 보내려면 어떻게해야합니까?

답변

18

이에 작은 추가 :
이는 QueueUserWorkItem 버전입니다. 다음은 일부를 돕는 확장 메소드입니다.

using Mvc.Mailer; 
using System.Threading.Tasks; 
public static void SendEmailAsync(this MvcMailMessage msg, HttpContext currContext) 
{ 
     //make this process a little cleaner 
     Task.Factory.StartNew(() => 
     { 
      System.Web.HttpContext.Current = currContext; 
      msg.SendAsync(); 
     }); 
} 

다음과 같이 컨트롤러 방법을 사용하십시오.

Mailers.UserMailer um = new Mailers.UserMailer(); 
um.SendWelcomeEmail(dataObject).SendEmailAsync(ControllerContext.HttpContext.ApplicationInstance.Context); 
+0

안녕 Matt! 감사합니다 참조,이 멋지다, 그것 asyncronously 100 % 보내, 사람들이 MVCMailer 하나 pseudo 비동기 하하라고 말 들었습니다. 고마워! – Baconbeastnz

+2

문제가 없습니다! 나는 이것과 함께 조금만 책상에 맞서 머리를 치고 있었다. :) – Matt

+0

@ Matt, 그것은 훌륭하게 작동한다 !!! 감사합니다!!! =) –

0

작업이 필요하지 않습니다. SendAsync는 비동기이며 다른 스레드 자체를 사용합니다. 작업을하면 메일 링 속도가 빨라집니다.

업데이트 : 같은 문제를 해결할 때 작업 및 동기 전송을 사용합니다. 그것은 SendAsync 너무 asynchrous 아니 었 것으로 보인다. 이 (가 HttpContext를 원하는되지 않음) 내 코드의 샘플입니다 :

public void SendMailCollection(IEnumerable<Tuple<string, string, MailAddress>> mailParams) 
    { 
     var smtpClient = new SmtpClient 
     { 
      Credentials = new NetworkCredential(_configurationService.SmtpUser, _configurationService.SmtpPassword), 
      Host = _configurationService.SmtpHost, 
      Port = _configurationService.SmtpPort.Value 
     }; 

     var task = new Task(() => 
           { 
            foreach (MailMessage message in mailParams.Select(FormMessage)) 
            { 
             smtpClient.Send(message); 
            } 

           }); 
     task.Start(); 
    } 

    private MailMessage FormMessage(Tuple<string, string, MailAddress> firstMail) 
    { 
     var message = new MailMessage 
      { 
       From = new MailAddress(_configurationService.SmtpSenderEmail, _configurationService.SmtpSenderName), 
       Subject = firstMail.Item1, 
       Body = firstMail.Item2 
      }; 
     message.To.Add(firstMail.Item3); 
     return message; 
    } 
+0

왜 오래 걸리나요? 나는 배경으로 이메일을 보내야한다. –

+0

비동기가 반드시 빠른 것은 아닙니다. 느린 프로세스가 기본 스레드를 차단하지 않고 응용 프로그램이 사용자 작업에 반응 할 것임을 의미합니다. –

+0

물론 전자 메일을 보내면 프로세스가이 작업에 의해 차단됩니다. –

4

Task.Factory.StartNew는 새로운 스레드를 생성합니다.
당신은이 작업을 수행 할 필요가 메인 스레드에 HttpContext를에 액세스하려면 :

var task1 = Task.Factory.StartNew(() => 
    { 
    System.Web.HttpContext.Current = ControllerContext.HttpContext.ApplicationInstance.Context; 
    var mail = new UserMailer(); 
    var msg = mail.Welcome("My Name", "[email protected]"); 
    msg.SendAsync(); 
    }); 

task1.Wait(); 

이는 TPL 또는 QueueUserWorkItem를 사용하는 것이 좋습니다 경우 긴 논쟁.
누군가가 issue을 해결하려고했습니다.

public class HomeController : Controller 
{ 
    private AutoResetEvent s_reset = new AutoResetEvent(false); 

    public ActionResult Index() 
    { 
     var state = new WorkerState() { HttpContextReference = System.Web.HttpContext.Current }; 
     ThreadPool.QueueUserWorkItem(new WaitCallback(EmaiSenderWorker), state); 

     try 
     { 
     s_reset.WaitOne(); 
     } 
     finally 
     { 
     s_reset.Close(); 
     } 

     return View(); 
    } 

    void EmaiSenderWorker(object state) 
    { 
     var mystate = state as WorkerState; 

     if (mystate != null && mystate.HttpContextReference != null) 
     { 
     System.Web.HttpContext.Current = mystate.HttpContextReference; 
     } 

     var mail = new UserMailer(); 
     var msg = mail.Welcome(); 
     msg.SendAsync(); 

     s_reset.Set(); 
    } 

    private class WorkerState 
    { 
     public HttpContext HttpContextReference { get; set; } 
    } 

} 
+0

예 LeflyX! 이미 시도했지만 MVCMailer 객체에 컨텍스트 변수를 전달할 수 있는지 여부를 알 수 없습니다. –

+1

@MichelAndrade : 답변을 업데이트했습니다. 도움이되는지 확인하십시오. – LeftyX

+0

LeflyX, 거의 내가 거기서 "task1.Wait();"을 벗으면. 그것은 작동하지 않습니다 –

0
public class UserMailer : MailerBase  
{ 
    RegisterService Service = new RegisterService(); 
    HttpContext Context; 
    public UserMailer(HttpContext context) 
    { 
     Context = context; 
     MasterName="_Layout"; 
    } 

    public void ConfirmRegistration(Register model) 
    { 
     SendAsync(() => 
     { 
      model.Conference = Service.Context.Conferences.Find(model.ConferenceID); // load conference bcs it fails to lazy load automatically. 
      ViewData.Model = model; 
      return Populate(x => 
      { 
       x.Subject = "You registered for " + model.Conference.Name + "!"; ; 
       x.ViewName = "Confirm"; 
       x.To.Add(model.Email); 
      }); 
     }); 
    } 

    private void SendAsync(Func<MvcMailMessage> GetEmail) 
    { 
     Task.Factory.StartNew(() => 
     { 
      System.Web.HttpContext.Current = Context; 
      GetEmail().Send(); 
     }); 
    } 
+2

답변을 게시 해 주셔서 감사합니다. 코드 스 니펫이 질문에 대답 할 수 있지만 설명과 같이 추가 정보를 추가하는 것이 좋습니다. – j0k