在实际更新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);elseGenerateRecursion(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);elseGenerateRecursion(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));elsefieldList.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里面查询,发现第一个子项仍然存在。
暂时还没有好的解决方法,如果有涉及到数组子项的删除操作,都是将整个对象删掉,然后再重新插入,简单粗暴。