【问题标题】:UoW and DbContext fail in an eventUoW 和 DbContext 在事件中失败
【发布时间】:2017-08-20 23:37:37
【问题描述】:

我已经实现了这个 Windows 服务,它使用 SendAsync 方法每 30 秒发送一次电子邮件,每批 20 封。我使用的是 EF6 和 SQL Server 2016。这是部分代码

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

        }

    }

问题:

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

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

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

System.Data.Entity.Core.EntityException: '底层提供程序在打开时失败。'

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

【问题讨论】:

  • 哪个语句抛出异常?
  • 这是 this.ctx.SaveChanges();在UoW类

标签: c# .net entity-framework smtpclient


【解决方案1】:

不好意思我自己来回答,问题是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();
        }
    }

}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-08-04
    • 2013-12-25
    • 2013-09-15
    • 2013-04-23
    • 1970-01-01
    • 2019-12-07
    • 2019-04-15
    • 2021-02-10
    相关资源
    最近更新 更多