public ValueBindingResult BindObject(Type objType, string name, ValueProvider valueProvider, ModelBindingStateBuilder currentBindingStateBuilder) { var resolvedType = Nullable.GetUnderlyingType(objType) ?? objType; var suffix = name; string value = null; while (!string.IsNullOrEmpty(suffix) && value == null) { value = valueProvider.GetValue(suffix); suffix = string.Concat(suffix.SkipWhile(c => c != '.').Skip(1)); } try { if (value == null) { throw new Exception(); } if (resolvedType == typeof(object)) { var result = JsonConvert.DeserializeObject(value, resolvedType); return(new ValueBindingResult(true, result, resolvedType)); } else { var result = Convert.ChangeType(value, resolvedType); return(new ValueBindingResult(true, result, resolvedType)); } } catch { try { if (value == null) { throw new Exception(); } if (resolvedType == typeof(object)) { var result = Convert.ChangeType(value, resolvedType); return(new ValueBindingResult(true, result, resolvedType)); } else { var result = JsonConvert.DeserializeObject(value, resolvedType); return(new ValueBindingResult(true, result, resolvedType)); } } catch { var ctor = resolvedType.GetConstructor(Type.EmptyTypes); if (ctor == null) { return(new ValueBindingResult(false, null, resolvedType)); } var result = Activator.CreateInstance(resolvedType); var innerModelStateBuilder = new ModelBindingStateBuilder(); foreach (var f in resolvedType.GetFields().Where(f => f.IsPublic)) { var obj = BindObject(f.FieldType, $"{name}.{f.Name}".ToLower(), valueProvider, innerModelStateBuilder); if (obj.IsSuccessful) { innerModelStateBuilder.SetSucceeded(f.Name); f.SetValue(result, obj.Result); } else { if (f.GetCustomAttribute <RequiredAttribute>() != null) { return(new ValueBindingResult(false, null, resolvedType)); } innerModelStateBuilder.SetFailed(f.Name); } } foreach (var p in resolvedType.GetProperties().Where(info => info.GetSetMethod() != null)) { var obj = BindObject(p.PropertyType, $"{name}.{p.Name}".ToLower(), valueProvider, innerModelStateBuilder); if (obj.IsSuccessful) { innerModelStateBuilder.SetSucceeded(p.Name); p.SetValue(result, obj.Result); } else { if (p.GetCustomAttribute <RequiredAttribute>() != null) { return(new ValueBindingResult(false, null, resolvedType)); } innerModelStateBuilder.SetFailed(p.Name); } } var validateMethod = resolvedType.GetMethods().FirstOrDefault(method => method.GetCustomAttribute <ValidateMethodAttribute>() != null && method.GetParameters().SingleOrDefault() != null && method.GetParameters().SingleOrDefault().ParameterType == typeof(ModelBindingStateBuilder)); if (validateMethod != null) { validateMethod.Invoke(result, new object?[] { innerModelStateBuilder }); } var innerModelState = innerModelStateBuilder.Build(); if (validateMethod != null) { if (validateMethod.GetCustomAttribute <RequiredAttribute>() != null && innerModelState.LocalFailsCount > 0) { return(new ValueBindingResult(false, null, resolvedType)); } } string lastSuffix = name.Substring(name.LastIndexOf('.') + 1); currentBindingStateBuilder.AddState(lastSuffix, innerModelState); return(new ValueBindingResult(true, result, resolvedType)); } } }
public object[] ActivateParameters(HttpContext context, MethodInfo actionMethod, ModelBindingStateBuilder stateBuilder) { object[] parameters = new object[actionMethod.GetParameters().Length]; var paramInfos = actionMethod.GetParameters(); var builder = new ValueProviderBuilder(context); builder.Add <RouteValueProviderSource>(); builder.Add <QueryValueProviderSource>(); builder.Add <FormValueProviderSource>(); builder.Add <FormDataJsonValueProviderSource>(); var valueProvider = builder.Build(); foreach (var parameter in paramInfos) { var result = BindObject(parameter.ParameterType, parameter.Name.ToLower(), valueProvider, stateBuilder); if (result.IsSuccessful) { parameters[parameter.Position] = result.Result; stateBuilder.SetSucceeded(parameter.Name); } else { parameters[parameter.Position] = parameter.DefaultValue?.GetType() == typeof(DBNull) ? null : parameter.DefaultValue; stateBuilder.SetFailed(parameter.Name); } } return(parameters); }