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

基于索引提取JSON属性并优化MongoDB存储访问性能问题

高效解决动态属性映射与序列化兼容问题

针对你遇到的问题——既要高效从Product关联动态Properties,又要兼容MongoDB和WCF序列化,同时处理大体积JSON的内存友好性——这里有几个优化方案,完全避开ExpandoObject的兼容性坑:


方案1:预缓存Properties为列表,结合延迟迭代器(推荐)

核心思路是只转换一次OrderedDictionary为可快速访问的列表,之后通过索引直接获取属性,同时用迭代器yield return实现内存友好的延迟加载,完美兼容MongoDB和WCF。

步骤1:修改Entity类,添加缓存列表

在反序列化完成后,把OrderedDictionary转换成List<Dictionary<string, object>>缓存起来,避免每次访问都做Cast和ElementAt(后者是O(n)操作,大数据下极慢):

using System.Collections.Specialized;
using System.Runtime.Serialization;
using Newtonsoft.Json;

public class Entity 
{
    // 保留原始的OrderedDictionary用于反序列化
    [JsonProperty("Properties")]
    public OrderedDictionary RawProperties { get; set; }
    
    // 缓存转换后的Properties列表,反序列化后自动初始化
    [JsonIgnore]
    public List<Dictionary<string, object>> PropertiesCache { get; private set; }
    
    public IEnumerable<Product> Products { get; set; }
    
    // 反序列化完成后触发,初始化缓存
    [OnDeserialized]
    private void OnDeserialized(StreamingContext context)
    {
        PropertiesCache = RawProperties?
            .Cast<DictionaryEntry>()
            .Select(entry => new Dictionary<string, object> 
            { 
                { entry.Key.ToString(), entry.Value } 
            })
            .ToList() ?? new List<Dictionary<string, object>>();
    }
}

步骤2:修改Product类,实现高效的PropertiesData

用迭代器返回关联的属性数据,既不会一次性加载所有数据到内存,又能直接被MongoDB和WCF序列化:

using System.Collections.Generic;
using Newtonsoft.Json;

public class Product 
{
    [JsonIgnore]
    public Entity Entity { get; set; }
    
    public int Id { get; set; }
    
    public int[] PropertiesIndexes { get; set; }
    
    // 这个属性会被Json.NET正确序列化,MongoDB和WCF也能识别
    [JsonProperty("PropertiesData")]
    public IEnumerable<Dictionary<string, object>> PropertiesData 
    {
        get 
        {
            if (Entity?.PropertiesCache == null || PropertiesIndexes == null)
                yield break;
                
            foreach (var index in PropertiesIndexes)
            {
                // 边界检查避免索引越界
                if (index >= 0 && index < Entity.PropertiesCache.Count)
                    yield return Entity.PropertiesCache[index];
            }
        }
    }
}

优势

  • 性能提升:缓存列表后,索引访问是O(1)操作,彻底解决原实现中ElementAt的性能瓶颈
  • 内存友好:迭代器yield return只会在序列化/遍历的时候逐个加载属性,不会一次性占用大量内存
  • 兼容性强:Dictionary<string, object>是MongoDB原生支持的类型,WCF添加[DataContract]/[DataMember]标记后也能正常序列化:
    [DataContract]
    public class Product 
    {
        [DataMember] public int Id { get; set; }
        [DataMember] public IEnumerable<Dictionary<string, object>> PropertiesData { get; set; }
        // 其他属性...
    }
    

方案2:自定义JsonConverter,序列化时动态生成PropertiesData

如果不想在Entity中缓存数据(比如极端大文件场景),可以用Json.NET的自定义转换器,在序列化Product时直接从原始OrderedDictionary中提取数据:

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Collections.Specialized;

public class ProductConverter : JsonConverter<Product>
{
    public override void WriteJson(JsonWriter writer, Product value, JsonSerializer serializer)
    {
        writer.WriteStartObject();
        
        // 序列化Id
        writer.WritePropertyName("Id");
        serializer.Serialize(writer, value.Id);
        
        // 序列化PropertiesData
        writer.WritePropertyName("PropertiesData");
        writer.WriteStartArray();
        
        if (value.Entity?.RawProperties != null && value.PropertiesIndexes != null)
        {
            foreach (var index in value.PropertiesIndexes)
            {
                if (index >= 0 && index < value.Entity.RawProperties.Count)
                {
                    var entry = value.Entity.RawProperties.Cast<DictionaryEntry>().ElementAt(index);
                    var propDict = new Dictionary<string, object> 
                    { 
                        { entry.Key.ToString(), entry.Value } 
                    };
                    serializer.Serialize(writer, propDict);
                }
            }
        }
        
        writer.WriteEndArray();
        writer.WriteEndObject();
    }

    public override Product ReadJson(JsonReader reader, Type objectType, Product existingValue, bool hasExistingValue, JsonSerializer serializer)
    {
        // 反序列化逻辑:如果是从目标JSON(带PropertiesData)反序列化,这里可以反向生成PropertiesIndexes
        // 若还是从原始JSON反序列化,直接按原逻辑处理即可
        var jObj = JObject.Load(reader);
        var product = new Product
        {
            Id = jObj["Id"].Value<int>()
            // 按需添加PropertiesIndexes的生成逻辑
        };
        return product;
    }
}

然后给Product类标记转换器:

[JsonConverter(typeof(ProductConverter))]
public class Product 
{
    // 原属性...
}

优势

  • 无需缓存,节省内存空间
  • 序列化时动态生成数据,适合超大规模JSON文件处理

为什么这两个方案比原实现更好?

  1. 性能:原实现每次访问PropertiesData都要重复Cast和ElementAt,而缓存列表后是O(1)访问,性能提升显著
  2. 内存:迭代器避免一次性加载所有属性数据,适合数百MB的大文件
  3. 兼容性:完全避开ExpandoObject的序列化问题,Dictionary<string, object>是通用兼容类型

内容的提问来源于stack exchange,提问作者thomasb

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 07:15:04