EF Core子查询报错:Nullable object must have a value问题求助
EF Core 查询中因ratingDef为null导致子查询报错的解决问题
我在Entity Framework Core中编写了如下方法(为简化仅保留关键属性):
public async Task<TemplateDetailsDto?> SearchDetailsAsync(CancellationToken cancellationToken, Guid templateKey) { var search = await (from template in context.Set<Template>() join ratingDef in context.Set<RangeDefinition>() on template.FinalRatingRangeId equals ratingDef.Id into ratingGroup from ratingDef in ratingGroup.DefaultIfEmpty() where template.Key == templateKey select new TemplateDetailsDto { Key = template.Key, FinalRatingRange = ratingDef == null ? null : (new TemplateRangeDetailsDto { Legend = ratingDef.Legend, RangeTypeId = (QuestionsTypeDto)ratingDef.RangeTypeId, RangesValues = context.Set<RangeValue>() .Where(rangValue => rangValue.RangeDefinitionId == ratingDef.Id) .Select(rangValue => new TemplateRangeDetailsDto { Id = rangValue.Id, Value = rangValue.Value, }) .ToList(), }), }) .AsNoTracking() .SingleOrDefaultAsync(cancellationToken); return search; }
运行时抛出如下异常:
System.InvalidOperationException : Nullable object must have a value
错误发生在以下代码行:
RangesValues = context.Set<RangeValue>() .Where(rangValue => rangValue.RangeDefinitionId == ratingDef.Id) .Select(rangValue => new TemplateRangeDetailsDto { Id = rangValue.Id, Value = rangValue.Value, }) .ToList(),
注释掉该部分代码后程序正常运行。问题在于ratingDef可能为null,导致子查询报错,请问该如何解决?
更新1:
我使用内存数据库进行测试,测试代码如下:
[Fact] public async Task SearchDetailsAsync_WhenInvokeWithNullRating_ShouldGetData() { //Arrange var context = CreateContext(); var templateSearcher = await CreateTemplateSearcherAsync(context); var expectedTemplateId = 1; var templateToGet = await context.Set<Template>().Where(temp => temp.Id == expectedTemplateId).SingleAsync(); //Act var result = await templateSearcher.SearchDetailsAsync(default, templateToGet.Key); //Assert Assert.NotNull(result); ContextTestHelper.Dispose(templateSearcher); }
测试的调用栈信息如下:
Message:
System.InvalidOperationException : Nullable object must have a value.
Stack Trace:
lambda_method2119(Closure, ValueBuffer) WhereSelectEnumerableIterator`2.MoveNext() Enumerable.TryGetSingle[TSource](IEnumerable`1 source, Boolean& found) lambda_method2091(Closure) ResultEnumerable.GetEnumerator() Enumerator.MoveNextHelper() Enumerator.MoveNextAsync() ShapedQueryCompilingExpressionVisitor.SingleOrDefaultAsync[TSource](IAsyncEnumerable`1 asyncEnumerable, CancellationToken cancellationToken) ShapedQueryCompilingExpressionVisitor.SingleOrDefaultAsync[TSource](IAsyncEnumerable`1 asyncEnumerable, CancellationToken cancellationToken) TemplateSearcher.SearchDetailsAsync(CancellationToken cancellationToken, Guid templateKey) line 41 TemplateSearcherTests.SearchDetailsAsync_WhenInvokeWithNullRating_ShouldGetData() line 554 --- End of stack trace from previous location ---
更新2:
生成的SQL语句如下:
DECLARE @__ef_filter__TenantId_1 varchar(50) = 'xxxxxxxx'; DECLARE @__ef_filter__TenantId_2 varchar(50) = 'xxxxxxxx'; DECLARE @__ef_filter__TenantId_0 varchar(50) = 'xxxxxxxx'; DECLARE @__templateKey_0 uniqueIdentifier = 'some-guid-key'; SELECT [f].[Key], CASE WHEN [t].[Id] IS NULL THEN CAST(1 AS bit) ELSE CAST(0 AS bit) END, [t].[Legend], CAST([t].[RangeTypeId] AS tinyint), [f].[Id], [t].[Id], [t0].[Id], [t0].[Value] FROM [template].[Templates] AS [f] LEFT JOIN ( SELECT [r].[Id], [r].[Legend], [r].[RangeTypeId] FROM [template].[RangesDefinitions] AS [r] WHERE [r].[TenantId] = @__ef_filter__TenantId_1 ) AS [t] ON [f].[FinalRatingRangeId] = [t].[Id] LEFT JOIN ( SELECT [r0].[Id], [r0].[Text], [r0].[RangeDefinitionId] FROM [template].[RangesValues] AS [r0] WHERE [r0].[TenantId] = @__ef_filter__TenantId_2 ) AS [t0] ON [t].[Id] = [t0].[RangeDefinitionId] WHERE [f].[TenantId] = @__ef_filter__TenantId_0 AND [f].[Key] = @__templateKey_0 ORDER BY [f].[Id], [t].[Id]
有趣的是,该SQL执行正常,所以可能是模拟数据或内存数据库的问题,正在排查中。感谢Nikola建议查看生成的查询语句!
更新3:
测试发现仅当内存数据库存在子查询的null数据时才会报错,其他内存数据库测试正常,而SQL数据库在所有场景下都正常运行。因此推测是内存数据库的问题,我将评估切换为SQLite In-Memory的可行性。
非常感谢大家的帮助!
内容的提问来源于stack exchange,提问作者Marco Nuñez
相关产品推荐
相关产品推荐

