You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 06:15:20