我在使用带有实体框架和System.Transactions.TransactionScope的Sql ServerCE4来抑制部分事务时遇到了问题。
下面的简化代码来自演示该问题的单元测试。
其思想是在不影响outerScope块(“环境”事务)的情况下使innerScope块(没有事务)成功或失败。这就是TransactionScopeOption.Suppress声明的目的。
但是,代码会失败,因为在outerScope中的第一次插入似乎锁定了整个SomeTable表。在代码中指定的位置,抛出此错误:
"SQL Server Compact等待锁定超时。设备的默认锁定时间为2000ms,桌面的默认锁定时间为5000ms。可以使用ssce: default lock timeout属性在连接字符串中增加默认锁定超时。会话id = 2,线程id = 2248,进程id = 13516,表名= SomeTable,冲突类型=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的说法,似乎“事务作用域中的事务在隔离级别设置为可序列化的情况下执行”
但是,使用以下代码片段更改TransactionScope的隔离级别无济于事:
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()。但这将违背其目的。
这里我漏掉了什么?谢谢。
发布于 2012-05-30 17:30:37
AFAIK SQL Server Compact不支持嵌套事务。
发布于 2012-05-30 17:43:00
你为什么要这样做呢?如果我看一下您的代码,在第一个事务作用域中运行第二个事务作用域与按顺序运行它们没有区别。
这不是SQL Compact、TransactionScope或隔离级别的问题。这是一个错误的应用程序逻辑问题。
每个SaveChanges都在事务中运行-要么由TransactionScope定义的外部事务,要么由内部DbTransaction定义。即使不创建事务,每个数据库命令也有自己的隐式事务。如果您在内部代码块上使用Suppress,您将创建两个并发事务,这两个事务试图插入到同一个表中,而且,如果不完成第二个事务,第一个事务将无法完成;如果不完成第一个=>死锁,则第二个事务将无法完成。
原因是insert命令总是锁定表的一部分,在提交或回滚之前不允许新的插入。我不确定是否可以通过更改事务隔离级别来避免这种情况-如果可以,您很可能需要Read.Uncommitted。
https://stackoverflow.com/questions/10812966
复制相似问题