Blazor Razor组件绑定嵌套模型时触发NullReferenceException异常
问题根因
你在组件中仅对最外层的Purchase类做了实例化,所有作为类属性存在的嵌套引用类型都没有被初始化,默认值为null。
绑定表达式purchase.payment.cardholder.firstName的渲染解析链路需要依次访问4层对象:purchase → payment → cardholder → firstName。当Blazor渲染时访问到purchase.payment节点,因为这个属性没有被赋值、值为null,就会直接触发空引用异常。很多人排查的时候会下意识去找cardholder是不是null,实际上还没走到那一层,外层的payment属性就已经是null了,这也是很多人卡很久找不到问题的原因。
另外Blazor的渲染逻辑是通过动态生成的表达式树处理绑定的,这类异常的堆栈会指向匿名的渲染方法(就是你看到的b__0_1(RenderTreeBuilder __builder2)),不会直接标注到具体的代码行,排查难度比普通的空引用高很多。
修复方案
按可靠性从高到低选择适配的处理方式:
在领域模型定义阶段完成全链路嵌套初始化
对所有作为其他类属性的引用类型,直接在属性定义处加默认初始化器,从根源避免漏初始化的问题。改造示例:public class Purchase { public Payment payment { get; set; } = new Payment(); } public class Payment { // 嵌套引用属性全部初始化 public Urls urls { get; set; } = new Urls(); public Payeeinfo payeeInfo { get; set; } = new Payeeinfo(); public Cardholder cardholder { get; set; } = new Cardholder(); public Riskindicator riskIndicator { get; set; } = new Riskindicator(); public Price[] prices { get; set; } = Array.Empty<Price>(); // 字符串类型属性不需要单独初始化,只要上层链路不为null就不会触发空引用 public string operation { get; set; } public string intent { get; set; } public string currency { get; set; } public string description { get; set; } public string userAgent { get; set; } public string language { get; set; } } public class Cardholder { public Shippingaddress shippingAddress { get; set; } = new Shippingaddress(); // 其余字符串属性保留原有定义即可 public string firstName { get; set; } public string lastName { get; set; } public string email { get; set; } // ... 其他属性 } public class Riskindicator { public Pickupaddress pickUpAddress { get; set; } = new Pickupaddress(); // 其余属性保留原有定义即可 }这种方式的好处是不管在哪个位置实例化这个领域模型,整条属性访问链路都是通畅的,不会因为后续新增绑定字段就漏初始化再次抛异常。
如果不方便修改领域模型定义,就在组件初始化时手动补全全链路对象
把组件中的purchase字段初始化代码改成完整的嵌套赋值:private Purchase purchase = new() { payment = new() { cardholder = new() // 后续如果要绑定payment下的其他嵌套属性,比如urls.completeUrl, // 这里还要继续补urls = new() ,很容易漏 } };这种方式只适合临时测试,长期维护很容易因为漏补嵌套对象再次触发空引用。
避坑提醒:不要把没有实例化的
null对象直接传给EditForm的Model参数,比如写private Purchase purchase;这类代码,异常会出现在更早的渲染阶段,排查成本更高。
内容的提问来源于stack exchange,提问作者Pettanord

