c# - EF6 Transaction rollback & concurrency issue -
once rollback transaction , try again, fails dbupdateconcurrencyexception.
after looking @ sql profiler, see first time insert query sent, expected, while second time tries update, though first 1 rolled back?!
first query:
exec sp_executesql n'insert [dbo].[fiscalreceipt]([purchasetime], [receiptnumber], [cash], [card], [bank]) values (@0, @1, @2, @3, @4) select [fiscalreceiptid] [dbo].[fiscalreceipt] @@rowcount > 0 , [fiscalreceiptid] = scope_identity()',n'@0 datetime2(7),@1 int,@2 decimal(18,2),@3 decimal(18,2),@4 decimal(18,2)',@0='2016-02-08 15:05:43.9145089',@1=666,@2=1.70,@3=0,@4=0
second query:
exec sp_executesql n'update [dbo].[fiscalreceipt] set [purchasetime] = @0, [cash] = @1 ([fiscalreceiptid] = @2) ',n'@0 datetime2(7),@1 decimal(18,2),@2 int',@0='2016-02-08 15:12:11.8101261',@1=555.00,@2=2042
p.s. shouldn't there tm:rollback or tm:commit in eventclass column in sql profiler table when commited?
c# code:
note: outofpaperexception intended ignored , transaction inside scope being commited on purpose. changes should rolled on other exception.
var transaction = databasecontext.database.begintransaction(); try { // commented debugging //ifiscalprinter fprinter = devicemanager.getfiscalprinter(); //var lastreceiptnumber = fprinter.getlastreceiptnumber(); // false data debugging var lastreceiptnumber = 666; receipt.receiptnumber = lastreceiptnumber++; receipt.purchasetime = datetime.now; databasecontext.savechanges(); //fprinter.printfiscalreceipt(receipt); // exception debugging purpose if (receipt.cash < 500) { throw new exception(); } transaction.commit(); close(); } catch (outofpaperexception) { if (transaction != null) { transaction.commit(); close(); } messageboxservice.showmessage("promenite papir pre sledećeg štampanja!", "nema više papira!", messagebutton.ok, messageicon.warning); } catch (exception ex) { // added doesn't receipt.purchasetime = new datetime(); receipt.receiptnumber = 0; //receipt.fiscalreceiptid = 0; <- i'm not allowed if (transaction != null) { transaction.rollback(); } messageboxservice.showmessage(ex.message, "greška!", messagebutton.ok, messageicon.error); } { if (transaction != null) { transaction.dispose(); } }
databasecontext lifetime equal viewmodel's lifetime.
edit: changing appropriate entry states entrystate.added results in successful operation, feels dirty. shouldn't entries remain in added state on transaction rollback/fail?
edit2: after running code:
using (metalshopdb ctx = new metalshopdb()) using (var transaction = ctx.database.begintransaction()) { var receipt = new fiscalreceipt() { receiptnumber = 555, purchasetime = datetime.now, cash = 100 }; ctx.fiscalreceipts.add(receipt); console.writeline("has changes " + ctx.changetracker.haschanges()); console.writeline(ctx.entry(receipt).state); ctx.savechanges(); console.writeline("saved changes"); console.writeline("has changes " + ctx.changetracker.haschanges()); console.writeline(ctx.entry(receipt).state); transaction.rollback(); console.writeline("rolled back"); console.writeline("has changes " + ctx.changetracker.haschanges()); console.writeline(ctx.entry(receipt).state); }
i output, find strange, because 1 expect context in sync db, when rollback, context should follow changes.
has changes true added saved changes has changes false unchanged rolled has changes false unchanged
for edit2
when savechanges methods called, if no error found when saving (which case since rollback after), objectcontext.acceptallchanges() methods invoked accept changes , populate primary keys, foreign keys , change entry state.
the rollback rollback transaction , not on object context / change tracker.
it's late @ point call savechanges again since entities populated database information if did rollback.
original question
you throw error (for debugging) after savechanges has been successfully completed
if (receipt.cash < 500) { throw new exception(); }
so following previous logic, acceptallchanges invoked.
edit
you can control acceptallchanges saving using objectcontext
var objectcontext = ((iobjectcontextadapter) ctx).objectcontext; objectcontext.savechanges(saveoptions.detectchangesbeforesave); transaction.commmit(); objectcontext.acceptallchanges();
Comments
Post a Comment