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").
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
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)
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...
Catch ex As System.Data.OptimisticConcurrencyException
Me.ErrorLabel.Text = "Changes lost."
...refresh the page...
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/.