7

Using db first approach, I want my application to throw a concurrency exception whenever I try to update an (out-of-date) entity which it's correspoinding row in the database has been already updated by another application/user/session.

I am using Entity Framework 5 on .Net 4.5. The corresponding table has a Timestamp column to maintain row version.

3 Answers 3

11

I have done this in the past by adding a timestamp field to the table you wish to perform a concurrency check. (in my example i added a column called ConcurrencyCheck)

There are two types of concurrency mode here depending on your needs :

1 Concurrency Mode: Fixed :

Then re-add/refresh your table in your model. For fixed concurrency , make sure your set your concurrency mode to fixed for your table when you import it into your model : like this :

enter image description here

Then to trap this :

    try 

    { 

    context.SaveChanges(); 

    } 

    catch (OptimisticConcurrencyException ex) { 


////handle your exception here...

2. Concurrency Mode: None

If you wish to handle your own concurrency checking , i.e. raise a validation informing the user and not even allowing a save to occur then you can set Concurrency mode None.

1.Ensure you change the ConcurrencyMode in the properties of the new column you just added to "None". 2. To use this in your code , i would create a variable to store your current timestamp on the screen you which to check a save on.

private byte[] CurrentRecordTimestamp 
        { 
            get 
            { 
                return (byte[])Session["currentRecordTimestamp"]; 
            } 

            set 
            { 
                Session["currentRecordTimestamp"] = value; 

            } 
        }

1.On page load (assuming you're using asp.net and not mvc/razor you dont mention above), or when you populate the screen with the data you wish you edit , i would pull out the current record under edit's ConcurrencyCheck value into this variable you created.

 this.CurrentRecordTimestamp = currentAccount.ConcurrencyCheck;

Then if the user leaves the record open , and someone else in the meantime changes it , and then they also attempt to save , you can compare this timestamp value you saved earlier with the concurrency value it is now.

if (Convert.ToBase64String(accountDetails.ConcurrencyCheck) != Convert.ToBase64String(this.CurrentRecordTimestamp)) 
{ 
} 
Sign up to request clarification or add additional context in comments.

5 Comments

This workaround is something I already pointed out in my own answer. It's disadvantage is that whenever the .edmx file is re-created then the modification will be lost silently.
What error are you getting with it ? 'None' works for the scenario stated above in my solution.If you follow my example above it works fine.I have this implemented in a live system now for 12 months with no issues.
I clarify: the second solution works only when the concurrent changes in just a certain application space are considered. For example if you run a query to modify the row in Management Studio the solution fails.
Moreover, in your solution the value of Timestamp should be stored in somewhere with wider scope than Session. What if the concurrent modification is done on another machine?
@Alireza Can't you just create a MetadataType so that the attribute does not get lost when you modify your EDMX...?
5

After reviewing many posts here and on the web explaining concurrency and timestamp in Entity Framework 5, I came into the conclusion that basically it is impossible to get a concurrency exception when the model is generated from an existing database.

One workaround is modifying the generated entities in the .edmx file and setting the "Concurrency Mode" of the entity's timestamp property to "Fixed". Unfortunately, if the model is repeatedly re-generated from the database this modification may be lost.

However, there is one tricky workaround:

  1. Initialize a transaction scope with isolation level of Repeatable Read or higher

  2. Get the timestamp of the row

  3. Compare the new timestamp with the old one

  4. Not equal --> Exception

  5. Equal --> Commit the transaction

The isolation level is important to prevent concurrent modifications of inferring.

PS: Erikset's solution seems to be fine to overcome regenerating the model file.

4 Comments

My answer to this question: stackoverflow.com/questions/11980516/… provides a console application that automates the process of finding the timestamp properties and setting the concurrency mode in your edmx file. You could run it as a prebuild step to automate the process.
Unfortunately I think you are right. There is no way automatically, though there is a work item planned for a future release. entityframework.codeplex.com/workitem/588
I've done the same as @Erikest. Created a console app that reads the XML to both set the ConcurrencyMode on all tables and it also renames my conceptual models' primary keys to "ID" so that my POCO models can share a common interface. I added the console app to the solution and use Visual Studio's Task Runner to make the console app easier to get at but it would be cool to turn it into an VS extension.
Thanks for this suggestion. We already had a date modified datetimecolumn. So I applied the 5 steps suggested in this answer on that column. While datetime column approach not bug free and clean, we didn't want to tinker around edmx. Its a shame that even after 4 years, there is no easy way around this.
0

EF detects a concurrency conflict if no rows were affected. Then if you use stored procedures to delete and update you could manually add the timestamp value in the where clause:

UPDATE | DELETE ... WHERE PKfield = PkValue and Rowversionfield = rowVersionValue

Then if the row has been deleted or modified by anyone else the Sql statement affects 0 rows and EF interpret it as concurrency conflict.

7 Comments

Well although you are right, firstly, using stored procedure to execute naitive Sql is something not (directly) related to EF. Secondly, the thing I am talking about is updating an entity via calling context.SaveChanges()
You can attach the stored procedures to your entty and then when you call savechanges EF is going to call the right store procedure for you. If the sp doent update any row you will get the concurrency exception
would you please post a line of code on how to attach a stored procedure to an entity for updating?
In EF designer: 1: Create the Stored procedures 2: Update model from Database and include the stored procedures 3: click the entity and go to "Mapping Details" in the right upper corner of the "Mapping Details window" click "Map Entity to functions" and select the INSERT, DELETE and UPDATE Store procedure for your entity. Give me a while and I'll post some screen shots for each step.
Santiago, once again I should say manual modification of the model will be lost if it is regenerated. Is it possible to quarantee when the model is regenerated the modification are by some trick restored?
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.