Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

The method of managing concurrency by Entity Framework

2025-03-04 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/01 Report--

Most people do not understand the knowledge points of this "Entity Framework Management concurrency method" article, so the editor summarizes the following content, detailed content, clear steps, and has a certain reference value. I hope you can get something after reading this article. Let's take a look at this "Entity Framework Management concurrency method" article.

Understand concurrency

The solution of concurrency management is to allow multiple entities to update at the same time, which in fact means allowing multiple users to perform multiple database operations on the same data at the same time. Concurrency is a way to manage multiple operations on a database while complying with the ACID properties (atomicity, consistency, isolation, and persistence) of database operations.

Imagine the following scenarios where concurrency can occur:

1. Both user An and user B try to modify the same entity.

2. Both user An and user B try to delete the same entity.

3. When user An is trying to modify an entity, user B has deleted the entity.

4. User A has requested to read an entity, and user B updates it after reading the entity.

These scenarios may potentially generate incorrect data. Imagine that hundreds of users try to operate the same entity at the same time. This concurrency problem will have a greater impact on the system.

There are generally two ways to deal with concurrency-related issues:

1. Optimistic concurrency: whenever data is requested from the database, the data is read and saved to the application memory. No display locks are placed at the database level. Data operations are performed in the order received by the data layer.

Pessimistic concurrency: whenever data is requested from the database, the data is read, and then the data is locked, so no one can access the data. This reduces the chances of concurrency-related problems, but the disadvantage is that locking is an expensive operation that degrades the performance of the entire application.

First, understand optimistic concurrency

As mentioned earlier, in optimistic concurrency, whenever data is requested from the database, the data is read and saved to application memory. No explicit locks are placed at the database level. Because this approach does not add explicit locks, it is more scalable and flexible than pessimistic concurrency. Using optimistic concurrency, the point is that if any conflicts occur, the application should handle them personally. Most importantly, when using optimistic concurrency control, there should be a conflict resolution strategy in the application to let application users know whether their changes are not persisted because of conflicts. Optimistic concurrency is essentially about allowing conflicts to occur and then resolving them in an appropriate way.

The following is an example of a strategy for dealing with conflicts.

1. Ignore conflicts / force updates

This strategy is to have all users change the same dataset, and then all modifications go through the database, which means that the database displays the value of the last update. This strategy results in potential data loss because many users' change data is lost and only the last user's changes are visible.

2. Partial update

In this case, we also allow all changes, but the complete row is not updated, only the columns owned by the specific user are updated. This means that if two users update the same record but different columns, both updates will succeed, and changes from both users will be visible.

3. Warn / question the user

When a user tries to update a record, but the record has been changed by another user since he read it, the application warns the user that the data has been changed by another user, and then asks him if he still wants to rewrite the data or check the updated data first.

4. Reject changes

When a user tries to update a record, but the record has been changed by other users since he read it, the user is told that the data is not allowed to be updated because the data has been updated by other users.

Second, understand pessimistic concurrency

Pessimistic concurrency is the opposite of optimistic concurrency, the goal of pessimistic concurrency is to never let any conflict occur. This is achieved by placing an explicit lock on the record before using it. Two types of locks are available on database records:

Read-only lock

Update the lock.

When a read-only lock is placed on a record, the application can only read the record. If the application wants to update the record, it must acquire the update lock on the record. If a read-only lock is added to a record, the record can still be used by requests that want a read-only lock. However, if the lock needs to be updated, the request must wait until all read-only locks are released. Similarly, if an update lock is added to the record, other requests can no longer be locked on the record, and the request cannot be locked until the existing update lock is released.

From the previous description, it seems that pessimistic concurrency can solve all concurrency-related problems, because we don't have to deal with them in the application. However, this is not the case in fact. Before using pessimistic concurrency management, we need to keep in mind that there are many problems and overhead in using pessimistic concurrency. Here are some of the problems with using pessimistic concurrency:

