C#中使用UpdateDefinitionBuilder无法保存列表至MongoDB的问题
解决MongoDB反射更新List字段时序列化为"(Collection)"字符串的问题
嘿,我之前也踩过MongoDB驱动反射更新集合字段的这个坑,结合你提到的CSHARP-1984的bug,给你两个靠谱的解决思路,都不用硬编码具体的List类型:
1. 针对集合类型做特殊处理,转换为MongoDB可识别的BsonArray
问题根源在于MongoDB驱动对通过反射获取的非具体集合类型(比如IEnumerable
public override MyObj Update(MyObj model) { var builder = Builders<MyObj>.Update; var builder_def = builder.Set(x => x.Id, model.Id); foreach (PropertyInfo prop in model.GetType().GetProperties()) { var value = prop.GetValue(model); // 简化反射代码,无需重复调用GetType() if (value == null) continue; // 判断是否为集合类型(排除string) var propType = prop.PropertyType; if (typeof(IEnumerable).IsAssignableFrom(propType) && propType != typeof(string)) { // 将集合转换为BsonArray,MongoDB驱动可直接识别并序列化为数组 var bsonArray = BsonArray.Create(value); builder_def = builder_def.Set(prop.Name, bsonArray); } else { builder_def = builder_def.Set(prop.Name, value); } } var filter_def = Builders<MyObj>.Filter.Eq(x => x.Id, model.Id); Connection.Update(filter_def, builder_def); return model; }
这个方法能自动处理任意IEnumerable类型的集合,不管是List<string>还是List<int>,都能正确保存为MongoDB的数组类型。
2. 用表达式树构建强类型Update操作,绕过弱类型Set的bug
CSHARP-1984主要影响的是弱类型的Set(string name, object value)方法,如果你改用强类型的泛型Set<TField>方法,就能从根源上避免这个问题。我们可以通过反射和表达式树动态生成字段的访问表达式:
public override MyObj Update(MyObj model) { var builder = Builders<MyObj>.Update; var builder_def = builder.Set(x => x.Id, model.Id); var modelType = typeof(MyObj); foreach (PropertyInfo prop in modelType.GetProperties()) { var value = prop.GetValue(model); if (value == null) continue; // 构建字段访问表达式:x => x.PropertyName var parameter = Expression.Parameter(modelType, "x"); var propertyExpr = Expression.Property(parameter, prop); var lambda = Expression.Lambda(propertyExpr, parameter); // 调用泛型的Set方法 var setMethod = typeof(UpdateDefinitionBuilder<MyObj>) .GetMethod("Set", new[] { lambda.GetType(), prop.PropertyType }); var setDefinition = (UpdateDefinition<MyObj>)setMethod.Invoke(builder, new[] { lambda, value }); builder_def = builder_def.Combine(setDefinition); } var filter_def = Builders<MyObj>.Filter.Eq(x => x.Id, model.Id); Connection.Update(filter_def, builder_def); return model; }
这种强类型的方式更符合MongoDB驱动的最佳实践,不仅能解决集合序列化的问题,还能避免其他潜在的弱类型序列化异常,同时完全不需要硬编码任何集合类型。
这两个方案都能有效解决你遇到的问题,你可以根据自己的代码风格和需求选择其中一种。
内容的提问来源于stack exchange,提问作者JohnB
相关产品推荐
相关产品推荐

