在实际更新Mongo对象时发现,原有的更新代码无法更新复杂的数据类型对象。恰好看到张占岭老师有对该方法做相关的改进,因此全抄了下来。
总的核心思想就是运用反射与递归,对对象属性一层一层挖掘下去,循环创建父类及之类的更新表达式。
相关代码如下:
#region 递归获取字段更新表达式 private List<UpdateDefinition<T>> GetUpdateDefinitions<T>(T entity) { var type = typeof(T); var fieldList = new List<UpdateDefinition<T>>(); foreach (var property in type.GetProperties(BindingFlags.Instance | BindingFlags.Public)) { GenerateRecursion<T>(fieldList, property, property.GetValue(entity), entity, ""); } return fieldList; } private void GenerateRecursion<TEntity>( List<UpdateDefinition<TEntity>> fieldList, PropertyInfo property, object propertyValue, TEntity item, string father) { //复杂类型 if (property.PropertyType.IsClass && property.PropertyType != typeof(string) && propertyValue != null) { //集合 if (typeof(IList).IsAssignableFrom(propertyValue.GetType())) { foreach (var sub in property.PropertyType.GetProperties(BindingFlags.Instance | BindingFlags.Public)) { if (sub.PropertyType.IsClass && sub.PropertyType != typeof(string)) { var arr = propertyValue as IList; if (arr != null && arr.Count > 0) { for (int index = 0; index < arr.Count; index++) { foreach (var subInner in sub.PropertyType.GetProperties(BindingFlags.Instance | BindingFlags.Public)) { if (string.IsNullOrWhiteSpace(father)) GenerateRecursion(fieldList, subInner, subInner.GetValue(arr[index]), item, property.Name + "." + index); else GenerateRecursion(fieldList, subInner, subInner.GetValue(arr[index]), item, father + "." + property.Name + "." + index); } } } } } } //实体 else { foreach (var sub in property.PropertyType.GetProperties(BindingFlags.Instance | BindingFlags.Public)) { if (string.IsNullOrWhiteSpace(father)) GenerateRecursion(fieldList, sub, sub.GetValue(propertyValue), item, property.Name); else GenerateRecursion(fieldList, sub, sub.GetValue(propertyValue), item, father + "." + property.Name); } } } //简单类型 else { if (property.Name != "_id")//更新集中不能有实体键_id { if (string.IsNullOrWhiteSpace(father)) fieldList.Add(Builders<TEntity>.Update.Set(property.Name, propertyValue)); else fieldList.Add(Builders<TEntity>.Update.Set(father + "." + property.Name, propertyValue)); } } } /// <summary> /// 构建Mongo的更新表达式 /// </summary> /// <param name="entity"></param> /// <returns></returns> private List<UpdateDefinition<T>> GeneratorMongoUpdate<T>(T item) { var fieldList = new List<UpdateDefinition<T>>(); foreach (var property in typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public)) { GenerateRecursion<T>(fieldList, property, property.GetValue(item), item, string.Empty); } return fieldList; } #endregion
在实际应用过程中,有几点要注意一下:
1.在对象创建时,就要将对象中的数组属性初始化,否则在更新时无法插入子项。
public class Users : MongoObj { public Users() { Subs = new List<Sub>(); Spell = new List<int>(); } public string ObjectId_id { get; set; } public string Name { get; set; } public string Sex { set; get; } public List<int> Spell { get; set; } public List<Sub> Subs { get; set; } }
2.如果数组是一个复杂对象数据,那么要给对象添加一个_id,并且在对象初始化时就给_id赋值。
public class Sub { public Sub() { _id = MongoDB.Bson.ObjectId.GenerateNewId().ToString(); } public string _id { get; set; } public string aa {get;set;} public string bb{get;set;} }
3.实际使用的时候发现无法对数组的子项做删除。
比如删除Subs中的第一个子项后,再到mongo里面查询,发现第一个子项仍然存在。
暂时还没有好的解决方法,如果有涉及到数组子项的删除操作,都是将整个对象删掉,然后再重新插入,简单粗暴。