Entity Framework Core 6.0.7升级后SqlNullValueException异常原因解析及批量修复方案咨询
先还原下你的问题场景:升级到EF Core 6.0.7后,调用通用查询方法GetAll()时抛出了空值异常,而升级前一切正常。这个方法的代码是:
public virtual IQueryable<T> GetAll() => _dataContext.Set<T>();
抛出的错误信息如下:
Microsoft.EntityFrameworkCore.Query[10100] An exception occurred while iterating over the results of a query for context type 'Data.DbContext'.
System.Data.SqlTypes.SqlNullValueException: Data is Null. This method or property cannot be called on Null values.
对应的GameVariant实体类定义是:
public class GameVariant : IEntityBase, IActiveEntity { public int Id { get; set; } public string Name { get; set; } public string Description { get; set; } public string IconUrl { get; set; } public bool Active { get; set; } = true; }
数据库里Description和IconUrl字段存在NULL值,把这两个属性改成string?后异常就消失了。接下来我来解答你的两个问题:
问题1:为什么将string改为string?可以解决该异常?我原以为string类型默认是可空的;
这是EF Core 6.0引入的严格可空类型映射特性导致的,和.NET本身的string可空性是两码事:
- 在.NET中,
string作为引用类型确实默认可以为null,但EF Core 6.0开始会严格遵循C#的可空注解(?)来推断数据库列的可空性。当你定义属性为string(不带?)时,EF Core会认为这个属性对应的数据库列不允许为NULL,所以当它读取到NULL值时,就会抛出SqlNullValueException——因为它预期这个字段永远有值。 - 改成
string?后,你明确告诉EF Core“这个属性可以为null”,它就会正确处理数据库中的NULL值,不再抛出异常。
而你之前的版本(比如EF Core 5.x及更早)对可空注解的处理没这么严格,即使属性是string,也会默认兼容数据库的NULL值,所以升级前没遇到问题。
问题2:无需逐个修改所有数据库实体类中的string属性,修复该问题的最佳方法是什么?
给你三个不同场景的方案,按需选择:
方案1:快速回退到旧行为(适合紧急修复)
在DbContext的OnConfiguring方法中关闭严格空值处理,让EF Core回到之前版本的兼容模式:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder.UseSqlServer("你的数据库连接字符串", sqlOptions => sqlOptions.UseStrictNullHandling(false)); }
这个配置是全局生效的,不用修改任何实体类,但不推荐长期使用——严格可空校验是EF Core的进步,能帮你提前发现潜在的空引用bug。
方案2:规范修正(推荐长期使用)
先在项目文件(.csproj)中启用可空上下文:
<Nullable>enable</Nullable>
启用后,编译器会自动检测出所有和数据库可空性不匹配的string属性,你可以批量修改这些属性为string?。虽然需要改代码,但这能让你的实体模型和数据库结构完全对齐,从根源上避免类似问题。
方案3:批量配置可空性(不用修改实体类)
如果不想改动实体的属性定义,可以用Fluent API批量配置所有string属性的可空性。先写一个扩展方法:
public static class ModelBuilderExtensions { public static void SetAllStringPropertiesAsNullable(this ModelBuilder modelBuilder) { foreach (var entityType in modelBuilder.Model.GetEntityTypes()) { foreach (var property in entityType.GetProperties()) { // 把所有非可空的string属性设置为可空 if (property.ClrType == typeof(string) && !property.IsNullable) { property.SetIsNullable(true); } } } } }
然后在DbContext的OnModelCreating中调用:
protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.SetAllStringPropertiesAsNullable(); // 其他实体配置... }
这个方案能批量处理所有实体的string属性,不用逐个修改实体类,同时保持代码的整洁性。
内容的提问来源于stack exchange,提问作者Bad Dub

