【发布时间】:2014-01-06 21:55:32
【问题描述】:
我有一个命令行工具,用于通过实体框架将数据记录批量导入/导出到应用程序数据库中。该工具适用于将新记录插入数据库,但在尝试更新似乎与 EF 锁定相关的现有记录时遇到超时错误。
我已经阅读了some 的many 其他posts 关于实体框架和死锁的内容,但似乎没有一个答案适用于这种情况。我尝试将我的导入代码包装在 TransactionScope 中以及执行 SET TRANSACTION ISOLATION LEVEL SQL 命令,但都没有解决超时问题。
无论在一次调用SaveChanges 中更新了多少实体,都会发生超时。在下面的示例代码中,我将批量大小设置为 1 到 500 之间的值,总是抛出相同的异常。
这里是更新代码的精简版,后面是异常详细信息和 SQL Server 活动监视器的屏幕截图。
我正在使用从 EDMX 模型(模型优先)初始化的 Entity Framework 5 DbContext 对象。
using(var readContext = new MySourceEntities())
using(var readWriteContext = new MyTargetEntities())
{
var query = "SELECT ..."; // Determine which records to update
var keys = readContext.Database.SQLQuery<int>(query);
// Group the update into batches to improve performance. Batch()
// extension method from MoreLINQ
foreach (var batch in keys.Batch(BATCH_SIZE))
{
var sourceRecords = readContext
.AsNoTracking()
.Where(x => batch.Contains(x.SharedId))
.ToList();
var targetRecords = readWriteContext
.Where(x => batch.Contains(x.SharedId))
.ToLookup(x => x.SharedId);
foreach (var record in sourceRecords)
{
// Enforce a constraint on having only a single match
var target = targetRecords[record.SharedId].Single();
target.Field = record.Field;
}
readWriteContext.SaveChanges(); // <--- Timeout happens here
}
}
我从命令行应用程序运行它,它抛出的具体堆栈跟踪如下:
An error occurred while updating the entries. See the inner exception for details.
at System.Data.Entity.Internal.InternalContext.SaveChanges()
at System.Data.Entity.Internal.LazyInternalContext.SaveChanges()
at System.Data.Entity.DbContext.SaveChanges()
at <snip>
An error occurred while updating the entries. See the inner exception for details.
at System.Data.Mapping.Update.Internal.UpdateTranslator.Update(IEntityStateManager stateManager, IEntityAdapter adapter)
at System.Data.EntityClient.EntityAdapter.Update(IEntityStateManager entityCache)
at System.Data.Objects.ObjectContext.SaveChanges(SaveOptions options)
at System.Data.Entity.Internal.InternalContext.SaveChanges()
Timeout expired. The timeout period elapsed prior to completion of the operation or the server is not responding. The statement has been terminated.
at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
at System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose)
at System.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean& dataReady)
at System.Data.SqlClient.SqlCommand.FinishExecuteReader(SqlDataReader ds, RunBehavior runBehavior, String resetOptionsString)
at System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, Boolean async, Int32 timeout, Task& task, Boolean asyncWrite)
at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method, TaskCompletionSource`1 completion, Int32 timeout, Task& task, Boolean asyncWrite)
at System.Data.SqlClient.SqlCommand.InternalExecuteNonQuery(TaskCompletionSource`1 completion, String methodName, Boolean sendToPipe, Int32 timeout, Boolean asyncWrite)
at System.Data.SqlClient.SqlCommand.ExecuteNonQuery()
at System.Data.Mapping.Update.Internal.DynamicUpdateCommand.Execute(UpdateTranslator translator, EntityConnection connection, Dictionary`2 identifierValues, List`1 generatedValues)
at System.Data.Mapping.Update.Internal.UpdateTranslator.Update(IEntityStateManager stateManager, IEntityAdapter adapter)
当SaveChanges 方法挂起时,SQL Server 活动监视器显示以下查询,全部处于SUSPENDED 状态。红色查询来自readContext 所针对的数据库,蓝色查询来自readWriteContext 所针对的数据库。
另外,被挂起的查询自己看起来并不可疑,只是简单的 SELECT 和 UPDATE 命令。我可以手动运行它们而不会出错。
编辑
这里是运行的query 子句的详细信息,因为它似乎是相关的。该查询跨数据库执行连接,以将记录与SharedId 匹配。从this page 运行sys.dm_os_waiting_tasks 查询,得到下表。
session_id wait_duration_ms wait_type blocking_session_id resource_description program_name text
55 15 ASYNC_NETWORK_IO NULL NULL EntityFramework <cross-db join query>
54 29310 LCK_M_IX 55 pagelock fileid=1... EntityFramework <update query>
查询的内容是这样的
SELECT DB1.dbo.Table1.SharedId
FROM DB2.dbo.Table2 INNER JOIN DB1.dbo.Table1.SharedId
ON DB1.dbo.Table1.SharedId = DB2.dbo.Table2.SharedId
WHERE (
(DB1.dbo.Table1.Field1 <> DB2.dbo.Table2.Field1) OR
(DB1.dbo.Table1.Field2 <> DB2.dbo.Table2.Field2)
)
对我来说最令人惊讶的观察是查询仍然处于活动状态。为什么readContext.Database.SQLQuery() 调用无法完成查询的任何想法?听起来这种等待类型通常表示应用程序错误,但我不确定我是如何触发这种行为的。
【问题讨论】:
标签: c# sql-server entity-framework database-deadlocks