The application must manage all locks being acquired by each operation.

The memory requirements of the locking mechanism will degrade application performance.

Multiple requests wait for each other for the required lock, which increases the possibility of deadlock. For these reasons, EF does not directly support pessimistic concurrency. If we want to use pessimistic concurrency, we can customize the database access code. In addition, LINQ to Entities does not work correctly when using pessimistic concurrency.

Using EF to achieve optimistic concurrency

There are many ways to achieve optimistic concurrency with EF, and let's take a look at them.

1. Create a new console project with the project name: EFConcurrencyApp. The news entity class is defined as follows:

Using System;using System.Collections.Generic;using System.ComponentModel.DataAnnotations;using System.Linq;using System.Text;using System.Threading.Tasks;namespace EFConcurrencyApp.Model {public class News {get; set;} [MaxLength (100)] public string Title {get; set;} [MaxLength (30)] public string Author {get; set;} public string Content {get; set } public DateTime CreateTime {get; set;} public decimal Amount {get; set;}

2. Use the method of data migration to generate the database and fill the seed data.

Namespace EFConcurrencyApp.Migrations {using EFConcurrencyApp.Model; using System; using System.Data.Entity; using System.Data.Entity.Migrations; using System.Linq; internal sealed class Configuration: DbMigrationsConfiguration {public Configuration () {AutomaticMigrationsEnabled = false;} protected override void Seed (EFConcurrencyApp.EF.EFDbContext context) {/ / This method will be called after migrating to the latest version. / / You can use the DbSet.AddOrUpdate () helper extension method / / to avoid creating duplicate seed data. Context.News.AddOrUpdate (new Model.News () {Title = "too expensive house prices in American big cities" young people buy houses by crowdfunding ", Author =" anonymous ", Content =" house prices are too expensive in American big cities young people buy houses by crowdfunding " CreateTime = DateTime.Now, Amount = 0,}, new Model.News () {Title = "is it cruel to kill stray dogs with blood? Then raising the cost is the necessary price, Author = "anonymous", Content = "bloody culling of stray dogs is too cruel?" Then raising the cost is the necessary price, CreateTime = DateTime.Now, Amount = 0,}, new Model.News () {Title = "iPhone 8 or from September 6th release price or from $1100", Author = "Network" Content = "iPhone 8 or September 6th release price or from $1100", CreateTime = DateTime.Now, Amount = 0,}) }}}

3. The database context is defined as follows

Using EFConcurrencyApp.Model;using System;using System.Collections.Generic;using System.Data.Entity;using System.Linq;using System.Text;using System.Threading.Tasks;namespace EFConcurrencyApp.EF {public class EFDbContext:DbContext {public EFDbContext (): base ("name=AppConnection") {} public DbSet News {get; set } protected override void OnModelCreating (DbModelBuilder modelBuilder) {/ / set table name and primary key modelBuilder.Entity (). ToTable ("News") .HasKey (p = > p.Id); base.OnModelCreating (modelBuilder);}

4. Realize the default concurrency of EF

Let's take a look at how EF handles concurrency by default, and now suppose our application is going to update an News value, so we first need to implement these two functions, FindNews () and UpdateNews (), which are used to get the specified News and update the specified News.

The two methods defined in the Program class are as follows:

Static News FindNews (int id) {using (var db = new EFDbContext ()) {return db.News.Find (id);}} static void UpdateNews (News news) {using (var db = new EFDbContext ()) {db.Entry (news). State = EntityState.Modified; db.SaveChanges ();}}

Let's implement a scenario where two users An and B both read the same News entity, and then both users try to update different fields of the entity, such as A updating the Title field and B updating the Author field as follows:

/ / 1. User A gets the news of id=1 var news1 = FindNews (1); / / 2. User B gets the news of id=1 var news2 = FindNews (1); / / 3. User A updates the entity's headline news1.Title = news1.Title + "(update)"; UpdateNews (news1); / / 4. User B updates the entity's Amountnews2.Amount = 10m _ news2 _ UpdateNews

The above code attempts to simulate a concurrency problem. Now, both user An and user B have the same copy of the data, and then try to update the same record. Before executing the code, take a look at the data in the database:

To test, hit a breakpoint when performing step 4:

Before the code after the breakpoint is executed, go to the database to take a look at the data, and you can see that the update of user A has already worked:

Continue to execute the code and see what happens to the data in the database:

As can be seen from the screenshot above, user B's request was successful, while user A's update was lost. Therefore, it is not difficult to see from the above code that if we update the entire piece of data using EF, the last request will always win, that is, the last requested update will overwrite all previously requested updates.

Fourth, design the application of dealing with field-level concurrency

Next, we'll see how to write application code that deals with field-level concurrency issues. The idea of this design approach is that only updated fields will be changed in the database. This ensures that if multiple users are updating different fields, all changes can be persisted to the database.

The key to this is to have the application identify all the columns that the user is requesting to update, and then selectively update those fields for the user. This is achieved in the following two ways:

Method of fetching data: this method will give us a clone of the original model, and only the properties requested by the user will be updated to the new value.

Update method: it checks which property values of the original request model have changed, and then updates only those values in the database.

Therefore, you first need to create a simple method that requires the value of the model property, and then returns a new model where all the property values are the same as the original model property values except for the properties that the user is trying to update. The method is defined as follows:

Static News GetUpdatedNews (int id, string title, string author, decimal amount, string content, DateTime createTime) {return new News {Id = id, Title = title, Amount = amount, Author = author, Content = content, CreateTime = createTime,};}

Next, you need to change the update method. This update method implements the following algorithms for updating data:

1. Retrieve the latest model values from the database according to Id.

2. Check the original model and the model to be updated to find a list of changed properties.

3. Update only the properties that have changed in the model retrieved in step 2.

4. Save changes.

The update method is defined as follows:

Static void UpdateNewsEnhanced (News originalNews, News newNews) {using (var db = new EFDbContext ()) {/ / retrieve the latest model var news = db.News.Find (originalNews.Id) from the database / / then check each property if (originalNews.Title! = newNews.Title) modified by the user {/ / update the new value to the database news.Title = newNews.Title } if (originalNews.Content! = newNews.Content) {/ / update the new value to the database news.Content = newNews.Content } if (originalNews.CreateTime! = newNews.CreateTime) {/ / update the new value to the database news.CreateTime = newNews.CreateTime } if (originalNews.Amount! = newNews.Amount) {/ / update the new value to the database news.Amount = newNews.Amount } if (originalNews.Author! = newNews.Author) {/ / update the new value to database news.Author = newNews.Author;} / / persist to database db.SaveChanges ();}}

Before running the code, look at the data in the database:

Then execute the main program code and make a breakpoint when performing step 4:

Look at the data in the database again and find that the operation of user A has been performed:

Continue to run the program, look at the data in the database again, and find that the operation of user B has also been performed:

As you can see from the screenshot above, the update values requested by two users for the same entity are persisted to the database. Therefore, if the user updates different fields, the program can effectively handle concurrent updates. However, if multiple users update the same field at the same time, this method still displays the value of the last request. Although this approach reduces some concurrency-related problems, it means that we have to write a lot of code to deal with concurrency problems. We'll see later how to use the mechanisms provided by EF to deal with concurrency issues.

Use RowVersion to achieve concurrency

Earlier we saw how EF handles concurrency by default (the last requested data update succeeded), and then how to design an application to handle these problems if multiple users try to update different fields. Next, let's take a look at how EF handles field-level updates when multiple users update the same field.

EF lets us specify field-level concurrency so that if a user updates a field and the field has already been updated by another user, a concurrency-related exception will be thrown. With this approach, we can deal with concurrency-related issues more effectively when multiple users try to update the same field.

If we use the concurrency of specific fields for multiple fields, it will degrade the application performance because the generated SQL will be larger and the more efficient way is to use the RowVersion mechanism. The RowVersion mechanism uses a database feature that creates a new row value whenever a row is updated.

Add an attribute to the News entity class:

1646473255public byte [] RowVersion {get; set;}

Configure properties in the database context:

Protected override void OnModelCreating (DbModelBuilder modelBuilder) {/ / sets the table name and primary key modelBuilder.Entity (). ToTable ("News") .HasKey (p = > p.Id); / / sets the property modelBuilder.Entity (). Property (d = > d.RowVersion). IsRowVersion (); base.OnModelCreating (modelBuilder);}

Delete the previous database, then regenerate the database, and the database schema changes to:

Looking at the data, the RowVersion column shows binary data:

EF now tracks RowVersion column values for concurrency control. Next, try to update different columns:

Using (var context = new EFDbContext ()) {var news = context.News.SingleOrDefault (p = > p.Id = = 1); Console.WriteLine ("title: {0} reward amount: {1}", news.Title, news.Amount.ToString ("C") Context.Database.ExecuteSqlCommand (@ "update news set amount = 229.95 where Id = @ p0", news.Id); news.Amount = 239.95M; Console.WriteLine (string.Format ("title: {0} reward amount: {1}", news.Title, news.Amount.ToString ("C")); context.SaveChanges ();}

When you run the program, the following exception is thrown:

From the exception information thrown, it is clear that an exception DbUpdateConcurrencyException related to concurrency has been thrown, and other information indicates that it may have been modified or deleted since the entity was loaded.

Whenever a user tries to update a record that has been updated by another user, it will get an exception DbUpdateConcurrencyException.

When implementing concurrency, we always write exception handling code to show users a more friendly description. The above code is modified with exception handling mechanism as follows:

Using (var context = new EFDbContext ()) {var news = context.News.SingleOrDefault (p = > p.Id = = 1); Console.WriteLine ("title: {0} reward amount: {1}", news.Title, news.Amount.ToString ("C")); context.Database.ExecuteSqlCommand (@ "update News set Amount = 229.95 where Id = {0}", news.Id)) News.Amount = 239.95M; Console.WriteLine (string.Format ("title: {0} reward amount: {1}", news.Title, news.Amount.ToString ("C")); try {context.SaveChanges ();} catch (DbUpdateConcurrencyException ex) {Console.WriteLine ("concurrent exception: {0}", ex.Message)) } catch (Exception ex) {Console.WriteLine (string.Format ("normal exception: {0}", ex.Message));}}

At this point, we should update the data with the current database values and then change it again. As developers, if we want to assist users, we can use EF's DbEntityEntry class to get the current database values.

Using (var context = new EFDbContext ()) {var news = context.News.SingleOrDefault (p = > p.Id = = 1); Console.WriteLine ("title: {0} reward amount: {1}", news.Title, news.Amount.ToString ("C")); context.Database.ExecuteSqlCommand (@ "update News set Amount = 229.95 where Id = {0}", news.Id)) News.Amount = 239.95M; Console.WriteLine (string.Format ("title: {0} reward amount: {1}", news.Title, news.Amount.ToString ("C")); try {context.SaveChanges ();} catch (DbUpdateConcurrencyException ex) {/ / use this code to update Amount to 239.95 var postEntry = context.Entry (news) PostEntry.OriginalValues.SetValues (postEntry.GetDatabaseValues ()); context.SaveChanges ();} catch (Exception ex) {Console.WriteLine (string.Format ("normal exception: {0}", ex.Message)) }} the above is the content of this article on "Entity Framework Management concurrency methods". I believe we all have a certain understanding. I hope the content shared by the editor will be helpful to you. If you want to know more about the relevant knowledge, please follow the industry information channel.

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report