2017-08-20 2 views
0

J'ai implémenté ce service Windows qui envoie des emails en utilisant la méthode SendAsync, toutes les 30 secondes par lots de 20. J'utilise EF6 et SQL Server 2016. Voici quelques parties des codesUoW et DbContext échouent dans un événement

EmailRepository.cs

public class EmailRepository : IEmailRepository 
{ 
    private BBEntities db = null; 


    public EmailRepository(BBEntities db) 
    { 
     this.db = db; 
    } 

    public IEnumerable<tb_Email> SelectAll(int batchAge, int batchSize) 
    { 
     DateTime tDate = DateTime.Now.AddMinutes(batchAge); 
     return db.tb_Email.Where(x => x.ReadyToSend.Equals(true) & x.DateSent.Equals(null) & x.DateCreated >= tDate).OrderBy(x => x.DateCreated).Take(batchSize); 
    } 

    public tb_Email SelectByID(Guid id) 
    { 
     return db.tb_Email.Find(id); 
    } 

    public void Update(tb_Email obj) 
    { 
     db.Entry(obj).State = EntityState.Modified; 
    } 

    #region IDisposable Support 
    private bool disposedValue = false; 

    protected virtual void Dispose(bool disposing) 
    { 
     if (!disposedValue) 
     { 
      if (disposing) 
      { 
       db.Dispose(); 
      } 
      disposedValue = true; 
     } 
    } 

    public void Dispose() 
    { 
     Dispose(true); 
     GC.SuppressFinalize(this); 
    } 
    #endregion 
} 

UnitOfWork.cs

public class UnitOfWork : IUnitOfWork 
{ 

    private readonly BBEntities ctx = new BBEntities(); 
    private IEmailRepository emailRepository; 

    public IEmailRepository EmailRepository 
    { 
     get 
     { 

      if (this.emailRepository == null) 
      { 
       this.emailRepository = new EmailRepository(ctx); 
      } 
      return emailRepository; 
     } 
    } 

    public void Dispose() 
    { 
     this.ctx.Dispose(); 
    } 


    public void Commit() 
    { 
     this.ctx.SaveChanges(); 
    } 

} 

EmailService.cs

public class EmailService : IEmailService 
{ 

    private IUnitOfWork unitOfWork; 

    public EmailService() 
    { 
     unitOfWork = new UnitOfWork(); 
    } 

    public List<tb_Email> SelectAll(int batchAge, int batchSize) 
    { 
     return unitOfWork.EmailRepository.SelectAll(batchAge, batchSize).ToList(); 
    } 

    public tb_Email SelectByID(Guid id) 
    { 
      return unitOfWork.EmailRepository.SelectByID(id); 
    } 

    public void Update(tb_Email obj) 
    { 
     using (unitOfWork = new UnitOfWork()) 
     { 
      unitOfWork.EmailRepository.Update(obj); 
      unitOfWork.Commit(); 
     } 
    } 

} 

SMTPService.cs

public class SMTPService : ISMTPService 
    { 

     SmtpClient client; 
     MailMessage newMessage; 
     EmailService emailService; 
     IEventLoggerService MailCheckerLog; 


     public SMTPService() 
     { 
      emailService = new EmailService(); 
      MailCheckerLog = new EventLoggerService(); 

     } 



     public void SendEmail(tb_Email email) 
     { 

      try 
      {// rest of the code ..... 

       newMessage = new MailMessage(); 

       newMessage.Headers.Add("X-Email_Id", email.Id.ToString()); 


       client.SendCompleted += (sender, e) => SendCompletedCallback(sender, e); 

       tb_Email userState = email; 


       // 
       // if I put the update database logic here, it works fine 
       // 


       client.SendAsync(newMessage, userState); 

      } 
      catch (Exception e) 
      { 
       MailCheckerLog.log("Error in SendComplete event handler - Exception: " + e.Message.ToString() + " -- InnerException: " + e.InnerException.Message, EventLogEntryType.Error); 
       client.Dispose(); 
       newMessage.Dispose(); 
       throw; 
      } 

     } 
     void SendCompletedCallback(object sender, System.ComponentModel.AsyncCompletedEventArgs e) 
     { 

      tb_Email email = (tb_Email)e.UserState; 
      Console.WriteLine("----------------------------------" + emailID.Id); 
      email.ReadyToSend = false; 
      emailService.Update(email); 

      client.Dispose(); 
      newMessage.Dispose(); 

     } 

    } 

Le problème:

donc d'envoyer des e-mails et de processus je cours méthode SendEmail dans une boucle simple avec un liste des objets tb_Email, une fois que chaque email est envoyé, je dois mettre à jour la base de données. Pour ce faire, j'utilise

 email.ReadyToSend = false; 
     emailService.Update(email); 

dans mon cas SendCompleted, comme j'utilise SendAsync le système va de l'avant et de traiter de nombreux e-mails mais l'événement SendCompleted pourrait tirer un peu plus tard pour chaque e-mail. Pour m'assurer qu'il utilise un dbContext unique et unique, j'utilise une instruction using sur mon instance UoW pour la méthode de mise à jour. Cela fonctionne bien si je mets ma logique de mise à jour dans la méthode SendEmail directement (ce qui n'a aucun sens car j'ai besoin de savoir si l'email a été envoyé avec succès ou non), cependant si je le mets dans l'événement après quelques mises à jour réussies, il suffit de lancer

System.Data.Entity.Core.EntityException: 'Le fournisseur sous-jacent a échoué sur Open.'

Je ne comprends pas comment cela est possible lorsque je crée un nouveau contexte pour chaque opération.

+0

quelle instruction renvoie l'exception? – ashin

+0

C'est this.ctx.SaveChanges(); dans la classe UoW – user65248

Répondre

0

Désolé, je dois répondre à moi-même, le problème était que la variable UOW était encore utilisé par d'autres threads, de sorte que la solution est de déclarer une nouvelle variable pour l'instruction à l'aide dans la méthode de mise à jour, comme ci-dessous

public class EmailService : IEmailService 
{ 

    private IUnitOfWork unitOfWork; 

    public EmailService() 
    { 
     unitOfWork = new UnitOfWork(); 
    } 

    public List<tb_Email> SelectAll(int batchAge, int batchSize) 
    { 
     return unitOfWork.EmailRepository.SelectAll(batchAge, batchSize).ToList(); 
    } 

    public tb_Email SelectByID(Guid id) 
    { 
      return unitOfWork.EmailRepository.SelectByID(id); 
    } 

    public void Update(tb_Email obj) 
    { 
     IUnitOfWork unitOfWorkUpdate; 
     using (unitOfWorkUpdate = new UnitOfWork()) 
     { 
      unitOfWorkUpdate.EmailRepository.Update(obj); 
      unitOfWorkUpdate.Commit(); 
     } 
    } 

}