What is concurrency?
Concurrency is divided into pessimistic concurrency and optimistic concurrency.
Pessimistic concurrencyFor example, if two users a and B log on to the system and modify a document, if a first changes the document, the system locks the document and B cannot open it, B can be modified only when a is completely exited.
Optimistic Concurrency: As shown in the preceding example, users a and B log on to the ECS instance at the same time. A is modifying the document while B is modifying it. If B saves its modifications after a is saved, the system detects that the document records in the database are inconsistent with that of B when B enters the database. B throws an exception during saving and the modification fails.
How to Control concurrency in EF?
Entity Framework does not support pessimistic concurrency, but supports optimistic concurrency.
To Concurrently process a table, add a timestamp field to the table. Note that a table can have only one timestamp field.
Data annotationsUse timestamp to identify and set the concurrency control field. The field identified as timestamp must be of the byte [] type.
public class Person { public int PersonId { get; set; } public int SocialSecurityNumber { get; set; } public string FirstName { get; set; } public string LastName { get; set; } 1541178944 public byte[] RowVersion { get; set; } }
Fluent APIIsrowversion Method
modelBuilder.Entity<Person>().Property(p => p.RowVersion).IsRowVersion();
In the generated database, rowversion is of the timestamp type.
Let's write a piece of code to test it:
Static void main (string [] ARGs) {var person = new person {firstname = "Rowan", lastname = "Miller", socialsecuritynumber = 12345678}; // Add a record, save to database using (VAR con = new breakawaycontext () {con. people. add (person); con. savechanges ();} var fircontext = new breakawaycontext (); // obtain the first record and modify a field: The firstname is modified. // var p1 = fircontext is not saved first. people. firstordefault (); p1.firstname = "Steven"; // create another context, take the first record, modify the lastname field, and save using (VAR seccontext = new breakawaycontext ()) {var P2 = seccontext. people. firstordefault (); p2.lastname = "Francis"; seccontext. savechanges ();} Try {fircontext. savechanges (); console. writeline ("saved successfully");} catch (dbupdateconcurrencyexception ex) {console. writeline (ex. entries. first (). entity. getType (). name + "failed to save");} console. read ();}
We have instantiated three dbcontext above. The first one adds a record to the database, and the second one modifies the newly added record but does not save it, the third context also retrieves and saves the newly added record, and then saves the second context. The result fails to be saved.
We can see that our concurrency control has taken effect.
Analyze the SQL statements generated by EF:
exec sp_executesql N‘update [dbo].[People]set [LastName] = @0where (([PersonId] = @1) and ([RowVersion] = @2))select [RowVersion]from [dbo].[People]where @@ROWCOUNT > 0 and [PersonId] = @1‘,N‘@0 nvarchar(max) ,@1 int,@2 binary(8)‘,@0=N‘Francis‘,@1=1,@2=0x00000000000007D1
As you can see, it also uses rowversion as the filter condition when getting the corresponding record. When the seccontext in the preceding example is saved, the value of the rowversion field in the database changes. Therefore, when the fircontext is saved, the original rowversion value is used. In this case, no corresponding record is obtained and an error is returned.
What if we only want to control the concurrency of a field? Don't worry, EF also has a solution.
Data annotationsIdentified by concurrencycheck
public class Person { public int PersonId { get; set; } [ConcurrencyCheck] public int SocialSecurityNumber { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public byte[] RowVersion { get; set; } }
Fluent APIUse isconcurrencytoken
modelBuilder.Entity<Person>().Property(p => p.SocialSecurityNumber).IsConcurrencyToken();
In the above Entity, we mark socialsecuritynumber (Social Security number) as open concurrency, and also write a similar code to test it:
Static void main (string [] ARGs) {var person = new person {firstname = "Rowan", lastname = "Miller", socialsecuritynumber = 12345678}; // Add a record, save to database using (VAR con = new breakawaycontext () {con. people. add (person); con. savechanges ();} var fircontext = new breakawaycontext (); // obtain the first record and modify the socialsecuritynumber field. // do not save var p1 = fircontext. people. firstordefault (); p1.socialsecuritynumber = 123; // create another context, take the first record, // modify the socialsecuritynumber field and save using (VAR seccontext = new breakawaycontext ()) {var P2 = seccontext. people. firstordefault (); p2.socialsecuritynumber = 456; seccontext. savechanges ();} Try {fircontext. savechanges (); console. writeline ("saved successfully");} catch (dbupdateconcurrencyexception ex) {console. writeline (ex. entries. first (). entity. getType (). name + "failed to save");} console. read ();}
The running results are also failed to be saved, indicating that our concurrency control takes effect.
Analyze the SQL statement executed by EF:
exec sp_executesql N‘update [dbo].[People]set [SocialSecurityNumber] = @0where (([PersonId] = @1) and ([SocialSecurityNumber] = @2))‘,N‘@0 int,@1 int,@2 int‘,@0=123,@1=1,@2=12345678
We can see that EF uses the socialsecuritynumber column to be concurrently controlled as a filter condition, so that the value of socialsecuritynumber in the database will change during the fircontext saving, failed to update because the corresponding record cannot be obtained.
Add: For edmx, how does one set the field to concurrency. You can right-click the corresponding field and choose Properties. There isConcurrency Mode, You chooseFixedYou can.
Entity Framework concurrent processing