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

  • Compare New GitHub Copilot Free Plan for Visual Studio/VS Code to Paid Plans

    The free plan restricts the number of completions, chat requests and access to AI models, being suitable for occasional users and small projects.

  • Diving Deep into .NET MAUI

    Ever since someone figured out that fiddling bits results in source code, developers have sought one codebase for all types of apps on all platforms, with Microsoft's latest attempt to further that effort being .NET MAUI.

  • Copilot AI Boosts Abound in New VS Code v1.96

    Microsoft improved on its new "Copilot Edit" functionality in the latest release of Visual Studio Code, v1.96, its open-source based code editor that has become the most popular in the world according to many surveys.

  • AdaBoost Regression Using C#

    Dr. James McCaffrey from Microsoft Research presents a complete end-to-end demonstration of the AdaBoost.R2 algorithm for regression problems (where the goal is to predict a single numeric value). The implementation follows the original source research paper closely, so you can use it as a guide for customization for specific scenarios.

  • Versioning and Documenting ASP.NET Core Services

    Building an API with ASP.NET Core is only half the job. If your API is going to live more than one release cycle, you're going to need to version it. If you have other people building clients for it, you're going to need to document it.

Subscribe on YouTube