c# - UoW and DbContext fail in an event -
i have implemented windows service sends emails using sendasync method, every 30 seconds in batches of 20. i'm using ef6 , sql server 2016. here parts of 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 { { 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 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 put update database logic here, 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 send , process emails run sendemail method in simple loop list of tb_email objects, once each email sent have update database. that, use
email.readytosend = false; emailservice.update(email);
in sendcompleted event, i'm using sendasync system goes ahead , process many emails sendcompleted event might fire bit later each email. make sure using unique , single dbcontext using using statement on uow instance update method. works fine if put update logic in sendemail method directly (which doesn't make sense need know if email sent or not), if put in event after few successful updates, throw
system.data.entity.core.entityexception: 'the underlying provider failed on open.'
i don't understand how possible when i'm creating new context each operation.
sorry have answer myself, problem uow variable still being used other threads, solution declare new variable using statement inside update method, 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(); } } }
Comments
Post a Comment