/// <summary> /// Recursively visits through the properties of the object and based on mode takes appropriate action. /// </summary> /// <param name="obj">Object to visit</param> /// <param name="name">Fully qualified name of the property to take action on. We insist on qualifying the property fully like 'System.Fabric.Chaos.DataStructures.ChaosParameters.MaxConcurrentFaults' so that in the property graph we know exactly which property we are targetting.</param> /// <param name="value">Value of the target property</param> /// <param name="mode">If 'Update' updates the target property with 'value', if 'Verify' checks if the target property has 'value'</param> public static void VisitObject(object obj, string name, string value, ObjectVisitMode mode) { if (string.IsNullOrEmpty(name) || string.IsNullOrEmpty(value)) { return; } Helper(obj, name, value, mode, new HashSet <string>(StringComparer.Ordinal)); }
/// <summary> /// Recursively visits through the properties of the object and based on mode takes appropriate action. /// </summary> /// <param name="obj">Object to visit</param> /// <param name="name">Fully qualified name of the property to take action on. We insist on qualifying the property fully like 'System.Fabric.Chaos.DataStructures.ChaosParameters.MaxConcurrentFaults' so that in the property graph we know exactly which property we are targetting.</param> /// <param name="value">Value of the target property</param> /// <param name="mode">If 'Update' updates the target property with 'value', if 'Verify' checks if the target property has 'value'</param> /// <param name="visited">To avoid cycles, contains the names of the properties that have already been visited and if encountered again avoids digging deeper.</param> private static void Helper(object obj, string name, string value, ObjectVisitMode mode, HashSet <string> visited) { if (obj == null) { return; } Type objType = obj.GetType(); PropertyInfo[] properties = objType.GetProperties(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public); object propValue = null; // Check if any property of obj matches the provided 'name' foreach (PropertyInfo property in properties) { string propertyFullName = objType.FullName + "." + property.Name; if (visited.Contains(propertyFullName)) { continue; } if (MatchSuffix(propertyFullName, name)) { // Handle list type property if (property.PropertyType.IsGenericType && property.PropertyType.GetGenericTypeDefinition() == typeof(List <>)) { Type listItemType = property.PropertyType.GetGenericArguments()[0]; if (listItemType == null) { return; } var resultList = CreateList(listItemType); var inputlist = GetList(listItemType, value, ListElementSeparator); switch (mode) { case ObjectVisitMode.Update: foreach (var item in inputlist) { resultList.Add(item); } property.SetValue(obj, resultList, null); break; case ObjectVisitMode.Verify: IList propertyValue = property.GetValue(obj, null) as IList; if (propertyValue == null || propertyValue.Count != inputlist.Count) { throw new InvalidOperationException("propertyValue should have been a list of the same size as value."); } for (int i = 0; i < inputlist.Count; ++i) { if (!inputlist[i].Equals(propertyValue[i])) { throw new InvalidOperationException("property does not have the expected value."); } } break; } } else if (property.PropertyType.GetInterface(IEnumerableInterfaceName) == null) // not a collection type { object inputValue = Convert.ChangeType(value, property.PropertyType); propValue = property.GetValue(obj, null); switch (mode) { case ObjectVisitMode.Update: property.SetValue(obj, inputValue, null); break; case ObjectVisitMode.Verify: if (!propValue.Equals(inputValue)) { throw new InvalidOperationException("property does not have the expected value."); } break; } } return; } try { visited.Add(propertyFullName); propValue = property.GetValue(obj, null); if (property.PropertyType.Assembly == objType.Assembly) { Helper(propValue, name, value, mode, visited); } } catch (TargetParameterCountException) { if (!property.GetIndexParameters().Any()) { throw; } } } }