如何让JsonConvert.DeserializeObject执行方法而非赋值属性,避免触发事件?
问题分析
你遇到的核心问题是:Json.NET反序列化嵌套BindingList(如autor__entity的telephones)时,会逐个向已绑定ListChanged事件的集合中添加元素,触发不必要的事件逻辑。虽然你对_books使用了AddRange来规避事件,但反序列化内部嵌套集合时无法控制这个过程。
解决方案
方案1:自定义BindingList的Json转换器
创建自定义转换器,在反序列化BindingList<T>时先禁用事件、批量添加元素,完成后恢复事件状态,从根源上避免逐个添加触发事件。
public class BindingListConverter<T> : JsonConverter<BindingList<T>> { public override BindingList<T> ReadJson(JsonReader reader, Type objectType, BindingList<T> existingValue, bool hasExistingValue, JsonSerializer serializer) { // 先把JSON反序列化为普通列表 var items = serializer.Deserialize<List<T>>(reader); if (items == null) return new BindingList<T>(); BindingList<T> bindingList; if (hasExistingValue && existingValue != null) { bindingList = existingValue; // 禁用事件后批量添加元素 var oldRaiseEvents = bindingList.RaiseListChangedEvents; try { bindingList.RaiseListChangedEvents = false; foreach (var item in items) { bindingList.Add(item); } } finally { bindingList.RaiseListChangedEvents = oldRaiseEvents; if (oldRaiseEvents) { bindingList.ResetBindings(); } } } else { // 新建BindingList时直接传入所有元素,此时还未绑定事件 bindingList = new BindingList<T>(items); } return bindingList; } public override void WriteJson(JsonWriter writer, BindingList<T> value, JsonSerializer serializer) { // 序列化时按普通列表处理 serializer.Serialize(writer, value.ToList()); } }
然后在实体类的BindingList字段上标记使用该转换器:
[JsonObject(MemberSerialization.OptIn)] public class autor__entity { [JsonProperty("id")] internal int _id; [JsonProperty("name")] internal string _name; [JsonProperty("telephones"), JsonConverter(typeof(BindingListConverter<telephone__entity>))] internal BindingList<telephone__entity> _telephones; // 其余代码保持不变 } // 给其他嵌套的BindingList字段也添加转换器标记 [JsonObject(MemberSerialization.OptIn)] public class book__entity { [JsonProperty("title")] internal string _title; [JsonProperty("autors"), JsonConverter(typeof(BindingListConverter<autor__entity>))] internal BindingList<autor__entity> _autors; // 其余代码保持不变 } [JsonObject(MemberSerialization.OptIn)] public class library__entity { [JsonProperty("id")] internal int _id; [JsonProperty("books"), JsonConverter(typeof(BindingListConverter<book__entity>))] internal BindingList<book__entity> _books; // 其余代码保持不变 }
方案2:调整实体类构造逻辑,延迟绑定事件
修改实体类,让ListChanged事件在反序列化完成后再绑定,避免反序列化过程中触发事件。
步骤1:添加私有构造函数供Json.NET使用
[JsonObject(MemberSerialization.OptIn)] [JsonConstructor] // 指定Json.NET使用该构造函数 public class autor__entity { // 反序列化时使用,默认不绑定事件 private autor__entity(bool bindEvents = false) { _telephones = new BindingList<telephone__entity>(); if (bindEvents) { _telephones.ListChanged += telephones_ListChanged; } } // 对外公开的构造函数,默认绑定事件 public autor__entity() : this(true) { } // 其余代码保持不变 }
步骤2:反序列化完成后手动绑定所有嵌套事件
在数据层反序列化完成后,递归遍历所有嵌套集合,手动绑定事件:
using (var reader = cmd.ExecuteReader(CommandBehavior.CloseConnection)) { while (reader.Read()) { var record = new library__entity(); record._id = Convert.ToInt32(reader["ID"]); // 反序列化books,此时内部autors、telephones都未绑定事件 var books = JsonConvert.DeserializeObject<List<book__entity>>(Convert.ToString(reader["BOOKS"])); record._books.AddRange(books); // 递归绑定所有嵌套集合的事件 BindNestedListEvents(record); return record; } } // 递归绑定所有ListChanged事件 private void BindNestedListEvents(library__entity library) { foreach (var book in library.books) { book._autors.ListChanged += book.autors_ListChanged; foreach (var autor in book.autors) { autor._telephones.ListChanged += autor.telephones_ListChanged; } } }
方案3:修改属性setter,直接替换集合
让Json.NET直接赋值内部BindingList字段,绕过逐个添加元素的过程,通过属性setter统一绑定事件:
[JsonObject(MemberSerialization.OptIn)] public class autor__entity { [JsonProperty("id")] internal int _id; [JsonProperty("name")] internal string _name; [JsonProperty("telephones")] internal BindingList<telephone__entity> _telephones; public int id { get; set; } public string name { get; set; } public BindingList<telephone__entity> telephones { get => _telephones; set { // 先解绑旧集合的事件 if (_telephones != null) { _telephones.ListChanged -= telephones_ListChanged; } // 直接替换为反序列化后的新集合 _telephones = value; // 给新集合绑定事件 if (_telephones != null) { _telephones.ListChanged += telephones_ListChanged; } } } public autor__entity() { _telephones = new BindingList<telephone__entity>(); _telephones.ListChanged += telephones_ListChanged; } // 其余代码保持不变 }
这种方式下,Json.NET会直接把反序列化后的完整BindingList赋值给字段,不会触发逐个添加的事件,仅在赋值完成后绑定事件。
总结
优先推荐方案1,它无需修改实体类的核心构造逻辑,通过转换器全局控制BindingList的反序列化行为,是最通用、侵入性最低的解决方案。
内容的提问来源于stack exchange,提问作者pedroPG
相关产品推荐
相关产品推荐

