C#访问数据库遇‘Data is Null’错误:为何?有效而if语句无效?
场景还原
尝试访问包含NULL值的数据库数据,示例数据如下:
1 DrugName Paracetamolum Coffeinum NULL NULL
字段对应关系:id→ProductId、ProductName、Substance1、Substance2、Substance3、Substance4。
最初定义的Product模型:
namespace Pharma; public partial class Product { public int ProductId { get; set; } public string ProductName { get; set; } public string Substance1 { get; set; } public string Substance2 { get; set; } public string Substance3 { get; set; } public string Substance4 { get; set; } // 构造函数 public Product() { if (ProductName == null) { ProductName = ""; } if (Substance1 == null) { Substance1 = ""; } if (Substance2 == null) { Substance2 = ""; } if (Substance3 == null) { Substance3 = ""; } if (Substance4 == null) { Substance4 = ""; } } }
运行时抛出Data is Null错误,给Substance1到Substance4添加?修饰符(声明为可空引用类型)后问题解决,但疑惑构造函数的空值处理为何无效,需要了解背后的.NET运行机制。
原因分析
1. 构造函数与ORM赋值的执行顺序
ORM框架(比如Entity Framework)从数据库加载数据映射到模型对象时,执行流程是:
- 先调用模型的构造函数实例化对象,此时所有引用类型属性(如
string)的默认值为null,构造函数会把这些null替换为空字符串。 - 构造函数执行完成后,ORM才会将数据库查询到的实际值(包括NULL)赋值给对应属性。
也就是说,构造函数的空值处理只在对象初始化时生效一次,后续ORM的赋值操作会直接覆盖构造函数设置的空字符串,数据库的NULL值会被再次赋值给属性。
2. 非可空引用类型的约束
在.NET的非可空引用类型(NRT)规则下,string类型默认被标记为非可空,这意味着该类型的变量/属性不允许被赋值为null。当ORM试图把数据库的NULL值赋值给Substance3、Substance4这些非可空的string属性时,会触发框架的强制校验,直接抛出Data is Null错误——这个错误发生在赋值阶段,构造函数的代码根本没机会干预这个过程。
3. string?的作用
给属性添加?修饰符后,string?被声明为可空引用类型,明确告诉.NET框架:这个属性允许被赋值为null。此时ORM可以正常将数据库的NULL值映射到属性上,不会触发非可空约束的校验错误。
总结
构造函数的空值处理逻辑仅在对象初始化时执行,无法覆盖后续ORM的赋值操作;而.NET的非可空引用类型约束会直接阻止NULL值赋值给非可空属性,这才是错误的根源。声明属性为可空引用类型,是从类型层面允许NULL值,才能真正解决问题。
内容的提问来源于stack exchange,提问作者user23278485

