Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/visual-studio/8.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sql 实体框架下的原子读写_Sql_Entity Framework_Transactions - Fatal编程技术网

Sql 实体框架下的原子读写

Sql 实体框架下的原子读写,sql,entity-framework,transactions,Sql,Entity Framework,Transactions,我有两个不同的进程(在不同的机器上)来读取和更新数据库记录 我需要确保的规则是,只有当记录的值为“Initial”时,才必须更新该记录。另外,在提交之后,我想知道它是否真的从当前进程得到了更新(如果值不是initial) 现在,下面的代码执行如下操作: var record = context.Records .Where(r => (r.id == id && r.State == "Initial")) .FirstO

我有两个不同的进程(在不同的机器上)来读取和更新数据库记录

我需要确保的规则是,只有当记录的值为“Initial”时,才必须更新该记录。另外,在提交之后,我想知道它是否真的从当前进程得到了更新(如果值不是initial)

现在,下面的代码执行如下操作:

var record = context.Records
             .Where(r => (r.id == id && r.State == "Initial"))
             .FirstOrDefault();

if(record != null) {
  record.State = "Second";
  context.SaveChanges();
}
现在有几个问题

1) 从代码中可以看出,在使用状态“Initial”获取记录之后,其他一些进程可能会在该进程执行SaveChanges之前将其更新为状态“Second”。 在这种情况下,我们不必要地将状态覆盖为相同的值。这就是这里发生的情况吗

2) 如果情况1并非如此,那么EntityFramework可能会将上述内容转换为

update Record set State = "Second" where Id = someid and State = "Initial"
并将其作为事务执行。这样,只有一个进程写入值。EF default TransactionScope是这种情况吗

在这两种情况下,我如何确定更新是从我的流程进行的,而不是从其他流程进行的

如果这是内存中的对象,那么在代码中,它将转换为假设多个线程访问相同的数据结构

Record rec = FindRecordById(id);
lock (someobject)
{
    if(rec.State == "Initial")
       {
          rec.State = "Second";
          //Now, that I know I updated it I can do some processing
       }
}

谢谢

通常可以使用两种主要的并发模式:

  • 悲观并发:锁定一行以防止其他人意外更改您当前尝试更新的数据。EF不为这种类型的并发模式提供任何本机支持
  • 乐观并发:引用自:“乐观并发涉及乐观地尝试将您的实体保存到数据库中,希望自实体加载后那里的数据没有更改。如果发现数据已更改,则会引发异常,您必须在再次尝试保存之前解决冲突。”EF支持此模式,并且可以非常简单地使用
关注EF确实支持的乐观并发选项,让我们比较一下您的示例在使用EF乐观并发控制处理和不使用EF乐观并发控制处理时的表现

无并发控制 让我们从数据库中的以下脚本开始:

创建表记录(
Id int identity非空主键,
State varchar(50)不为空
)
插入记录(状态)值(“初始值”)
下面是带有
DbContext
Record
实体的代码:

公共类MyDbContext:DbContext
{
静态MyDbContext()
{
Database.SetInitializer(null);
}
public MyDbContext():base(@“Server=localhost;Database=eftest;Trusted_Connection=True;”){
公共数据库集记录{get;set;}
模型创建时受保护的覆盖无效(DbModelBuilder modelBuilder)
{
基于模型创建(modelBuilder);
modelBuilder.Conventions.Remove();
modelBuilder.Configurations.Add(newrecord.Configuration());
}
}
公开课记录
{
公共int Id{get;set;}
公共字符串状态{get;set;}
公共类配置:EntityTypeConfiguration
{
公共配置()
{
this.HasKey(t=>t.Id);
this.Property(t=>t.State)
.HasMaxLength(50)
.IsRequired();
}
}
}
现在,让我们使用以下代码测试您的并发更新场景:

static void Main(字符串[]args)
{
使用(var context=new MyDbContext())
{
var record=context.Records
其中(r=>r.Id==1和&r.State==“初始”)
.Single();
//从不同的上下文插入偷偷的更新。
使用(var skeelycontext=new MyDbContext())
{
var skeelyrecord=skeelycontext.Records
其中(r=>r.Id==1和&r.State==“初始”)
.Single();
sleekyrecord.State=“偷偷更新”;
skeelycontext.SaveChanges();
}
//尝试更新刚刚被秘密上下文更新和提交的行。
record.State=“第二”;
SaveChanges();
}
}
如果跟踪SQL,您将看到
update
语句如下所示:

更新[dbo].[记录]
设置[状态]=“秒”
其中([Id]=1)
因此,实际上,它并不关心另一个事务是否潜入更新中。它只是盲目地覆盖其他更新所做的任何事情。因此,数据库中该行的
State
的最终值是
'Second'

乐观并发控制 让我们调整初始SQL脚本,将并发控制列包含到表中:

创建表记录(
Id int identity非空主键,
状态varchar(50)不为空,
并发时间戳非空--添加此行版本控制列
)
插入记录(状态)值(“初始值”)
我们还要调整
记录
实体类(DbContext类保持不变):

公共类记录
{
公共int Id{get;set;}
公共字符串状态{get;set;}
//添加此属性。
公共字节[]并发{get;set;}
公共类配置:EntityTypeConfiguration
{
公共配置()
{
this.HasKey(t=>t.Id);
this.Property(t=>t.State)
.HasMaxLength(50)
.IsRequired();
//添加此配置以告知EF
//属性/列应用于
//并发检查。
this.Property(t=>t.Concurrency)
.IsRowVersion();
}
}
}
现在,如果我们尝试重新运行前面场景中使用的
Main()
方法,您将不会