繁体   English   中英

UoW和DbContext在事件中失败

[英]UoW and DbContext fail in an event

我已经实现了此Windows服务,该服务使用SendAsync方法每20秒发送一次,每30秒发送一次电子邮件。我正在使用EF6和SQL Server2016。这是代码的某些部分

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

        }

    }

问题:

因此,要发送和处理电子邮件,我会在一个简单的循环中使用tb_Email对象的列表运行SendEmail方法,一旦发送了每封电子邮件,我就必须更新数据库。 为此,我使用

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

在我的SendCompleted事件中,当我使用SendAsync时,系统会继续处理许多电子邮件,但是对于每个电子邮件,SendCompleted事件可能会在稍后触发。 为了确保它使用的是唯一的dbContext,我在UoW实例上使用了using语句作为update方法。 如果我将更新逻辑直接放在SendEmail方法中(这没有任何意义,因为我需要知道电子邮件是否已成功发送),这会很好地工作,但是如果在几次成功更新后将其放入事件中,它只是抛出

System.Data.Entity.Core.EntityException:“基础提供程序在打开时失败。”

我不明白当我实际上为每个操作创建一个新的上下文时怎么可能。

抱歉,我必须自己回答,问题是其他线程仍在使用UoW变量,因此解决方案是在update方法内为using语句声明一个新变量,如下所示

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

}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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