none
TransactionScope vs DbContext.SaveChanges() RRS feed

  • Question

  • Hi,

    I'm very used to transactionscope on my components. Usually I build methods that calls savechanges everytime it's needed and I control transactions across several methods using TransactionScope.

    This seems for me very simple and the methods become independent: I can use a method by it's own or I can use it inside a bigger transaction.

    Of course, I know it's terrible to use serializable isolation level, but a small change when creating a transactionscope changes the isolationlevel to readcommitted and everything is fine.

    However, I saw many articles on internet talking about the use of savechanges once per transaction, as a way to manage unity of work. In this case, the transactionscope isn't even needed, because entity framework creates a transaction for each savechanges. I saw a lot of articles talking about this, but none of them managed to make me loose the feeling that my components would have a high level of coupling, because the task of each method will rely on something outside calling savechanges.

    This high level of coupling seems very bad for me, however, there are tons of articles talking about ways to use savechanges like this and not even half of this amount talking about transactionscope with entity framework.

    I'm I missing something? I'm I missing some detail that makes the options with savechanges better than the use of transactionscope with lower coupling across the methods and components ?

    Thank you!

    Dennes


    Dennes [http://bufaloinfo.cloudapp.net] Inscreva-se em meu treinamento on-line de T-SQL - [http://bufaloinfo.cloudapp.net/Cursos/linguagemsql.aspx]

    • Moved by CoolDadTx Friday, February 2, 2018 3:49 PM EF related
    Thursday, February 1, 2018 4:14 PM

All replies

  • What you are talking about and a SaveChanges() in a transaction scope is only applicable for the parent and/or children objects being saved in a single SaveChanges().

    It can't apply if multiple SaveChanges() were being applied because one has to deal with multiple Saves() against/across multiple entities to complete a business need where all the saves must be successful or if it is not successful and rollback in using a System.Transaction where a scope would be warranted.

    Thursday, February 1, 2018 5:53 PM
  • Sorry, I couldn't understand what you are talking about.

    Let's see a small example:

    Using(var ts=new transactionScope()) // I'm simplifying of course

    {

        obj1.method1();

        obj2.method2();

        obj3.method3()

        ts.Complete();

    }

    Each method has its own savechanges. Each method can work alone, or inside a bigger transaction like in this situation.

    Obvious I simplified the code without the needed try/catch.

    I already used this pattern for many systems and I like very much the low coupling between objects and methods, exactly the opposite as doing a single save changes for all the methods.

    My questions continues: Am I missing something? There are so many articles about savechanges architecture that I keep thinking that I'm missing something.

    Cheers,


    Dennes [http://bufaloinfo.cloudapp.net] Inscreva-se em meu treinamento on-line de T-SQL - [http://bufaloinfo.cloudapp.net/Cursos/linguagemsql.aspx]

    Thursday, February 1, 2018 7:20 PM
  • Using(var ts=new transactionScope()) // I'm simplifying of course

    {

        PurchaseOrders.SaveChanges();

        Inventory.SaveChanges();

        Shipping.SaveChanges()

        ts.Complete();

    }

    The above is across business needs. So it all better be successful,  or all of it is rolled backed. 

    individual standalone save.........

    Shipping Parent with Address in    ---->  Shipping.Addresses

    Savechanges() on Shipping with Addresses is in a transaction, and  it all is successful or it's rolled back.

    Thursday, February 1, 2018 8:41 PM
  • "The above is across business needs. So it all better be successful,  or all of it is rolled backed. "

    Yes, that's the objective, a transaction. So, you think transactionScope is better than SaveChanges on this situation?

    "Individual standalone save.........

    Shipping Parent with Address in    ---->  Shipping.Addresses

    Savechanges() on Shipping with Addresses is in a transaction, and  it all is successful or it's rolled back."

    I'm not saying Savechanges() doesn't work - it does! However, from my point of view, it creates a very bad coupling.

    For example: There would be two components: ShippingParent and Addresses. Using transaction scope, each one has it's own SaveChanges. I would create the method RegisterNewShipping in a 3rd component and call both components, ShippingParent and Addresses, from inside a transactionScope, achieving the needed transaction. By doing this, I can still use the Addresses component for other tasks, for example, change a single address of an existing ShippingParent, with no worries.

    On the other hand, if I call Addresses from ShippingParent and use a single SaveChanges on ShippingParent the Addresses component doesn't work by it's own. When I need to call it to change a single Address I would need to remember that this component doesn't use SaveChanges, I would need to put the SaveChanges in another place, I would need to thing about Addresses implementation and that is a very high coupling.

    Worse than that: Using the SaveChanges pattern I can't reuse the ShippingParent method inside a bigger transaction that involves the ShippingParent registration, because if I try to do that, I would end with two SaveChanges for the transaction and I wouldn't really be a transaction.

    As you may be noticing, from my point of view, TransactionScope is better than SaveChanges(). However, there are plenty of articles talking about SaveChanges() patterns and not even half about TransactionScope. Why? That's the question. Am I missing something? 

    Cheers,


    Dennes [http://bufaloinfo.cloudapp.net] Inscreva-se em meu treinamento on-line de T-SQL - [http://bufaloinfo.cloudapp.net/Cursos/linguagemsql.aspx]

    Friday, February 2, 2018 8:12 AM
  • "The above is across business needs. So it all better be successful,  or all of it is rolled backed. "

    Yes, that's the objective, a transaction. So, you think transactionScope is better than SaveChanges on this situation?

    Yes it is what I do in this scenario where transactions are being saved across database tables whether I am using EF or not. It's database 101 that any developer should know and understand.

    For example: There would be two components: ShippingParent and Addresses. Using transaction scope, each one has it's own SaveChanges.

    Well if one was doing it manually, then use the parent and child would have independent SaveChanges(); which is basically the developer is controlling the save where the save of the parent and child would need encompassed in a single Transaction.Scope.

    On the other hand, if the parent is modified and a child within the parent.child.collection is modified too, then there is only one SaveChanges where the transaction scope used by EF in viable and valid.

    Worse than that: Using the SaveChanges pattern I can't reuse the ShippingParent method inside a bigger transaction that involves the ShippingParent registration, because if I try to do that, I would end with two SaveChanges for the transaction and I wouldn't really be a transaction.

    It sounds like you are using EF code first that I don't even bother with, because it hurts my head just thinking about using code first. I use database first that eliminates a lot of hassle.

     

    Friday, February 2, 2018 10:56 AM
  • Hi,

    "Yes it is what I do in this scenario where transactions are being saved across database tables whether I am using EF or not. It's database 101 that any developer should know and understand."

    Use traction, yes, it's database 101. However, there are a huge amount of people choosing to use only savechanges and relying on the transaction entity framework creates during savechanges, avoiding the use of transactionScope.

    That's the question: Both work, of course, but in my opinion transactionScope creates a much better code with low coupling, but a huge amount of people not even consider it.

    I'm not using code first. It's very difficult to find someone that also thinks like this.

    Cheers,


    Dennes [http://bufaloinfo.cloudapp.net] Inscreva-se em meu treinamento on-line de T-SQL - [http://bufaloinfo.cloudapp.net/Cursos/linguagemsql.aspx]

    Friday, February 2, 2018 11:17 AM
  • Throwing my 2 cents in here. If you're using DbContext.SaveChanges you don't need a transaction. As documented in MSDN SaveChanges wraps the entire DB update in a transaction. So it either all works or it doesn't.

    There are a couple of cases where you might want to use your own transaction. The first case is when you want to call SaveChanges multiple times. This actually goes against the concept of Unit of Work which is what a DbContext represents in EF. Code shouldn't worry about where data resides (defeats the purpose of a data layer). It should just identify what work should be atomic. Hence a DbContext should be used for each set of changes. But if you really wanted to use a transaction for multiple saves you could. The aforementioned link demonstrates using Database.BeginTransaction to allow you to control the transaction scope.

    The other use case is when you are working across databases and/or resources. In this case no amount of EF transactions are going to help. This is really why TransactionScope was added to the framework in v2. It is specifically designed for distributed cases. You really should only be using this type when you have that type of situation. While it does have some optimizations to use a local transaction in some cases, in my experience using TransactionScope even against databases on the same server will generally require DTC. Using DTC requires it to be running which means the server the app is running on has to enable DTC (which isn't on by default). This adds complexity to deployments. 

    So, in summary. Do not use TransactionScope unless you need to use transactions across systems or with non-database resources. Use DbContext.SaveChanges to save all the work in a unit following the unit of work pattern. If you need to expose "save" functionality to client code then consider using an intermediate service layer that uses the save to just update the context. In the rare cases where you really need to call DbContext.SaveChanges multiple times then use Database.BeginTransaction to wrap them all in a transaction.


    Michael Taylor http://www.michaeltaylorp3.net

    Friday, February 2, 2018 3:49 PM
  • Hi, Michael, 

    Thank you for your contribution about this subject...

    "There are a couple of cases where you might want to use your own transaction. The first case is when you want to call SaveChanges multiple times. This actually goes against the concept of Unit of Work which is what a DbContext represents in EF. "

    Does it really goes against unit of work, or is it just using TransactionScope to implement unit of work, instead of DbContext and SaveChanges? Because, if I'm not mistaking, what transactionScope is doing is exactly this - managing a unit of work. Am I mistaken?

    "Code shouldn't worry about where data resides (defeats the purpose of a data layer)."

    I missed something here. From my point of view, if I use transactionScope the code has no worries about where the data resides, it only call several methods to do their tasks. On the other hand, if I use savechanges, I need access to the correct context to call savechanges at the end - I'm more worried about where the data resides using savechanges than transactionScope.

    "This is really why TransactionScope was added to the framework in v2."

    If I'm not mistaken, before version 6 entity framework was not using transactions for saveChanges, so we should use transactionScope also. Anyway, transactionScope is older than entity framework.

    "in my experience using TransactionScope even against databases on the same server will generally require DTC. Using DTC requires it to be running which means the server the app is running on has to enable DTC (which isn't on by default). "

    Could you give more details? I know how DTC works, but in my experience the transactionScope controls very well if it needs or not to escalate to a distributed transaction. So, if there is no need, it will use regular transactions. Have you found problems with this?


    Thank you !


    Dennes [http://bufaloinfo.cloudapp.net] Inscreva-se em meu treinamento on-line de T-SQL - [http://bufaloinfo.cloudapp.net/Cursos/linguagemsql.aspx]

    Friday, February 2, 2018 4:08 PM
  • "Does it really goes against unit of work, or is it just using TransactionScope to implement unit of work, "

    TransactionScope would be a unit of work but if it is used outside your data layer than it is allowing data concerns to bubble up into your app. It is appropriate to use it, if you need it, in the data layer. But using it "just because" makes no sense. Why set up a transaction to wrap code that is already in a transaction? It just slows things down. Of course if you really need distributed or non-DB resource transactions then it makes sense. But I don't believe it does in other cases.

    "I'm more worried about where the data resides using savechanges than transactionScope."

    I don't see why. In my opinion SaveChanges and that final Commit call do the exact same thing. You could just as easily make the argument that you want to sprinkle Commit calls in all your code and use SaveChanges to make the final "save". One view is action-focused while the other is database-focused. 

    "If I'm not mistaken, before version 6 entity framework was not using transactions for saveChanges"

    The EF team would need to chime in but that isn't what MSDN says.

    "In all versions of Entity Framework, whenever you execute SaveChanges() to insert, update or delete on the database the framework will wrap that operation in a transaction."

    The key here is the "In all versions". The document discusses EF6+ changes related to transactions but this first statement indicates it has always worked this way. This document (from VS 2010) confirms this as well.

    "Could you give more details?"

    Unfortunately I don't remember the details. We have multiple databases residing on the same machine and multiple instances of SQL as well. We found that when we used TransactionScope that DTC pretty much always kicked in. This required the DBAs to enable it on the SQL servers. When we removed the TransactionScope calls we were able to turn DTC back off. I'll leave you with some final links to some other postings on this topic. At the end of the day you should do what you feel is architecturally best. You ended your post initially with the question about "why don't people agree with me". I'm just trying to provide the counter point as to why folks might not agree with you. This, like the best ORM to use, is an opinionated topic.

    https://coderwall.com/p/jnniww/why-you-shouldn-t-use-entity-framework-with-transactions

    https://msdn.microsoft.com/library/bb738523(v=vs.100).aspx (this demos using TransactionScope with context to sync it with an MQ).


    Michael Taylor http://www.michaeltaylorp3.net

    Friday, February 2, 2018 4:50 PM
  • Use traction, yes, it's database 101. However, there are a huge amount of people choosing to use only savechanges and relying on the transaction entity framework creates during savechanges, avoiding the use of transactionScope.

    I would say most don't even know about it, particular if they are using ASP.NET MVC and EF code first, the cheesecake teachings concerning ASP.NET MVC and EF.

    To be frank about it, they don't have the basic T-SQL or database administration skills either. They don't know about the UOW, Repository or Data Access Object patterns, let alone what is happening with transactions.

    Friday, February 2, 2018 6:47 PM
  • Hi, Michael,

    "TransactionScope would be a unit of work but if it is used outside your data layer than it is allowing data concerns to bubble up into your app. It is appropriate to use it, if you need it, in the data layer."

    I don't think so. TransactionScope is able to control a business transaction. This is a very good way of abstraction, since transactionScope isn't aware even it the methods inside it are using data or webservices, for example, it's there to control a business transaction and the developer doesn't need to be aware of what's inside it, very different than the saveChanges approach. 

    In fact, in my humble opinion, the fact that the code will remain the same if the underlining code is using webservices or transactions is an advantage of the use of the transactionscope instead of savechanges().

    "
    Why set up a transaction to wrap code that is already in a transaction? It just slows things down. Of course if you really need distributed or non-DB resource transactions then it makes sense. But I don't believe it does in other cases."

    Check this explanation I made some messages before. I think it explains a scenario with several advantages for transactionScope against saveChanges:

     Me, some messages above: "
    For example: There would be two components: ShippingParent and Addresses. Using transaction scope, each one has it's own SaveChanges. I would create the method RegisterNewShipping in a 3rd component and call both components, ShippingParent and Addresses, from inside a transactionScope, achieving the needed transaction. By doing this, I can still use the Addresses component for other tasks, for example, change a single address of an existing ShippingParent, with no worries.

    On the other hand, if I call Addresses from ShippingParent and use a single SaveChanges on ShippingParent the Addresses component doesn't work by it's own. When I need to call it to change a single Address I would need to remember that this component doesn't use SaveChanges, I would need to put the SaveChanges in another place, I would need to thing about Addresses implementation and that is a very high coupling.

    Worse than that: Using the SaveChanges pattern I can't reuse the ShippingParent method inside a bigger transaction that involves the ShippingParent registration, because if I try to do that, I would end with two SaveChanges for the transaction and I wouldn't really be a transaction."

    You: "I don't see why. In my opinion SaveChanges and that final Commit call do the exact same thing. "

    Not if you think about a layered architecture. That's the same example about ShippingParent and Addresses above: With transactionScope we have no worries about what each method is doing in its code, but using savechanges() patterns we will be always worried about where there is or there isn't a savechanges().

    "
    The EF team would need to chime in but that isn't what MSDN says."

    From MSDN: "Prior to EF6 the recommended way of providing larger scope transactions was to use a TransactionScope object" - https://msdn.microsoft.com/en-us/library/dn456843(v=vs.113).aspx


    "At the end of the day you should do what you feel is architecturally best. You ended your post initially with the question about "why don't people agree with me". I'm just trying to provide the counter point as to why folks might not agree with you. This, like the best ORM to use, is an opinionated topic."

    Of course. I'm noticing that my architectural choice are pretty much old school, used prior EF 6. Now I'm in need to understand what are new ways to solves some needs, as in the problem of ShippingParent/Addresses above. 

    "https://coderwall.com/p/jnniww/why-you-shouldn-t-use-entity-framework-with-transactions"

    I found this one before, there is a problem with it: It miss the fact that a simple and small configuration can turn transactionScope from serializable to readcommited, solving the problem and allowing us to use it. It's just a matter to use on the right way.
    "https://msdn.microsoft.com/library/bb738523(v=vs.100).aspx"

    Even more interesting if we notice that each part could be in different methods and classes and transactionScope would manage everything as a business transaction, without the need to be aware of the details inside.


    Once again, thank you for your contribution, it's an interesting subject.

    Cheers,


    Dennes [http://bufaloinfo.cloudapp.net] Inscreva-se em meu treinamento on-line de T-SQL - [http://bufaloinfo.cloudapp.net/Cursos/linguagemsql.aspx]

    Friday, February 2, 2018 7:54 PM
  • https://msdn.microsoft.com/en-us/library/dn456843(v=vs.113).aspx

    <copied>

    What EF does by default


    In all versions of Entity Framework, whenever you execute SaveChanges() to insert, update or delete on the database the framework will wrap that operation in a transaction. This transaction lasts only long enough to execute the operation and then completes. When you execute another such operation a new transaction is started.

    <end>

    So one has to use System.Transaction  to encompass multiple SaveChanges() to be a single transaction in scope.

    "in my experience using TransactionScope even against databases on the same server will generally require DTC. Using DTC requires it to be running which means the server the app is running on has to enable DTC (which isn't on by default). "

    Could you give more details? I know how DTC works, but in my experience the transactionScope controls very well if it needs or not to escalate to a distributed transaction. So, if there is no need, it will use regular transactions. Have you found problems with this?

    I used EF 3.5 back in 2008 where the backend of the n-tier solution was using WCF and EF on a Web application server, and DTC had to be enabled between the Web server and the server hosting MS SQL Server. I don't see how that would be any different in today's environment using DTC.

    Saturday, February 3, 2018 4:01 AM
  • Hi guys,

    This has been an interesting discussion. I don't use EF, but I do use TransactionScope (with ReadCommitted).

    Michael's point about DTC is a good one. You can't always tell if the TransactionScope will kick off the need for DTC, usually it's pretty good about doing so only when it's really needed, but sometimes not. Of course, if the machine that you're developing on doesn't even have DTC enabled, then you'd be able to tell really quickly as to whether or not that's a problem.

    Personally, I don't think that the need for enabling DTC is that big of a deal. Yes, there's some additional work that needs to be done when deploying applications, but it's not that bad.

    Our application needs DTC anyway, because we have Transactions that involve two databases and MSMQ at the same time ... TransactionScope is the only way to handle that scenario.

    Like Michael said, "This, like the best ORM to use, is an opinionated topic." I'm of the same opinion as you are, Dennes. I'm a fan, but many aren't. Another very opinionated topic is the use of EF or just rolling your own data access (using Typed DataSets)! I'm definitely in the minority opinion on that topic, but it doesn't bother me at all. If you want to read some pro-TransactionScope material, check out the posts on my blog, just for the heck of it: https://geek-goddess-bonnie.blogspot.com/search?q=transactionscope


    ~~Bonnie DeWitt [C# MVP]

    http://geek-goddess-bonnie.blogspot.com

    Saturday, February 3, 2018 5:55 PM