[entity framework core] Concurrency Management in Entity Fra
https://www.learnentityframeworkcore.com/concurrency concurrency token无论何时,当一个更新操作或者删除操作发生时,即我们的代码调用 SaveChange() 时,target value 上面拿到当前的 concurrency token,与调用 Save Change 之前记录的 concurrency token 相比较. 其原理其实是 EF Core 会在每个 update / delete 语句的 where 条件中增添一个 statement: where xxx = yyy and concurrency token = old token,随后 ef core 会记录此次 operation affect 的行数,如果行数为 0 那么会抛出一个 DbUpdateConcurrencyException. but the exception will be informed to the user. what is the next step? ef core 的并发冲突解决方案两个方案去检测是否并发冲突:
public class Author { public int AuthorId { get; set; } public string FirstName { get; set; } [ConcurrencyCheck] public string LastName { get; set; } public ICollection<Book> Books { get; set; } } 或者 public class SampleContext : DbContext { public DbSet<Author> Authors { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Author>() .Property(a => a.LastName).IsConcurrencyToken(); } } public class Author { public int AuthorId { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public ICollection<Book> Books { get; set; } }
具体的做法就是设置一列为version,自增列,当user a,和 user b 同时获取到这个数据时,version为0,此时 user a 对数据进行了修改,存回到db中,此时version为1,user b对数据也进行了修改,再存入db时发现已经找不到version为0的这行数据了,此时user b会得到一个 DbUpdateConcurrencyException. public class Author { public int AuthorId { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public ICollection<Book> Books { get; set; } [TimeStamp] public byte[] RowVersion { get; set; } } public class SampleContext : DbContext { public DbSet<Author> Authors { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Author>() .Property(a => a.RowVersion) .IsConcurrencyToken() .ValueGeneratedOnAddOrUpdate(); } } public class Author { public int AuthorId { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public ICollection<Book> Books { get; set; } public byte[] RowVersion { get; set; } } resolving a concurrency conflict
the example code like below: using (var context = new PersonContext()) { // Fetch a person from database and change phone number var person = context.People.Single(p => p.PersonId == 1); person.PhoneNumber = "555-555-5555"; // Change the person's name in the database to simulate a concurrency conflict context.Database.ExecuteSqlRaw( "UPDATE dbo.People SET FirstName = 'Jane' WHERE PersonId = 1"); var saved = false; while (!saved) { try { // Attempt to save changes to the database context.SaveChanges(); saved = true; } catch (DbUpdateConcurrencyException ex) { foreach (var entry in ex.Entries) { if (entry.Entity is Person) { var proposedValues = entry.CurrentValues; var databaseValues = entry.GetDatabaseValues(); foreach (var property in proposedValues.Properties) { var proposedValue = proposedValues[property]; var databaseValue = databaseValues[property]; // TODO: decide which value should be written to database // proposedValues[property] = <value to be saved>; } // Refresh original values to bypass next concurrency check entry.OriginalValues.SetValues(databaseValues); } else { throw new NotSupportedException( "Don't know how to handle concurrency conflicts for " + entry.Metadata.Name); } } } } } (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |