public void Validation_Method_Invalid_Bad_Params_Throws() { ValTestClass vtc = new ValTestClass(); ValidationContext context = ValidationUtilities.CreateValidationContext(vtc, null); object[] parameters = { "LongerThan5Chars", 2.0 }; // legal params ExceptionHelper.ExpectValidationException(delegate() { ValidationUtilities.ValidateMethodCall("MethodWithParameters", context, parameters); }, "The field FirstParameterDisplayName must be a string with a maximum length of 5.", typeof(StringLengthAttribute), "LongerThan5Chars"); }
public void ComplexType_CustomValidator_MultipleMemberNames() { // ensure TDPs are registered DomainServiceDescription.GetDescription(typeof(ComplexTypes_TestService)); ComplexType_Parent entity = new ComplexType_Parent { ID = 1, ContactInfo = new ContactInfo { Name = "Mathew", HomeAddress = new Address { AddressLine1 = "47 South Wynn Rd.", City = "Oregon", State = "OH" }, PrimaryPhone = new Phone { AreaCode = "419", Number = "693-6096" } }, }; // configure multi member validation errors DynamicTestValidator.ForcedValidationResults.Clear(); ValidationResult contactResult = new ValidationResult("ContactInfo", new string[] { "Name", "PrimaryPhone" }); ValidationResult phoneResult = new ValidationResult("Phone", new string[] { "AreaCode", "Number" }); DynamicTestValidator.ForcedValidationResults[entity.ContactInfo] = contactResult; DynamicTestValidator.ForcedValidationResults[typeof(Phone)] = phoneResult; ValidationContext validationContext = ValidationUtilities.CreateValidationContext(entity, null); List <ValidationResult> results = new List <ValidationResult>(); bool isValid = ValidationUtilities.TryValidateObject(entity, validationContext, results); Assert.IsFalse(isValid); // Verify that the member names have been transformed into full paths ValidationResult result = results.Single(q => q.ErrorMessage == "ContactInfo-ContactInfo"); string[] memberNames = result.MemberNames.ToArray(); Assert.AreEqual(2, memberNames.Length); Assert.IsTrue(memberNames.Contains("ContactInfo.Name")); Assert.IsTrue(memberNames.Contains("ContactInfo.PrimaryPhone")); // here we expect member names to be transformed into full paths result = results.Single(q => q.ErrorMessage == "Phone-TypeLevel"); memberNames = result.MemberNames.ToArray(); Assert.AreEqual(2, memberNames.Length); Assert.IsTrue(memberNames.Contains("ContactInfo.PrimaryPhone.AreaCode")); Assert.IsTrue(memberNames.Contains("ContactInfo.PrimaryPhone.Number")); }
public void Validation_Method_Invalid_Good_Params_Throws() { ValTestClass vtc = new ValTestClass(); ValidationContext context = ValidationUtilities.CreateValidationContext(vtc, null); object[] parameters = { "hello", 2.0 }; // legal params vtc._failMethod = true; // tells validation method below to fail the IsValid ExceptionHelper.ExpectValidationException(delegate() { ValidationUtilities.ValidateMethodCall("MethodWithParameters", context, parameters); }, "-MethodDisplayName", typeof(CustomValidationAttribute), vtc); }
public void Validation_Method_Valid_No_Parameters() { ValTestClass vtc = new ValTestClass(); ValidationContext context = ValidationUtilities.CreateValidationContext(vtc, null); object[] parameters = null; bool isValid = ValidationUtilities.TryValidateCustomUpdateMethodCall("MethodWithNoParameters", context, parameters, null); Assert.IsTrue(isValid); List <ValidationResult> output = new List <ValidationResult>(); isValid = ValidationUtilities.TryValidateCustomUpdateMethodCall("MethodWithNoParameters", context, parameters, output); Assert.IsTrue(isValid); Assert.AreEqual(0, output.Count); }
public void Validation_Method_No_Attributes() { ValTestNoAttributesClass instance = new ValTestNoAttributesClass(); ValidationContext context = ValidationUtilities.CreateValidationContext(instance, null); object[] parameters = { "hello", 2.0 }; bool isValid = ValidationUtilities.TryValidateCustomUpdateMethodCall("MethodWithoutAttributes", context, parameters, null); Assert.IsTrue(isValid); List <ValidationResult> output = new List <ValidationResult>(); isValid = ValidationUtilities.TryValidateCustomUpdateMethodCall("MethodWithoutAttributes", context, parameters, output); Assert.IsTrue(isValid); Assert.AreEqual(0, output.Count); }
public void Validation_Method_Invalid_No_Params() { ValTestClass vtc = new ValTestClass(); vtc._failMethod = true; // forces failure ValidationContext context = ValidationUtilities.CreateValidationContext(vtc, null); ExceptionHelper.ExpectException <MissingMethodException>(delegate() { ValidationUtilities.TryValidateCustomUpdateMethodCall("MethodWithParameters", context, null, null); }, "Method 'OpenRiaServices.DomainServices.Hosting.Test.ValidationUtilitiesTests+ValTestClass.MethodWithParameters' accepting zero parameters could not be found."); ExceptionHelper.ExpectException <MissingMethodException>(delegate() { ValidationUtilities.TryValidateCustomUpdateMethodCall("MethodWithParameters", context, null, new List <ValidationResult>()); }, "Method 'OpenRiaServices.DomainServices.Hosting.Test.ValidationUtilitiesTests+ValTestClass.MethodWithParameters' accepting zero parameters could not be found."); }
public void Validation_Method_Invalid_Null_Params() { ValTestClass vtc = new ValTestClass(); ValidationContext context = ValidationUtilities.CreateValidationContext(vtc, null); object[] parameters = null; // actually requires 2 params // IsValid entry point ExceptionHelper.ExpectException <MissingMethodException>(delegate() { ValidationUtilities.TryValidateCustomUpdateMethodCall("MethodWithParameters", context, parameters, null); }, "Method 'OpenRiaServices.DomainServices.Hosting.Test.ValidationUtilitiesTests+ValTestClass.MethodWithParameters' accepting zero parameters could not be found."); // Validate entry point ExceptionHelper.ExpectException <MissingMethodException>(delegate() { ValidationUtilities.ValidateMethodCall("MethodWithParameters", context, parameters); }, "Method 'OpenRiaServices.DomainServices.Hosting.Test.ValidationUtilitiesTests+ValTestClass.MethodWithParameters' accepting zero parameters could not be found."); }
public void Validation_Fail_Method_Null_ValueType() { ValTestClass vtc = new ValTestClass(); ValidationContext context = ValidationUtilities.CreateValidationContext(vtc, null); object[] parameters = { "xxx", null }; // 2nd param should be double // IsValid entry point ExceptionHelper.ExpectException <MissingMethodException>(delegate() { ValidationUtilities.TryValidateCustomUpdateMethodCall("MethodWithParameters", context, parameters, null); }, "Method 'OpenRiaServices.DomainServices.Hosting.Test.ValidationUtilitiesTests+ValTestClass.MethodWithParameters('System.String', null)' could not be found. Parameter count: 2."); // Validate entry point ExceptionHelper.ExpectException <MissingMethodException>(delegate() { ValidationUtilities.ValidateMethodCall("MethodWithParameters", context, parameters); }, "Method 'OpenRiaServices.DomainServices.Hosting.Test.ValidationUtilitiesTests+ValTestClass.MethodWithParameters('System.String', null)' could not be found. Parameter count: 2."); }
public void Validation_Fail_Method_Too_Many_Params() { ValTestClass vtc = new ValTestClass(); ValidationContext context = ValidationUtilities.CreateValidationContext(vtc, null); object[] parameters = { "xxx", 2.0, 5 }; // actually requires 2 params // IsValid entry point ExceptionHelper.ExpectException <MissingMethodException>(delegate() { ValidationUtilities.TryValidateCustomUpdateMethodCall("MethodWithParameters", context, parameters, null); }, "Method 'OpenRiaServices.DomainServices.Hosting.Test.ValidationUtilitiesTests+ValTestClass.MethodWithParameters('System.String', 'System.Double', 'System.Int32')' could not be found. Parameter count: 3."); // Validate entry point ExceptionHelper.ExpectException <MissingMethodException>(delegate() { ValidationUtilities.ValidateMethodCall("MethodWithParameters", context, parameters); }, "Method 'OpenRiaServices.DomainServices.Hosting.Test.ValidationUtilitiesTests+ValTestClass.MethodWithParameters('System.String', 'System.Double', 'System.Int32')' could not be found. Parameter count: 3."); }
public void Validation_Method_Invalid_Null_Required_Nullable() { ValTestClass vtc = new ValTestClass(); ValidationContext context = ValidationUtilities.CreateValidationContext(vtc, null); string message = "The doubleParam field is required."; object[] parameters = { null }; // param is nullable but marked [Required] ExceptionHelper.ExpectValidationException(delegate { ValidationUtilities.TryValidateCustomUpdateMethodCall("MethodWithRequiredNullableParameter", context, parameters, null); }, message, typeof(RequiredAttribute), parameters[0]); List <ValidationResult> output = new List <ValidationResult>(); bool isValid = ValidationUtilities.TryValidateCustomUpdateMethodCall("MethodWithRequiredNullableParameter", context, parameters, output); Assert.IsFalse(isValid); Assert.AreEqual(1, output.Count); UnitTestHelper.AssertListContains(output, message); }
public void Validation_Method_Invalid_Bad_Params() { ValTestClass vtc = new ValTestClass(); ValidationContext context = ValidationUtilities.CreateValidationContext(vtc, null); string message = "The field FirstParameterDisplayName must be a string with a maximum length of 5."; object[] parameters = { "LongerThan5Chars", 2.0 }; // legal params ExceptionHelper.ExpectValidationException(delegate() { ValidationUtilities.TryValidateCustomUpdateMethodCall("MethodWithParameters", context, parameters, null); }, message, typeof(StringLengthAttribute), parameters[0]); List <ValidationResult> output = new List <ValidationResult>(); bool isValid = ValidationUtilities.TryValidateCustomUpdateMethodCall("MethodWithParameters", context, parameters, output); Assert.IsFalse(isValid); Assert.AreEqual(1, output.Count); UnitTestHelper.AssertListContains(output, message); }
public void Validation_Method_Invalid_Good_Params() { ValTestClass vtc = new ValTestClass(); ValidationContext context = ValidationUtilities.CreateValidationContext(vtc, null); string message = "-MethodDisplayName"; vtc._failMethod = true; // tells validation method below to fail the IsValid object[] parameters = { "hello", 2.0 }; // legal params ExceptionHelper.ExpectValidationException(delegate { ValidationUtilities.TryValidateCustomUpdateMethodCall("MethodWithParameters", context, parameters, null); }, message, typeof(CustomValidationAttribute), vtc); List <ValidationResult> output = new List <ValidationResult>(); bool isValid = ValidationUtilities.TryValidateCustomUpdateMethodCall("MethodWithParameters", context, parameters, output); Assert.IsFalse(isValid); Assert.AreEqual(1, output.Count); UnitTestHelper.AssertListContains(output, message); // hyphen means it came from default formatter }
internal static bool ValidateMethodCall(string methodName, ValidationContext validationContext, object[] parameters, List <ValidationResult> validationResults, bool performTypeValidation) { if (string.IsNullOrEmpty(methodName)) { throw new ArgumentNullException("methodName"); } if (validationContext == null) { throw new ArgumentNullException("validationContext"); } if (validationContext.ObjectInstance == null) { throw new ArgumentException(DataResource.ValidationUtilities_ContextInstance_CannotBeNull, "validationContext"); } MethodInfo method = GetMethod(validationContext.ObjectInstance, methodName, parameters); ValidationContext methodContext = CreateValidationContext(validationContext.ObjectInstance, validationContext); methodContext.MemberName = method.Name; DisplayAttribute display = GetDisplayAttribute(method); if (display != null) { methodContext.DisplayName = display.GetName(); } string memberPath = string.Empty; if (performTypeValidation) { memberPath = method.Name + "."; } IEnumerable <ValidationAttribute> validationAttributes = method.GetCustomAttributes(typeof(ValidationAttribute), true).Cast <ValidationAttribute>(); bool success = ValidationUtilities.ValidateValue(validationContext.ObjectInstance, methodContext, validationResults, validationAttributes, string.Empty); ParameterInfo[] parameterInfos = method.GetParameters(); for (int paramIndex = 0; paramIndex < parameterInfos.Length; paramIndex++) { ParameterInfo methodParameter = parameterInfos[paramIndex]; object value = (parameters.Length > paramIndex ? parameters[paramIndex] : null); ValidationContext parameterContext = ValidationUtilities.CreateValidationContext(validationContext.ObjectInstance, validationContext); parameterContext.MemberName = methodParameter.Name; string paramName = methodParameter.Name; display = GetDisplayAttribute(methodParameter); if (display != null) { paramName = display.GetName(); } parameterContext.DisplayName = paramName; string parameterPath = memberPath; if (performTypeValidation) { parameterPath += methodParameter.Name; } IEnumerable <ValidationAttribute> parameterAttributes = methodParameter.GetCustomAttributes(typeof(ValidationAttribute), false).Cast <ValidationAttribute>(); bool parameterSuccess = ValidationUtilities.ValidateValue(value, parameterContext, validationResults, parameterAttributes, ValidationUtilities.NormalizeMemberPath(parameterPath, methodParameter.ParameterType)); if (parameterSuccess && performTypeValidation && value != null) { Type parameterType = methodParameter.ParameterType; // If the user expects an exception we perform shallow validation only. if (validationResults == null) { if (TypeUtility.IsComplexType(parameterType)) { ValidationContext context = ValidationUtilities.CreateValidationContext(value, parameterContext); context.DisplayName = paramName; Validator.ValidateObject(value, context, /*validateAllProperties*/ true); } } // Else we are able to report fully recursive validation errors to the user, perform deep validation. else { if (TypeUtility.IsComplexType(parameterType)) { parameterSuccess = ValidationUtilities.ValidateObjectRecursive(value, parameterPath, validationContext, validationResults); } else if (TypeUtility.IsComplexTypeCollection(parameterType)) { parameterSuccess = ValidationUtilities.ValidateComplexCollection(value as IEnumerable, parameterPath, validationContext, validationResults); } } } success &= parameterSuccess; } return(success); }
internal static bool TryValidateMethodCall(DomainOperationEntry operationEntry, ValidationContext validationContext, object[] parameters, List <ValidationResult> validationResults) { bool breakOnFirstError = validationResults == null; ValidationContext methodContext = CreateValidationContext(validationContext.ObjectInstance, validationContext); methodContext.MemberName = operationEntry.Name; DisplayAttribute display = (DisplayAttribute)operationEntry.Attributes[typeof(DisplayAttribute)]; if (display != null) { methodContext.DisplayName = display.GetName(); } string methodPath = string.Empty; if (operationEntry.Operation == DomainOperation.Custom) { methodPath = operationEntry.Name + "."; } IEnumerable <ValidationAttribute> validationAttributes = operationEntry.Attributes.OfType <ValidationAttribute>(); bool success = Validator.TryValidateValue(validationContext.ObjectInstance, methodContext, validationResults, validationAttributes); if (!breakOnFirstError || success) { for (int paramIndex = 0; paramIndex < operationEntry.Parameters.Count; paramIndex++) { DomainOperationParameter methodParameter = operationEntry.Parameters[paramIndex]; object value = (parameters.Length > paramIndex ? parameters[paramIndex] : null); ValidationContext parameterContext = ValidationUtilities.CreateValidationContext(validationContext.ObjectInstance, validationContext); parameterContext.MemberName = methodParameter.Name; string paramName = methodParameter.Name; AttributeCollection parameterAttributes = methodParameter.Attributes; display = (DisplayAttribute)parameterAttributes[typeof(DisplayAttribute)]; if (display != null) { paramName = display.GetName(); } parameterContext.DisplayName = paramName; string parameterPath = string.Empty; if (!string.IsNullOrEmpty(methodPath) && paramIndex > 0) { parameterPath = methodPath + methodParameter.Name; } IEnumerable <ValidationAttribute> parameterValidationAttributes = parameterAttributes.OfType <ValidationAttribute>(); bool parameterSuccess = ValidationUtilities.ValidateValue(value, parameterContext, validationResults, parameterValidationAttributes, ValidationUtilities.NormalizeMemberPath(parameterPath, methodParameter.ParameterType)); // Custom methods run deep validation as well as parameter validation. // If parameter validation has already failed, stop further validation. if (parameterSuccess && operationEntry.Operation == DomainOperation.Custom && value != null) { Type parameterType = methodParameter.ParameterType; if (TypeUtility.IsComplexType(parameterType)) { parameterSuccess = ValidationUtilities.ValidateObjectRecursive(value, parameterPath, parameterContext, validationResults); } else if (TypeUtility.IsComplexTypeCollection(parameterType)) { parameterSuccess = ValidationUtilities.ValidateComplexCollection(value as IEnumerable, parameterPath, parameterContext, validationResults); } } success &= parameterSuccess; if (breakOnFirstError && !success) { break; } } } return(success); }
/// <summary> /// This method recursively validates an object, first validating all properties, then /// validating the type. This method implements the classic Try pattern. However it serves /// as code sharing for the validation pattern where an exception is thrown on first error. /// </summary> /// <param name="instance">The object to validate.</param> /// <param name="memberPath">The dotted path of the member.</param> /// <param name="validationContext">The validation context.</param> /// <param name="validationResults">The collection in which the validation results will be /// stored. The collection can be <c>null</c>.</param> /// <returns><c>True</c> if the object was successfully validated with no errors.</returns> /// <exception cref="ValidationException">When <paramref name="validationResults"/> is /// <c>null</c> and the object has a validation error.</exception> private static bool ValidateObjectRecursive(object instance, string memberPath, ValidationContext validationContext, List <ValidationResult> validationResults) { MetaType metaType = MetaType.GetMetaType(instance.GetType()); if (!metaType.RequiresValidation) { return(true); } // First validate all properties bool hasValidationErrors = false; foreach (MetaMember metaMember in metaType.Members.Where(m => m.RequiresValidation || m.IsComplex)) { ValidationContext propertyValidationContext = ValidationUtilities.CreateValidationContext(instance, validationContext); propertyValidationContext.MemberName = metaMember.Member.Name; // Form the current member path, appending the current // member name if it is complex. string currMemberPath = memberPath; if (metaMember.IsComplex) { if (currMemberPath.Length > 0) { currMemberPath += "."; } currMemberPath += metaMember.Member.Name; } object value = metaMember.GetValue(instance); // first validate the property itself if (metaMember.RequiresValidation) { hasValidationErrors |= !ValidationUtilities.ValidateProperty(value, propertyValidationContext, validationResults, currMemberPath); } // for complex members, in addition to property level validation we need to // do deep validation recursively if (value != null && metaMember.IsComplex) { if (!metaMember.IsCollection) { hasValidationErrors |= !ValidateObjectRecursive(value, currMemberPath, validationContext, validationResults); } else { hasValidationErrors |= !ValidateComplexCollection((IEnumerable)value, currMemberPath, validationContext, validationResults); } } } // Only proceed to Type level validation if there are no property validation errors if (hasValidationErrors) { return(false); } // Next perform Type level validation without validating properties, since we've already validated all properties. // Note that we can't use Validator.ValidateObject specifying 'validateAllProperties' since even when specifying false, // that API will validate RequiredAttribute. ValidationContext context = ValidationUtilities.CreateValidationContext(instance, validationContext); if (metaType.ValidationAttributes.Any()) { hasValidationErrors |= !ValidationUtilities.ValidateValue(instance, context, validationResults, metaType.ValidationAttributes, memberPath); } #if !SILVERLIGHT // Only proceed to IValidatableObject validation if there are no errors if (hasValidationErrors) { return(false); } // Test for IValidatableObject implementation and run the validation if applicable // Note : this interface doesn't exist in Silverlight IValidatableObject validatable = instance as IValidatableObject; if (validatable != null) { IEnumerable <ValidationResult> results = validatable.Validate(context); if (!string.IsNullOrEmpty(memberPath)) { results = ValidationUtilities.ApplyMemberPath(results, memberPath); } foreach (ValidationResult result in results.Where(r => r != ValidationResult.Success)) { validationResults.Add(result); hasValidationErrors = true; } } #endif return(!hasValidationErrors); }
public void TestEntityValidation_ComplexTypes() { // ensure TDPs are registered DomainServiceDescription.GetDescription(typeof(ComplexTypes_TestService)); ComplexType_Parent entity = new ComplexType_Parent { ID = 1, ContactInfo = new ContactInfo { Name = "Mathew", HomeAddress = new Address { AddressLine1 = "47 South Wynn Rd.", City = "Oregon", State = "OH" }, PrimaryPhone = new Phone { AreaCode = "419", Number = "693-6096" } }, }; ValidationContext validationContext = ValidationUtilities.CreateValidationContext(entity, null); List <ValidationResult> results = new List <ValidationResult>(); bool isValid = ValidationUtilities.TryValidateObject(entity, validationContext, results); Assert.IsTrue(isValid && results.Count == 0); // set an invalid property and revalidate entity.ContactInfo.PrimaryPhone.AreaCode = "Invalid"; results = new List <ValidationResult>(); isValid = ValidationUtilities.TryValidateObject(entity, validationContext, results); Assert.IsTrue(!isValid && results.Count == 1); ValidationResult result = results.Single(); Assert.AreEqual("The field AreaCode must be a string with a maximum length of 3.", result.ErrorMessage); Assert.AreEqual("ContactInfo.PrimaryPhone.AreaCode", result.MemberNames.Single()); // create TWO validation errors entity.ContactInfo.PrimaryPhone.AreaCode = "Invalid"; entity.ContactInfo.HomeAddress.State = "Invalid"; results = new List <ValidationResult>(); isValid = ValidationUtilities.TryValidateObject(entity, validationContext, results); Assert.IsTrue(!isValid && results.Count == 2); Assert.AreEqual("The field State must be a string with a maximum length of 2.", results[0].ErrorMessage); Assert.AreEqual("ContactInfo.HomeAddress.State", results[0].MemberNames.Single()); Assert.AreEqual("The field AreaCode must be a string with a maximum length of 3.", results[1].ErrorMessage); Assert.AreEqual("ContactInfo.PrimaryPhone.AreaCode", results[1].MemberNames.Single()); // verify custom validation was called at CT type and property levels entity.ContactInfo.PrimaryPhone.AreaCode = "419"; entity.ContactInfo.HomeAddress.State = "OH"; DynamicTestValidator.Monitor(true); results = new List <ValidationResult>(); isValid = ValidationUtilities.TryValidateObject(entity, validationContext, results); Assert.AreEqual(3, DynamicTestValidator.ValidationCalls.Count); Assert.IsNotNull(DynamicTestValidator.ValidationCalls.Single(v => v.MemberName == null && v.ObjectType == typeof(ContactInfo))); Assert.IsNotNull(DynamicTestValidator.ValidationCalls.Single(v => v.MemberName == "ContactInfo" && v.ObjectType == typeof(ComplexType_Parent))); Assert.IsNotNull(DynamicTestValidator.ValidationCalls.Single(v => v.MemberName == null && v.ObjectType == typeof(Phone))); DynamicTestValidator.Monitor(false); // verify deep CT collection validation List <ComplexType_Recursive> children = new List <ComplexType_Recursive> { new ComplexType_Recursive { P1 = "1", P4 = -1 }, // invalid element new ComplexType_Recursive { P1 = "2", P3 = new List <ComplexType_Recursive> { new ComplexType_Recursive { P1 = "3", P4 = -5 } // invalid element in nested collection } } }; ComplexType_Scenarios_Parent parent = new ComplexType_Scenarios_Parent { ID = 1, ComplexType_Recursive = new ComplexType_Recursive { P1 = "1", P3 = children } }; validationContext = ValidationUtilities.CreateValidationContext(parent, null); results = new List <ValidationResult>(); isValid = ValidationUtilities.TryValidateObject(parent, validationContext, results); Assert.AreEqual(2, results.Count); result = results.Single(p => p.MemberNames.Single() == "ComplexType_Recursive.P3().P4"); Assert.AreEqual("The field P4 must be between 0 and 5.", result.ErrorMessage); result = results.Single(p => p.MemberNames.Single() == "ComplexType_Recursive.P3().P3().P4"); Assert.AreEqual("The field P4 must be between 0 and 5.", result.ErrorMessage); }