EFCore 在复杂的 splitQuery 上抛出 NullReferenceException,但只是有时
EFCore throws NullReferenceException on complex splitQuery, but only sometimes
我有一个 EFCore 查询,对于相同的参数值和相同的数据库状态,有时会抛出 NullReferenceException。有什么想法可以确定问题出在哪里吗?
查询看起来像这样(是的,它是一个有点复杂的数据库结构):
var lines = _context.Calcs.Where(x => x.PlanId == planId && x.IsDeleted == false)
.Include(x => x.Taggings)
.ThenInclude(x => x.CalcTag)
.Include(x => x.Destination)
.ThenInclude(x => x.FormatCode)
.Include(x => x.Destination)
.ThenInclude(x => x.Dimensions.Where(vtd => vtd.PlanId == planId))
.Include(x => x.CalcDimensions)
.ThenInclude(x => x.Dimension)
.Include(x => x.CLVT)
.ThenInclude(x => x.ValueType)
.Include(x => x.CLVT)
.ThenInclude(x => x.CLVTJoinFields)
.ThenInclude(x => x.Dimension)
.Include(x => x.CLVT)
.ThenInclude(x => x.CLVTTagFields)
.Include(x => x.CLVT)
.ThenInclude(clvt => clvt.PreCalc)
.Include(x => x.CLVTPreCalcs)
.ThenInclude(x => x.CLVTJoinFields)
.Include(x => x.CLVTPreCalcs)
.ThenInclude(x => x.CLVTTagFields)
.AsSplitQuery()
.ToList();
对于完全相同的 planId
和数据库状态,有时执行此操作会导致 NullReferenceException
,有时则不会。看起来抛出异常时调用堆栈总是相同的,这很有帮助。
例外情况是:
System.NullReferenceException
HResult=0x80004003
Message=Object reference not set to an instance of an object.
Source=Microsoft.EntityFrameworkCore.Relational
StackTrace:
at Microsoft.EntityFrameworkCore.Query.Internal.SplitQueryingEnumerable`1.Enumerator.Dispose()
at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
at MyClass.MyMethod(Int32 planId) in C:\code\server\MyProduct\MyClass.cs:line 94
从 EFCore 搜索其他 NullReferenceException
个案例时,当 EF 模型期望它不可为空时,当数据库中存在空值时似乎会发生这种情况。但在这种情况下,相同的数据被拉回并且通常没有例外,所以我猜这里不是这种情况。还有什么可能导致这种情况?
我的下一个猜测是,在导致问题的代码路径中,DbContext 发生了其他事情,例如它缓存了一些对象,导致同一查询出现不同的行为。但是我的_context.ChangeTracker.DebugView.ShortView
在抛出这个异常的时候是空的,说明不是这样的。我的上下文通常有 QueryTrackingBehavior==NoTracking
和 AutoDetectChangesEnabled==false
除非它在更新的中间,但我在没有那个的情况下进行了测试(即默认跟踪行为)并得到了相同的结果。
我使用 SQL 服务器扩展事件来跟踪执行的 SQL。我看到大约有 5 个查询正在启动,我 认为 异常发生在 rpc_completed
为它们全部触发之前。我还没有对此进行足够彻底的分析,以了解它是否总是在接收 SQL 服务器结果的同一点抛出异常。但是鉴于每次查询的结果都是相同的,我不确定进一步分析是否会有很大帮助。
任何可能导致异常的建议?
我正在使用 EFCore 5.0.12(我使用的是 5.0.5 但升级后出现了同样的问题)
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer"
Version="5.0.12" />
更新:
现在我认为有多个线程同时使用同一个 DbContext。它们都来自同一个请求,但有些代码有点像这样:
foreach (var x in y)
{
tasks.Add(MyAsyncMethodThatUsesDbContext(planId, otherParams));
}
var z = await Task.WhenAll(tasks);
它有点复杂,所以不那么明显,但我会看看是不是那样。
问题是多线程使用同一个DbContext引起的,这是一件很不礼貌的事情。我的 DbContext 没有被多个请求共享,但在单个后台 Hangfire 作业处理中,有代码创建了一系列任务,然后对它们调用 WaitAll,如下所示:
foreach (var x in y)
{
tasks.Add(MyAsyncMethodThatUsesDbContext(planId, otherParams));
}
var z = await Task.WhenAll(tasks);
我认为当您从多个线程使用相同的 DbContext 时,EFCore 中有一些东西会发出警告,但是当 运行 来自 Hangfire 作业时(即没有活动 asp 请求)或者当任务全部从同一个线程创建时?
一种解决方案是不并行化数据库访问。或者确保为每个任务使用单独的 DbContext。
我有一个 EFCore 查询,对于相同的参数值和相同的数据库状态,有时会抛出 NullReferenceException。有什么想法可以确定问题出在哪里吗?
查询看起来像这样(是的,它是一个有点复杂的数据库结构):
var lines = _context.Calcs.Where(x => x.PlanId == planId && x.IsDeleted == false)
.Include(x => x.Taggings)
.ThenInclude(x => x.CalcTag)
.Include(x => x.Destination)
.ThenInclude(x => x.FormatCode)
.Include(x => x.Destination)
.ThenInclude(x => x.Dimensions.Where(vtd => vtd.PlanId == planId))
.Include(x => x.CalcDimensions)
.ThenInclude(x => x.Dimension)
.Include(x => x.CLVT)
.ThenInclude(x => x.ValueType)
.Include(x => x.CLVT)
.ThenInclude(x => x.CLVTJoinFields)
.ThenInclude(x => x.Dimension)
.Include(x => x.CLVT)
.ThenInclude(x => x.CLVTTagFields)
.Include(x => x.CLVT)
.ThenInclude(clvt => clvt.PreCalc)
.Include(x => x.CLVTPreCalcs)
.ThenInclude(x => x.CLVTJoinFields)
.Include(x => x.CLVTPreCalcs)
.ThenInclude(x => x.CLVTTagFields)
.AsSplitQuery()
.ToList();
对于完全相同的 planId
和数据库状态,有时执行此操作会导致 NullReferenceException
,有时则不会。看起来抛出异常时调用堆栈总是相同的,这很有帮助。
例外情况是:
System.NullReferenceException
HResult=0x80004003
Message=Object reference not set to an instance of an object.
Source=Microsoft.EntityFrameworkCore.Relational
StackTrace:
at Microsoft.EntityFrameworkCore.Query.Internal.SplitQueryingEnumerable`1.Enumerator.Dispose()
at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
at MyClass.MyMethod(Int32 planId) in C:\code\server\MyProduct\MyClass.cs:line 94
从 EFCore 搜索其他 NullReferenceException
个案例时,当 EF 模型期望它不可为空时,当数据库中存在空值时似乎会发生这种情况。但在这种情况下,相同的数据被拉回并且通常没有例外,所以我猜这里不是这种情况。还有什么可能导致这种情况?
我的下一个猜测是,在导致问题的代码路径中,DbContext 发生了其他事情,例如它缓存了一些对象,导致同一查询出现不同的行为。但是我的_context.ChangeTracker.DebugView.ShortView
在抛出这个异常的时候是空的,说明不是这样的。我的上下文通常有 QueryTrackingBehavior==NoTracking
和 AutoDetectChangesEnabled==false
除非它在更新的中间,但我在没有那个的情况下进行了测试(即默认跟踪行为)并得到了相同的结果。
我使用 SQL 服务器扩展事件来跟踪执行的 SQL。我看到大约有 5 个查询正在启动,我 认为 异常发生在 rpc_completed
为它们全部触发之前。我还没有对此进行足够彻底的分析,以了解它是否总是在接收 SQL 服务器结果的同一点抛出异常。但是鉴于每次查询的结果都是相同的,我不确定进一步分析是否会有很大帮助。
任何可能导致异常的建议?
我正在使用 EFCore 5.0.12(我使用的是 5.0.5 但升级后出现了同样的问题)
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer"
Version="5.0.12" />
更新:
现在我认为有多个线程同时使用同一个 DbContext。它们都来自同一个请求,但有些代码有点像这样:
foreach (var x in y)
{
tasks.Add(MyAsyncMethodThatUsesDbContext(planId, otherParams));
}
var z = await Task.WhenAll(tasks);
它有点复杂,所以不那么明显,但我会看看是不是那样。
问题是多线程使用同一个DbContext引起的,这是一件很不礼貌的事情。我的 DbContext 没有被多个请求共享,但在单个后台 Hangfire 作业处理中,有代码创建了一系列任务,然后对它们调用 WaitAll,如下所示:
foreach (var x in y)
{
tasks.Add(MyAsyncMethodThatUsesDbContext(planId, otherParams));
}
var z = await Task.WhenAll(tasks);
我认为当您从多个线程使用相同的 DbContext 时,EFCore 中有一些东西会发出警告,但是当 运行 来自 Hangfire 作业时(即没有活动 asp 请求)或者当任务全部从同一个线程创建时?
一种解决方案是不并行化数据库访问。或者确保为每个任务使用单独的 DbContext。