我在使用带有实体框架和System.Transactions.TransactionScope
的 Sql Server CE 4 抑制部分事务时遇到问题。
下面的简化代码来自演示问题的单元测试。
这个想法是使innerScope
块(没有事务)成功或失败,而不会影响outerScope
块("环境"事务)。这是TransactionScopeOption.Suppress
的既定目的。
但是,代码失败,因为似乎整个SomeTable
表都被 outerScope
中的第一个插入锁定。在代码中指示的点,将引发此错误:
"SQL Server Compact 在等待锁定时超时。设备的默认锁定时间为 2000 毫秒,桌面的默认锁定时间为 5000 毫秒。可以使用 ssce: 默认锁定超时属性在连接字符串中增加默认锁定超时。[ 会话 ID = 2,线程 ID = 2248,进程 ID = 13516,表名 = 某个表,冲突类型 = x 锁定(x 块),资源 = PAG (idx):1046 ]"
[TestMethod()]
[DeploymentItem("MyLocalDb.sdf")]
public void MyLocalDb_TransactionSuppressed()
{
int count = 0;
// This is the ambient transaction
using (TransactionScope outerScope = new TransactionScope(TransactionScopeOption.Required))
{
using (MyObjectContext outerContext = new MyObjectContext())
{
// Do something in the outer scope
outerContext.Connection.Open();
outerContext.AddToSomeTable(CreateSomeTableRow());
outerContext.SaveChanges();
try
{
// Ambient transaction is suppressed for the inner scope of SQLCE operations
using (TransactionScope innerScope = new TransactionScope(TransactionScopeOption.Suppress))
{
using (MyObjectContext innerContext = new MyObjectContext())
{
innerContext.Connection.Open();
// This insert will work
innerContext.AddToSomeTable(CreateSomeTableRow());
innerContext.SaveChanges(); // ====> EXCEPTION THROWN HERE
// There will be other, possibly failing operations here
}
innerScope.Complete();
}
}
catch { }
}
outerScope.Complete();
}
count = GetCountFromSomeTable();
// The insert in the outer scope should succeed, and the one from the inner scope
Assert.AreEqual(2, count);
}
因此,根据 http://msdn.microsoft.com/en-us/library/ms172001,似乎"事务范围内的事务在隔离级别设置为可序列化的情况下执行"
但是,使用以下代码段更改事务范围的隔离级别无济于事:
public void MyLocalDb_TransactionSuppressed()
{
TransactionOptions opts = new TransactionOptions();
opts.IsolationLevel = IsolationLevel.ReadCommitted;
int count = 0;
// This is the ambient transaction
using (TransactionScope outerScope = new TransactionScope(TransactionScopeOption.Required, opts))
...
在同一位置引发相同的异常。
似乎避免这种情况的唯一方法是在进入innerScope
块之前调用outerScope.Complete()
。但这会违背目的。
我在这里错过了什么?谢谢。
AFAIK SQL Server Compact 不支持嵌套事务。
你为什么要这样做?如果我查看您的代码,则在第一个事务范围内运行第二个事务范围与按顺序运行它们之间没有区别。
恕我直言,这不是SQL契约,TransactionScope
或隔离级别的问题。这是应用程序逻辑错误的问题。
每个SaveChanges
都在事务中运行 - 由TransactionScope
定义的外部事务或内部DbTransaction
。即使它不会创建事务,每个数据库命令都有自己的隐式事务。如果在内部代码块上使用 Suppress,您将创建两个并发事务,这些事务试图插入到同一个表中,此外,第一个事务如果不完成第二个事务就无法完成,第二个事务如果不完成第一个 => 死锁就无法完成。
原因是插入命令始终锁定表的一部分,不允许新的插入,直到提交或回滚。我不确定这是否可以通过更改事务隔离级别来避免 - 如果是这样,您很可能需要Read.Uncommitted
。