我在 net core 3.1 上的旧代码在移动到 .NET 7 时抛出异常并显示消息“可为空的对象必须有一个值”

问题描述 投票:0回答:3

我的代码在下面,在 .NET Core 3.1 中运行良好:

// dto is search criteria passing from outside
var query = from p in dbContext.Products.Where(p => dto.Ids.Contains(p.Id))
            from c in dbContext.Categorys.Where(c => c.Id == p.CategoryId).DefaultIfEmpty()
            from wh in dbContext.Warehouses.Where(wh => wh.Id == p.WarehouseId).DefaultIfEmpty()
            select new 
            {
                Product = p,
                Category = c,
                Warehouse = wh
            };
`var products = await query.ToListAsync();` // this line will throw exception

结果将包含 Category 或 Warehouse 的一些 null,在 .NET Core 3.1 中,默认值将分配给对象。 但是在 .NET 7 中运行时,它会在运行时抛出异常

System.InvalidOperationException with message "Nullable object must have a value."
2023-03-29T10:34:25.7553873Z              at lambda_method3977(Closure, QueryContext, ValueBuffer)
2023-03-29T10:34:25.7555036Z              at Microsoft.EntityFrameworkCore.InMemory.Query.Internal.InMemoryShapedQueryCompilingExpressionVisitor.QueryingEnumerable`1.Enumerator.MoveNextHelper()
2023-03-29T10:34:25.7555635Z              at Microsoft.EntityFrameworkCore.InMemory.Query.Internal.InMemoryShapedQueryCompilingExpressionVisitor.QueryingEnumerable`1.Enumerator.MoveNextAsync()
2023-03-29T10:34:25.7559660Z              at System.Runtime.CompilerServices.ConfiguredCancelableAsyncEnumerable`1.Enumerator.MoveNextAsync()
2023-03-29T10:34:25.7560134Z              at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ToListAsync[TSource](IQueryable`1 source, CancellationToken cancellationToken)
2023-03-29T10:34:25.7560408Z              at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ToListAsync[TSource](IQueryable`1 source, CancellationToken cancellationToken)


整个项目有很多这样的地方,手动检查null几乎是不可能完成的任务,请谁能帮我解决这个问题,就像如何重新打开“设置为null时的默认值”一样.NET 3.1 以前做过。谢谢。

c# nullable .net-7.0
3个回答
2
投票

如果您正在移植遗留代码,请尝试在项目中设置

<Nullable>False</Nullable>

请参阅可为空的引用类型(C# 参考)教程:使用可为空和不可为空的引用类型更清楚地表达您的设计意图


2
投票

您需要显式定义空值。 试试这个:

var query = from p in dbContext.Products .Where(p => dto.Ids.Contains(p.Id)) 
            from c in dbContext.Categorys.Where(c => c.Id == p.CategoryId).DefaultIfEmpty() 
            from wh in dbContext.Warehouses.Where(wh => wh.Id == p.WarehouseId).DefaultIfEmpty() 
            select new { 
                Product = p, 
                Category = c == null ? null : c, 
                Warehouse = wh == null ? null : wh 
                }; 

var products = await query.ToListAsync();

更新:我忽略了你问题的最后一部分。 如果要禁用文件中的可为空功能,可以将

#nullable disable
放在文件顶部。要在整个项目范围内应用它,请在您的 .csproject 中设置此选项
<Nullable>enable</Nullable>
.


0
投票

在.NET Core 3.1中,当你使用DefaultIfEmpty()方法连接两个表时,如果结果为null,则会为该对象分配一个默认值。但是,在 .NET 7 中,这种行为发生了变化,您需要显式检查空值。

解决这个问题的一种方法是使用空条件运算符 ?。访问连接操作返回的对象的属性。此运算符允许您将多个 null 检查链接在一起,因此您可以安全地访问嵌套属性而不必担心 null 值。

这是一个示例,说明如何修改 LINQ 查询以使用 null 条件运算符:

var query = from p in dbContext.Products.Where(p => dto.Ids.Contains(p.Id))
            from c in dbContext.Categorys.Where(c => c.Id == p.CategoryId).DefaultIfEmpty()
            from wh in dbContext.Warehouses.Where(wh => wh.Id == p.WarehouseId).DefaultIfEmpty()
            select new 
            {
                Product = p,
                CategoryName = c?.Name,
                WarehouseName = wh?.Name
            };

在这个例子中,我们使用空条件运算符 ?。访问类别和仓库对象的名称属性。如果对象为 null,则运算符返回 null 而不是抛出异常。

通过以这种方式使用空条件运算符,您可以安全地访问连接对象的属性,而不必担心空值。

© www.soinside.com 2019 - 2024. All rights reserved.