简体   繁体   English

UoW和DbContext在事件中失败

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

EmailRepository.cs 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 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 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 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. 因此,要发送和处理电子邮件,我会在一个简单的循环中使用tb_Email对象的列表运行SendEmail方法,一旦发送了每封电子邮件,我就必须更新数据库。 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. 在我的SendCompleted事件中,当我使用SendAsync时,系统会继续处理许多电子邮件,但是对于每个电子邮件,SendCompleted事件可能会在稍后触发。 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. 为了确保它使用的是唯一的dbContext,我在UoW实例上使用了using语句作为update方法。 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 如果我将更新逻辑直接放在SendEmail方法中(这没有任何意义,因为我需要知道电子邮件是否已成功发送),这会很好地工作,但是如果在几次成功更新后将其放入事件中,它只是抛出

System.Data.Entity.Core.EntityException: 'The underlying provider failed on Open.' System.Data.Entity.Core.EntityException:“基础提供程序在打开时失败。”

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 抱歉,我必须自己回答,问题是其他线程仍在使用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.

相关问题 UOW + Repository + Autofac加载两个不同的DbContext - UOW + Repository + Autofac load two different DbContext DBContext 失败没有为实体类型“文件夹名称空间”找到合适的构造函数 - DBContext fail No suitable constructor was found for entity type 'FolderNamespace' 成功保存更改后,EF Core dbcontext 事务是否可能失败? - Is it possible for an EF Core dbcontext transaction to fail after a successful savechange? 带有UoW更新的EF存储库 - EF Repository with UoW Update DbContext注册完成后如何传递事件处理服务方法? 如果事件处理程序服务依赖于 DbContext 服务 - How to pass event handler service method after DbContext registration is done? If event handler service depent on DbContext service 存储库/ UoW - 使用或不使用? - Repository / UoW - To use or not to use? 生成后事件失败? - Fail on Post-Build event? 在C#中将受保护变量编码为Uow或_uow是否正常? - Is it normal practice to code a protected variable as Uow or _uow in C#? 在 EF 的 DbContext.SaveChangesAsync() 中引发异步事件 - Raise async event in EF's DbContext.SaveChangesAsync() 具有服务层的存储库和UoW模式 - Repository and UoW pattern with service layer
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM