简体   繁体   中英

UoW and DbContext fail in an event

I have implemented this Windows Service which sends emails using SendAsync method, every 30 seconds in batches of 20. I'm using EF6 and SQL Server 2016. Here is some parts of the codes

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();

        }

    }

The problem:

So to send and process emails I run SendEmail method in a simple loop with a list of tb_Email objects, once each email is sent I have to update the database. To do that, I use

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

in my SendCompleted event, as I'm using SendAsync the system goes ahead and process many emails however the SendCompleted event might fire a bit later for each email. To make sure it is using a unique and single dbContext I am using a using statement on my UoW instance for the update method. This works fine if I put my update logic in SendEmail method directly (which doesn't make any sense as I need to know if the email was sent successfully or not), however If I put it in the event after a few successful updates, it just throw

System.Data.Entity.Core.EntityException: 'The underlying provider failed on Open.'

I don't understand how is it possible when I'm actually creating a new context for each operation.

Sorry I have to answer it myself, the problem was that the UoW variable was still being used by other threads, so the solution is to declare a new variable for the using statement inside the update method, like below

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();
        }
    }

}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM