Instead of putting rollback into a try/catch you could also implement a Dispose method the calls rollback if the initial BeginTransaction has not been commited yet. So when the transaction goes out of scope without being commited, Roolback ist called automagically. If you'd want nested transactions you can implement a NumberOfOpenTransactions counter
The Transaction object will also call Rollback in its Dispose method. However, I don't like this implicit behavior and prefer to make it explicit with a try/catch block.
@@MilanJovanovicTech So if Rollback is already being executed in a Dispose method anyway, what's the use of having to call it implicitely? No offense, Milan, but IMHO the try/catch/Rollback are unneeded, even introduce redundant code, and therefore should be removed for the sake of simplicity and good readability. The using statement already tells the reader explicitely that unmanaged resources (in this case the transaction) are freed when the using block goes out of the scope
@@JoeMeyer8998 There was an issue you could run into if you don't rollback manually. I'm trying to find the documentation link I ran into for reference, but can't find it at the moment :(
Thanks Milan! The debugging part is really the cherry on top of an already delicious cake. You are very good at this, I'm proud I found this channel. Your content is gold mate, I'm having a lot of fun =)
it's not all, you didn't cover read committed, no need to have a couple of saves for that scenario, just do the validation for some uniqueness and make one save, call this logic concurrently and you'll be surprised =)
Nicely put. I had an idea what transactions are used for and have only ever seen 1-2 implementations of them, but with debugging them in this video I understood them more.
I have trouble in this bug "System.InvalidOperationException: 'A second operation was started on this context instance before a previous operation completed", I mean I just executed transaction for the first time but the second was not
Thanks Milan! My question is regarding "BeginTransaction()" method and it return type. You are telling at the end, that is possible to return a different type to expose less methods. Could you give a code example, because i did not understood this point. Thanks !!!
The DbContext stores a 'CurrentTransaction' instance internally. You can just wrap the calls to BeginTrasaction/CommitTransaction, and don't need to return the transaction instance.
I've noticed, you're using EF's entities as your domain model. There is a kind of implicit dependency on EF because EF applies some constraints on how we should design our entities (additional constructors, setters, etc.). Is it practical to don't use EF's entities inside a domain model? Which way do you recommend?
I've done it like this many times before, and didn't have a problem. With EF's fluent configuration you can get around most issues with encapsulating a domain model. You can have two separate models, one for the domain and one for EF, but I find it cumbersome to have to maintain two models if I can get by with just one.
Great work. Thank you. One question I have on your code. After if (attendeeResult.IsSuccess) should you not put "else" and inside it to roll back the transaction ? I mean if it is all or nothing. Thank you.
Don't you think it would be a good idea to also have the read operations within the transaction? Having a more strict isolation level, the read rows would also be locked by the db to make the whole method atomic from the perspective of the db.
Hi Milan, I have done your same thing with unit of work and injected dbcontext and other services that I use in my project, for dependency injection i have used autofac and registered dbcontext as InstanceForLifetimeScope so all injected services in my unit of work capture the same dbcontext, because every service inject dbcontext, all works if I do begintransaction the first time, but if I want to do again i get exception that dbcontext has already a transaction, shouldn't be disposed automatically with autofac or at least the transaction.commit() close the transaction, what if I want to get new transaction every request and not keeping the old? Thanks in advance. I hope i was clear.
I inject unit of work in form and I want to use every time that I press the save button, that can be pressed multiple times, but every time I need to create new transaction, if I do like you did the previous transaction is not disposed and I get the exception that the transaction has already been started, how can I fix that? Thanks in advance.
There is a subtle problem with the implementation. The get gathering by id call is still a separate transaction (since every sql statement is treated as an implicit transaction, in case we dont specify explicitly) and in case of concurrent writes, it might lead to the invitation object becoming stale by the time we try to write to db. Obviously it will throw and the catch clause would ensure graceful exits but we can improve the number of failed writes by having the read call within the same transaction in which we are writing. Please let me know ur thoughts... also nice video.. cheers 😊😊
| Obviously it will throw Only if we have optimistic concurrency. The default isolation level is ReadUncommitted, which doesn't lock rows in a transaction - so that won't help with the first query.
Very good tutorial👏🏻! I am still wondering how to handle the transaction when dealing with several different data repositories. How to pass a subset of the repositories selected in a given context and validate it at the end?
Thanks Milan., But i don't see what the RollBack is actually for, since anyway as long as we haven't reached the transaction.Commit(), nothing is written in the database ? Could you please detail that point ?
For closing the transaction, and reverting any partial changes. For example, you could have a few SaveChanges calls in a single transaction. And those changes should be visible to queries in the same transaction.
So now the real issue: How to commit it async? I am using TransactionScope which can wrap multiple DbContext instances on the same connection string but there is this issue - the async version of the commit call is not available.
@@MilanJovanovicTech Then is there a workaround you suggest e.g. with Task.Run()? Do you think it is a good idea? I am thinking of a server process doing these transactions, that might be a real bottleneck when it's blocking calls.
@@MilanJovanovicTech when You will enable transactionScopeAsyncFlowOption it will works, additionally if I will have for example 5 repositories instances (for example I will call methods from injected classes) I don't care about it, just all saveChanges taken to one Transaction. I'm just wondering if TransactionScope is more elastic solution. (Of course all depends :) but what do You think?)
I like al of your series, I'm in the industry almost for 8 years, I rarely seen people doing videos with good content. Very informative. Keep it up buddy.