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==NoTrackingAutoDetectChangesEnabled==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。