那里
我首先使用 EF 代码按照以下顺序保存数据:过滤器 => 删除 => 添加,运行附加示例(两个线程同时运行),有时我注意到过滤后没有现有记录返回,有时过滤后有两条记录,我认为/预期的是 - 每次过滤器应该有一个现有的记录返回。此外,在保存更改期间还会出现一些异常。
据我所知,EF默认使用Read Committed隔离级别来执行事务,我认为这意味着在过滤期间,共享锁被放在资源上,但是为什么我可以观察到不存在记录或两个现有记录过滤之后,remove 和 add 操作一起应该是原子操作吧?如果我是对的,过滤后应该只有一条记录。
有什么我错过的吗?如何正确处理这种情况?
请帮忙。
另一个问题: 使用 LastUpdated 列作为并发标记,如何正确处理以下情况: 1. 如果数据库中的实体比上下文中的实体更新,则启动另一个线程将实体归档到历史数据库。2. 如果数据库中的实体比上下文中的实体旧,重试保存覆盖数据库中的实体。
我是否正确使用下面的代码来处理此案:
internal void SaveChangesEx()
{
bool saveFailed;
do
{
saveFailed = false;
try
{
base.SaveChanges();
}
catch (DbUpdateConcurrencyException ex)
{
saveFailed = false;
ex.Entries.ToList().ForEach(entry =>
{
if (entry.State == System.Data.EntityState.Deleted)
{
var current = base.Set<Customer>().FirstOrDefault();
Customer rfqInDb = (Customer)entry.GetDatabaseValues();
// If the newer one aready exists, start the archive, otherwise, retry by reloading the entity from DB and marking the state as deleted
if (current.LastUpdated < customerInDb.LastUpdated)
{
using (var archiver = new CustomerArchiveDBContext())
{
archiver.RFQS.Add(current);
archiver.SaveChangesEx();
}
saveFailed = false;
}
else
{
//Refresh the context and retry
entry.Reload();
entry.State = System.Data.EntityState.Deleted;
}
}
});
}
} while (saveFailed);
}
脚本:========
CREATE TABLE [dbo].[Customers](
[FirstName] [nvarchar](20) NOT NULL,
[LastName] [nvarchar](60) NULL,
[Company] [nvarchar](250) NULL,
[Telephone] [nvarchar](20) NULL,
[LastUpdated] [datetime] NULL
) ON [PRIMARY]
代码 ========
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Transactions;
using System.Data.Entity.Validation;
using System.Threading.Tasks;
using System.Threading;
using System.Data.Entity.ModelConfiguration;
using System.Data.Entity;
using System.Data.Entity.ModelConfiguration.Conventions;
namespace EFOptimisticConcurrency
{
public abstract class Entity
{
public int Id { get; set; }
}
public class Customer
: Entity
{
public string FirstName { get; set; }
/// <summary>
/// Get or set the surname of this customer
/// </summary>
public string LastName { get; set; }
/// <summary>
/// Get or set the telephone
/// </summary>
public string Telephone { get; set; }
/// <summary>
/// Get or set the company name
/// </summary>
public string Company { get; set; }
public DateTime LastUpdated { get; set; }
}
class CustomerEntityConfiguration
: EntityTypeConfiguration<Customer>
{
/// <summary>
/// Create a new instance of customer entity configuration
/// </summary>
public CustomerEntityConfiguration()
{
//configure keys and properties
this.HasKey(c => c.FirstName);
this.Ignore(c => c.Id);
this.Property(c => c.FirstName)
.HasMaxLength(20)
.IsRequired();
this.Property(c => c.LastName)
.HasMaxLength(60)
.IsRequired();
this.Property(c => c.Company)
.HasMaxLength(250);
//this.Property(c => c.LastUpdated).IsConcurrencyToken();
this.Property(c => c.Telephone)
.HasMaxLength(20);
this.ToTable("Customers");
}
}
public class CustomerContext : DbContext
{
public DbSet<Customer> Customers { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
modelBuilder.Configurations.Add(new CustomerEntityConfiguration()); ;
}
}
public class Program
{
public static volatile int showStopper = 0;
static void Main(string[] args)
{
var color = Console.ForegroundColor;
EntityFrameworkProfiler.Initialize();
Task.Factory.StartNew(() =>
{
while (true)
{
Customer customer = new Customer();
customer.FirstName = "FirstName";
customer.LastName = "Last " + new Random().Next(0, 10000).ToString();
customer.Telephone = "686868";
customer.Company = "MyCity";
customer.LastUpdated = DateTime.Now;
if (showStopper == 2)
{
Console.ReadLine();
showStopper = 0;
}
try
{
Console.WriteLine("Start the Store => " + customer.LastName + " , " + customer.LastUpdated.ToString());
{
int i = 0;
using (var customerConext = new CustomerContext())
{
Console.WriteLine("Start the filter 1 => " + customer.Telephone + " , " + customer.LastUpdated.ToString());
var matched = customerConext.Customers.Where(c => c.Telephone == "686868" && c.LastUpdated < customer.LastUpdated);
foreach (var hit in matched)
{
i++;
customerConext.Customers.Remove(hit);
}
if (i == 2)
{
Console.WriteLine("1 - 2 exist, has the problem now");
showStopper = 2;
}
else if (i == 0)
{
Console.WriteLine("1 - 0 exist, has the problem now");
showStopper = 2;
}
Console.WriteLine("Start Adding 1 => " + customer.LastName + " , " + customer.LastUpdated.ToString());
try
{
customerConext.Customers.Add(customer);
customerConext.SaveChanges();
Console.WriteLine("SaveChanges 1 => " + customer.LastName + " , " + customer.LastUpdated.ToString());
}
catch (Exception ex)
{
Console.WriteLine("Exception 1 : " + ex.Message + " => " + customer.LastName + " , " + customer.LastUpdated);
if (ex.InnerException != null)
{
Console.WriteLine("Inner Exception 2 : " + ex.InnerException.Message + " => " + customer.LastName + " , " + customer.LastUpdated);
}
}
}
}
}
catch (Exception ex)
{
Console.WriteLine("Exception 1 " + ex.Message);
if (ex.InnerException != null)
{
Console.WriteLine(ex.InnerException.Message);
}
showStopper = 2;
}
}
});
Thread.Sleep(10000);
Task.Factory.StartNew(() =>
{
while (true)
{
Console.ForegroundColor = color;
try
{
Customer customer = new Customer();
customer.FirstName = "FirstName";
customer.LastName = "Last " + new Random().Next(0, 10000).ToString();
customer.Telephone = "686868";
customer.Company = "MyCity2";
customer.LastUpdated = DateTime.Now;
if (showStopper == 3)
{
Console.ReadLine();
showStopper = 0;
}
Console.WriteLine("Start the store 2 => " + customer.LastName + " , " + customer.LastUpdated.ToString());
{
int i = 0;
using (var customerConext = new CustomerContext())
{
Console.WriteLine("Start the filter 2 => " + customer.Telephone + " , " + customer.LastUpdated.ToString());
var matched = customerConext.Customers.Where(c => c.Telephone == "686868" && c.LastUpdated < customer.LastUpdated);
foreach (var hit in matched)
{
i++;
customerConext.Customers.Remove(hit);
}
Console.WriteLine("Start Adding 2 => " + customer.LastName + " , " + customer.LastUpdated.ToString());
try
{
customerConext.Customers.Add(customer);
customerConext.SaveChanges();
Console.WriteLine("SaveChanges 2 => " + customer.LastName + " , " + customer.LastUpdated.ToString());
}
catch (Exception ex)
{
Console.WriteLine("Exception 2 : " + ex.Message + " => " + customer.LastName + " , " + customer.LastUpdated);
if (ex.InnerException != null)
{
Console.WriteLine("Inner Exception 2 : " + ex.InnerException.Message + " => " + customer.LastName + " , " + customer.LastUpdated);
}
showStopper = 2;
}
}
if (i == 2)
{
Console.WriteLine("1 - 2 exist, has the problem now");
showStopper = 2;
}
else if (i == 0)
{
Console.WriteLine("1 - 0 exist, has the problem now");
showStopper = 2;
}
}
}
catch (Exception ex)
{
Console.WriteLine("Exception 2 " + ex.Message);
if (ex.InnerException != null)
{
Console.WriteLine(ex.InnerException.Message);
}
}
}
});
Console.WriteLine("PRESS ANY KEY TO END");
Console.ReadLine();
}
}
}