Blazor Server(.NET7)Message组件移除功能空引用异常排查
Blazor Server消息组件移除时的NullReferenceException问题解决
问题描述
基于.NET 7和Blazor Server构建消息查看器,将Message组件与消息列表封装为类库复用。所有功能正常,但点击Message组件的关闭按钮时,抛出System.NullReferenceException: Object reference not set to an instance of an object异常。
相关代码
Message组件Razor代码
@namespace TestApp.Components @if(MessageText!=null){ <div class="mt-3 @MessageClass"> @MessageText <button class="btn-close" @onclick="CloseMessage">Close</button> </div> }
Message组件后台代码
using Microsoft.AspNetCore.Components; namespace TestApp.Components; public partial class Message : ComponentBase { public string MessageText {get;set;} = string.Empty; public string MessageClass {get;set;} = string.Empty; public Guid MessageId {get;set;} = new Guid(); [Parameter] public Message Origin {set{ MessageText=value.MessageText; MessageClass=value.MessageClass; MessageId=value.MessageId; }} public Message(){ } public Message(string message){ MessageText=message; } public event EventHandler CloseRequested=default!; private void CloseMessage() { CloseRequested(this,EventArgs.Empty); } }
ApplicationState类代码
using TestApp.Components; public class ApplicationState { public List<Message> Messages {get;set;} = new(); public event EventHandler OnMessagesChange = default!; public void AddMessage(string messageText) { var message = new Message(messageText); Messages.Add(message); EventHandler handler = OnMessagesChange; if(handler!=null) handler.Invoke(this,EventArgs.Empty); message.CloseRequested+=RemoveMessage; } private void RemoveMessage(object sender, EventArgs e) { var message = (Message?)sender; if(message!=null) Messages.Remove(message); } }
问题根源
核心问题是Blazor渲染的Message组件实例与ApplicationState中持有的Message实例并非同一个对象:
- 调用
AddMessage时,你创建了一个Message对象并订阅其CloseRequested事件,随后将该对象加入列表。 - 但在Blazor渲染组件时,通过
[Parameter] public Message Origin赋值的过程中,Blazor会创建新的Message组件实例,而非直接使用你在ApplicationState中创建的那个对象。 - 新的组件实例的
CloseRequested事件并未被订阅,触发时自然会抛出空引用异常。
解决方案
1. 分离数据模型与组件实例
创建独立的消息数据DTO类,用于存储消息内容,组件仅负责渲染和触发移除请求,不直接作为数据载体。
修改后的Message组件
@namespace TestApp.Components @if(!string.IsNullOrEmpty(MessageText)){ <div class="mt-3 @MessageClass"> @MessageText <button class="btn-close" @onclick="CloseMessage">Close</button> </div> }
using Microsoft.AspNetCore.Components; namespace TestApp.Components; public partial class Message : ComponentBase { [Inject] private ApplicationState? AppState { get; set; } [Parameter] public string MessageText { get; set; } = string.Empty; [Parameter] public string MessageClass { get; set; } = string.Empty; [Parameter] public Guid MessageId { get; set; } = Guid.NewGuid(); private void CloseMessage() { AppState?.RemoveMessageById(MessageId); } }
修改后的ApplicationState与新增DTO类
using TestApp.Components; public class ApplicationState { public List<MessageData> Messages { get; set; } = new(); public event EventHandler OnMessagesChange = default!; public void AddMessage(string messageText) { var message = new MessageData(messageText); Messages.Add(message); OnMessagesChange?.Invoke(this, EventArgs.Empty); } public void RemoveMessageById(Guid messageId) { var messageToRemove = Messages.FirstOrDefault(m => m.MessageId == messageId); if (messageToRemove != null) { Messages.Remove(messageToRemove); OnMessagesChange?.Invoke(this, EventArgs.Empty); } } } // 消息数据DTO类 public class MessageData { public string MessageText { get; set; } = string.Empty; public string MessageClass { get; set; } = string.Empty; public Guid MessageId { get; set; } = Guid.NewGuid(); public MessageData(string messageText) { MessageText = messageText; } }
2. 页面渲染调整
在页面中遍历MessageData列表,为每个Message组件传递对应属性:
@foreach(var msg in AppState.Messages) { <Message MessageText="@msg.MessageText" MessageClass="@msg.MessageClass" MessageId="@msg.MessageId" /> }
说明
这种修改方式彻底分离了数据模型与UI组件,避免了实例引用不一致的问题,同时通过唯一ID操作消息,逻辑更可靠,也符合Blazor组件的设计原则。
内容的提问来源于stack exchange,提问作者Z. Campbell
相关产品推荐
相关产品推荐

