我在实体框架中有以下方法(为了简单起见,它的属性较少,但重要的是):
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:可为空的对象必须具有值
在这行代码中:
RangesValues = context.Set<RangeValue>()
.Where(rangValue => rangValue.RangeDefinitionId == ratingDef.Id)
.Select(rangValue => new TemplateRangeDetailsDto
{
Id = rangValue.Id,
Value = rangValue.Value,
})
.ToList(),
如果我评论这一部分,一切都像一个魅力。问题是ratingDef可以为null,而子查询会给我错误,因为ratingDef为null,所以,你认为我如何解决这个问题?
提前感谢!
**更新:**我正在使用这个进行测试(我正在使用内存数据库进行测试):
[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);
}
和测试结果的调用堆栈:
消息: System.InvalidOperationException:可为空的对象必须具有值。
堆栈跟踪:
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 ---
**更新二:**这是生成的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建议审核生成的查询!:D个
**更新3:**我正在做测试,只有在内存数据库中有来自子查询的一些空数据时才会发生错误。其余的SQL内存测试工作正常,现在SQL数据库在任何情况下都工作正常,所以正如他们提到的,这可能是内存中DB的问题,我将看看切换到SqlLite内存中的可行性如何。
非常感谢你们的帮助。
2条答案
按热度按时间lskq00tm1#
这部分代码可能是在内存中执行的,而不是翻译成sql。您可以在日志中或通过调用.ToQueryString()扩展方法检查生成的sql查询并进行验证。
我已经成功地解决了这种情况与LinqKit库。
顺便说一下,这里不需要NoTracking,因为结果是投影而不是跟踪实体。
jfewjypa2#
正如我在更新3中提到的,这似乎是内存数据库的问题。
谢谢大家的帮助。