很久以前,我们的系统是采用悲观锁定设计的,即在对属于该父对象的任何数据库对象执行任何工作之前,将锁定父对象的行,因此在该对象上只有一个进程可以对该对象进行操作时间。
我需要对一组子对象进行许多复杂的工作,这些子对象可以分为多个部分,每个部分可能成功或失败。为了简化这一点,我有一个连接在父记录上获得了一个锁,然后第二个连接完成了实际的工作,根据需要提交或回滚了每个块。这样可以节省连接工作,而不必在每次完成事务时都必须重新获取锁(并且有可能将该锁丢失给另一个进程)。
所有工作完成后,我想对锁定连接上的锁定父记录进行快速更新。我正在使用Dapper来获取锁定,并使用EF来更新锁定的记录。
但是,当我告诉EF SaveChanges()
时,我得到EntityException
:“在提供程序连接上启动事务时发生错误。有关详细信息,请参阅内部异常。”内部例外是InvalidOperationException
:“连接已经是本地或分布式事务的一部分”。显然,EF忽略了我告诉它使用的ADO事务,并试图以常规方式创建自己的事务。我在做什么错了?
问题区域的简化版本(错误处理和其他似乎与此问题无关的代码已被省略):
public async Task DoWork(long id)
{
using (var transaction = _connection.BeginTransaction())
{
await _connection.QueryAsync(LockStatement, new { id }, transaction);
// do relevant work in 2nd connection
using (var context = new MyContext(_connection))
{
var parent = await context.MyEntities.FindAsync(id);
parent.SomeProperty = newValue;
context.SaveChanges();
}
transaction.Commit();
}
}
我的上下文如下:
public class MyContext : DbContext
{
public MyContext(DbConnection connection)
: base(connection, false)
{
Configuration.ValidateOnSaveEnabled = false;
Configuration.ProxyCreationEnabled = false;
}
public DbSet<MyEntity> MyEntities { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
Database.SetInitializer<MyContext>(null);
modelBuilder.Conventions.Remove<ManyToManyCascadeDeleteConvention>();
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Configurations.Add(new MyEntityConfiguration());
modelBuilder.HasDefaultSchema(string.Empty);
}
}
使用Dapper进行读取,使用EF进行写入对我们来说是非常标准的,并且已经运行了一段时间。这是我们第一次尝试通过EF使用较大的事务,而不是每次保存更改时都让它创建新事务。