Practical ASP.NET

Handling Concurrency with Entity Framework

If you want to use Entity Framework in ASP.NET, you're going to need to handle concurrency problems. Here are some suggestions from a Microsoft "architect evangelist."

To effectively use Entity Framework (EF) in ASP.NET, we need to handle two problems: tracking changes and handling data concurrency. The tracking changes problem occurs because EF's ObjectContext keeps track of what data was originally retrieved from the database and what's been changed. You could hang on to the ObjectContext while the user is looking at the data in the page, but that would consume memory on the server for every retrieved object in the ObjectContext (and Microsoft's best practice for working with the ObjectContext is to dispose of it quickly; I have to assume there's a reason for that).

Alternatively, when the user submits the page, you could re-fetch the data from the database before applying changes -- simple, but it requires an extra trip to the database server, which is bad for scalability. I also tend to obsess about managing data when two people access the same row at the same time.

In ASP.NET, concurrency is a thorny problem. To start with, we send the data down to the user who can spend an indeterminate time looking at it while completely disconnected from the application. And in ASP.NET, users can wander off to another site or shut down their browsers without notifying us that they're done with the data. As a result, we don't want to lock records while a user has them (see "Never Lock Records in Web Applications! Never! That Means You!").

I've discussed solutions in the past. Most recently, I looked at how to use the ASP.NET ObjectDataSource to ensure that you only update fields that have actually changed ("Handling Concurrency with the ObjectDataSource"). However, that left some readers hanging. They want to prevent updates in case one user changes the data while another user was preparing their changes by using optimistic concurrency (see "Handling Data Contention with Optimistic Concurrency").

Tracking Changes
I've pulled one solution for tracking changes with the EF from Cesar de la Torre's blog. What Cesar is doing is making copies of individual EF entity objects related to the page the user is viewing and storing those objects in the Session. Keeping only the objects displayed on the page substantially reduces the overhead in holding objects in memory.

Cesar's code for creating a copy of an EF entity consists of serializing the Entity object to a MemoryStream (using the DataContractSerializer) and then, later, retrieving that version of the object from the stream. This example (in Visual Basic; Cesar's blog has a C# version) copies an Orders object held in the variable ord to a MemoryStream held in the Session object:

Dim dcs as System.Runtime.Serialization.DataContractSerializer 
dcs = New System.Runtime.Serialization.DataContractSerializer(ord.GetType())
Dim ms As System.IO.MemoryStream 
ms = New System.IO.MemoryStream
dcs.WriteObject(ms, ord)
Me.Session(ord.OrderId.ToString) = ms

On postback, you can de-serialize the object and re-attach it to a new instance of the ObjectContext using the ObjectContext's Attach method. Re-attaching an object to a recreated ObjectContext looks like this:

Dim ms As System.IO.MemoryStream
ms = CType(Me.Session(Me.OrderIdTextBox.Text), System.IO.MemoryStream)
ms.Position = 0
Dim ord2 As Orders
ord2 = CType(dcs.ReadObject(ms), Orders)

ob.Attach(ord2); 

At this point, you can check for changes made in the user interface and apply the changes from the object returned from ObjectDataSource. If you check for changes earlier in the process (as described in my column on the ObjectDataSource) you can skip the de-serialization and update process.

Handling Concurrency in EF
But what about concurrency? The solution that many of my readers have liked is what Microsoft calls "optimistic concurrency": building a Where clause in the SQL statements that checks to see if any fields have changed since the user retrieved the record. As I discussed in that earlier column on optimistic concurrency, you only really need to check one field: the row's timestamp which is changed each time a row is modified.

The good news is that you can get EF to incorporate the original value of any field into the Where clause that it generates. In the EF designer, click on the property in the Entity Object that represents the timestamp and set its Concurrency mode to Fixed. You just need to make sure that (a) your tables have a timestamp field and (b) that it's one of the properties on your Entity objects.

Unlike the ASP.NET SqlDataSource, where optimistic concurrency is a silent failure, with EF when a record isn't found because of a concurrency failure, an OptimisticConcurrencyException is raised. So, with the timestamp field flagged as Fixed, you should now be able to write code like this to handle updating the database with the user's changes:

Dim ob As New OrderEntities
... re-attach Entity objects and apply changes...
Try	
    ob.SaveChanges
Catch ex As System.Data.OptimisticConcurrencyException
   Me.ErrorLabel.Text = "Changes lost."
   ...refresh the page...
End Try

About the Author

Peter Vogel is a system architect and principal in PH&V Information Services. PH&V provides full-stack consulting from UX design through object modeling to database design. Peter tweets about his VSM columns with the hashtag #vogelarticles. His blog posts on user experience design can be found at http://blog.learningtree.com/tag/ui/.

comments powered by Disqus

Featured

  • Hands On: New VS Code Insiders Build Creates Web Page from Image in Seconds

    New Vision support with GitHub Copilot in the latest Visual Studio Code Insiders build takes a user-supplied mockup image and creates a web page from it in seconds, handling all the HTML and CSS.

  • Naive Bayes Regression Using C#

    Dr. James McCaffrey from Microsoft Research presents a complete end-to-end demonstration of the naive Bayes regression technique, where the goal is to predict a single numeric value. Compared to other machine learning regression techniques, naive Bayes regression is usually less accurate, but is simple, easy to implement and customize, works on both large and small datasets, is highly interpretable, and doesn't require tuning any hyperparameters.

  • VS Code Copilot Previews New GPT-4o AI Code Completion Model

    The 4o upgrade includes additional training on more than 275,000 high-quality public repositories in over 30 popular programming languages, said Microsoft-owned GitHub, which created the original "AI pair programmer" years ago.

  • Microsoft's Rust Embrace Continues with Azure SDK Beta

    "Rust's strong type system and ownership model help prevent common programming errors such as null pointer dereferencing and buffer overflows, leading to more secure and stable code."

  • Xcode IDE from Microsoft Archrival Apple Gets Copilot AI

    Just after expanding the reach of its Copilot AI coding assistant to the open-source Eclipse IDE, Microsoft showcased how it's going even further, providing details about a preview version for the Xcode IDE from archrival Apple.

Subscribe on YouTube

Upcoming Training Events