Data Annotations on record fails in unittest - c#

Currently we are setting up a new project and like to use the new records introduced in C# 9.
We encounter a problem with DataAnnotations inside the record (constructor) not being triggered during the unittest.
Now the DataAnnotation is triggered when calling the Controller, but when i try to simulate this in a unittest (see code below) it will never return any errors.
//Unit Testing ASP.NET DataAnnotations validation
//http://stackoverflow.com/questions/2167811/unit-testing-asp-net-dataannotations-validation
protected static IList<ValidationResult> ValidateModel(object model)
{
var validationResults = new List<ValidationResult>();
var ctx = new ValidationContext(model, null, null);
Validator.TryValidateObject(model, ctx, validationResults, true);
return validationResults;
}
Currently we created a workaround:
public record FooRecord(string BarProperty)
{
[Required]
public string BarProperty { get; init; } = BarProperty;
}
But I'm hoping if someone knows why this happens and maybe know how to solve this using the shorthand syntax:
public record FooRecord([Required] BarProperty){ }

It will work as expected if you define your record as:
public record FooRecord([property: Required] BarProperty){ }

Related

Validator ignoring MaxLength attributes

Problem:
I am trying to manually Validate some c# objects, and the Validator is ignoring string length related validations.
Test Case:
extending this example which uses the [Required] attribute, i also wanted to validate that strings were not too long, as follows.
public class Recipe
{
//[Required]
public string Name { get; set; }
[MaxLength(1)] public string difficulty = "a_string_that_is_too_long";
}
public static void Main(string[] args)
{
var recipe = new Recipe();
var context = new ValidationContext(recipe, serviceProvider: null, items: null);
var results = new List<ValidationResult>();
var isValid = Validator.TryValidateObject(recipe, context, results);
if (!isValid)
{
foreach (var validationResult in results)
{
Console.WriteLine(validationResult.ErrorMessage);
}
} else {
Console.WriteLine("is valid");
}
}
Expected result: an error: "difficulty is too long."
Actual result: 'is valid'
other things tested:
the validator is working, uncommenting the [Required] results in the message "The Name field is required."
using [StringLength] instead (as noted
at https://stackoverflow.com/a/6802739/432976 ) made no difference.
You need to make 2 changes to have the validation work the way you expect:
1. You have to change the difficulty field to a property.
The Validator class only validates properties, so change the difficulty definition to a property like this:
[MaxLength(1)] public string difficulty { get; set; } = "a_string_that_is_too_long";
2. Specify the validateAllProperties: true parameter to the Validator.TryValidateObject call.
The documentation for Validator.TryValidateObject is not very forthcoming about the fact that, unless you use the overload with validateAllProperties: true, only the Required attribute will be checked. So modify the call like this:
var isValid = Validator.TryValidateObject(recipe,
context,
results,
validateAllProperties: true);

C# Entity Framework: Data validation between add to context and saveChanges()

I have a simple scenario using the Entity Framework in C#. I have an Entity Post:
public class Post
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
In my PostManager I have these methods:
public int AddPost(string name, string description)
{
var post = new Post() { Name = name, Description = description };
using (var db = new DbContext())
{
var res = db.Posts.Add(post);
res.Validate();
db.SaveChanges();
return res.Id;
}
}
public void UpdatePost(int postId, string newName, string newDescription)
{
using (var db = new DbContext())
{
var data = (from post in db.Posts.AsEnumerable()
where post.Id == postId
select post).FirstOrDefault();
data.Name = newName;
data.Description = newDescription;
data.Validate();
db.SaveChanges();
}
}
The method validate() refers to class:
public static class Validator
{
public static void Validate(this Post post)
{
if ( // some control)
throw new someException();
}
I call the validate method before the savechanges() but after adding the object to the context. What's the best practice to validate data in this simple scenario? It's better validate the arguments instead? What's happen to object post if the validate method throw exception after adding the object to the context?
UPDATE:
I have to throw a custom set of exception depending on data validation error.
I strongly recommend you to (if at all possible) to modify your entity so the setters are private (don't worry, EF can still set them on proxy creation), mark the default constructor as protected (EF can still do lazy loading/proxy creation), and make the only public constructors available check the arguments.
This has several benefits:
You limit the number of places where the state of an entity can be changed, leading to less duplication
You protect your class' invariants. By forcing creation of an entity to go via a constructor, you ensure that it is IMPOSSIBLE for an object of your entity to exist in an invalid or unknown state.
You get higher cohesion. By putting the constraints on data closer to the data itself, it becomes easier to understand and reason about your classes.
You code becomes self-documenting to a higher degree. One never has to wonder "is it OK if I set a negative value on this int property?" if it is impossible to even do it in the first place.
Separation of concerns. Your manager shouldn't know how to validate an entity, this just leads to high coupling. I've seen many managers grow into unmaintainable monsters because they simply do everything. Persisting, loading, validation, error handling, conversion, mapping etc. This is basically the polar opposite of SOLID OOP.
I know it is really popular nowadays to just make all "models" into stupid property bags with getters and setters and only a default constructor because (bad) ORMs have forced us to do this, but this is no longer the case, and there are so many issues with this imo.
Code example:
public class Post
{
protected Post() // this constructor is only for EF proxy creation
{
}
public Post(string name, string description)
{
if (/* validation check, inline or delegate */)
throw new ArgumentException();
Name = name;
Description = description;
}
public int Id { get; private set; }
public string Name { get; private set; }
public string Description { get; private set; }
}
Then your PostManager code becomes trivial:
using (var db = new DbContext())
{
var post = new Post(name, description); // possibly try-catch here
db.Posts.Add(post);
db.SaveChanges();
return post.Id;
}
If the creation/validation logic is extremely intricate this pattern lends itself very well for refactoring to a factory taking care of the creation.
I would also note that encapsulating data in entities exposing a minimal state-changing API leads to classes that are several orders of magnitude easier to test in isolation, if you care at all about that sort of thing.
As I mentioned in the comments above, you might want to check out .NET System.ComponentModel.DataAnnotations namespace.
Data Annotations (DA) allows you to specify attributes on properties to describe what values are acceptable. It's important to know that DA is completely independent of databases and ORM APIs such as Entity Framework so classes decorated with DA attributes can be used in any tier of your system whether it be the data tier; WCF; ASP.NET MVC or WPF.
In the example below, I define a Muppet class with a series of properties.
Name is required and has a max length of 50.
Scaryness takes an int but it must be in the range of {0...100}.
Email is decorated with an imaginary custom validator for validating strings that should contain an e-mail.
Example:
public class Muppet
{
[Required]
[StringLength(50)]
public string Name {get; set;}
public Color Color {get; set; }
[Range(0,100)]
public int Scaryness {get; set; }
[MyCustomEmailValidator]
public string Email {get;set; }
}
In my project I have to throw customException when i validate the data. It's possible do it using Data Annotations?
Yes you can. To validate this object at any time of your application (regardless of whether it has reached EF or not) just perform this:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
.
.
.
Post post = ... // fill it in
Validator.Validate(post);
public static class Validator
{
public static void Validate(this Post post)
{
// uses the extension method GetValidationErrors defined below
if (post.GetValidationErrors().Any())
{
throw new MyCustomException();
}
}
}
public static class ValidationHelpers
{
public static IEnumerable<ValidationResult> GetValidationErrors(this object obj)
{
var validationResults = new List<ValidationResult>();
var context = new ValidationContext(obj, null, null);
Validator.TryValidateObject(obj, context, validationResults, true);
return validationResults;
}
.
.
.
If you want to get the validation error messages you could use this method:
/// <summary>
/// Gets the validation error messages for column.
/// </summary>
/// <param name="obj">The object.</param>
/// <returns></returns>
public static string GetValidationErrorMessages(this object obj)
{
var error = "";
var errors = obj.GetValidationErrors();
var validationResults = errors as ValidationResult[] ?? errors.ToArray();
if (!validationResults.Any())
{
return error;
}
foreach (var ee in validationResults)
{
foreach (var n in ee.MemberNames)
{
error += ee + "; ";
}
}
return error;
}
The free set of steak knives is that the validation attributes will be detected once the object reaches EF where it will be validated there as well in case you forget or the object is changed since.
I think you should be working with Data Annotation as #Micky says above. Your current approach is validating manually after it has been added.
using System.ComponentModel.DataAnnotations;
// Your class
public class Post
{
[Required]
public int Id { get; set; }
[Required,MaxLength(50)]
public string Name { get; set; }
[Required,MinLength(15),MyCustomCheck] // << Here is your custom validator
public string Description { get; set; }
}
// Your factory methods
public class MyFactory() {
public bool AddPost() {
var post = new Post() { Id = 1, Name = null, Description = "This is my test post"};
try {
using (var db = new DbContext()) {
db.Posts.Add(post);
db.SaveChanges();
return true;
}
} catch(System.Data.Entity.Validation.DbEntityValidationException e) {
Console.WriteLine("Something went wrong....");
} catch(MyCustomException e) {
Console.WriteLine(" a Custom Exception was triggered from a custom data annotation...");
}
return false;
}
}
// The custom attribute
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)]
sealed public class MyCustomCheckAttribute : ValidationAttribute
{
public override bool IsValid(object value)
{
if (value instanceof string) {
throw new MyCustomException("The custom exception was just triggered....")
} else {
return true;
}
}
}
// Your custom exception
public class MyCustomException : Exception() {}
See also:
DbEntityValidationException class: https://msdn.microsoft.com/en-us/library/system.data.entity.validation.dbentityvalidationexception(v=vs.113).aspx
Default data annotations
http://www.entityframeworktutorial.net/code-first/dataannotation-in-code-first.aspx
Building your custom data annotations (validators):
https://msdn.microsoft.com/en-us/library/cc668224.aspx
I always use two validations:
client side - using jQuery Unobtrusive Validation in combination with Data Annotations
server side validation - and here it depends on application - validation is performed in controller actions or deeper in business logic. Nice place to do it is to override OnSave method in your context and do it there
Remember that you can write custom Data Annotation attributes which can validate whatever you need.
You can modify the code in this way:
public int AddPost(string name, string description)
{
var post = new Post() { Name = name, Description = description };
if(res.Validate())
{
using (var db = new DbContext())
{
var res = db.Posts.Add(post);
db.SaveChanges();
return res.Id;
}
}
else
return -1; //if not success
}
public static bool Validate(this Post post)
{
bool isValid=false;
//validate post and change isValid to true if success
if(isvalid)
return true;
}
else
return false;
}
After adding data to DbContext and before calling SaveChanges() you can call GetValidationErrors() method of DbContext and check its count to quiqckly check if there are any errors. You can further enumerate all of errors and get error details against each of them. I have bundled Error conversion from ICollection to string in GetValidationErrorsString() extension method.
if (db.GetValidationErrors().Count() > 0)
{
var errorString = db.GetValidationErrorsString();
}
public static string GetValidationErrorsString(this DbContext dbContext)
{
var validationErrors = dbContext.GetValidationErrors();
string errorString = string.Empty;
foreach (var error in validationErrors)
{
foreach (var innerError in error.ValidationErrors)
{
errorString += string.Format("Property: {0}, Error: {1}<br/>", innerError.PropertyName, innerError.ErrorMessage);
}
}
return errorString;
}

How to manually validate a model with attributes?

I have a class called User and a property Name
public class User
{
[Required]
public string Name { get; set; }
}
And I want to validate it, and if there are any errors add to the controller's ModelState or instantiate another modelstate...
[HttpPost]
public ActionResult NewUser(UserViewModel userVM)
{
User u = new User();
u.Name = null;
/* something */
// assume userVM is valid
// I want the following to be false because `user.Name` is null
if (ModelState.IsValid)
{
TempData["NewUserCreated"] = "New user created sucessfully";
return RedirectToAction("Index");
}
return View();
}
The attributes works for UserViewModel, but I want to know how to validate a class without posting it to an action.
How can I accomplish that?
You can use Validator to accomplish this.
var context = new ValidationContext(u, serviceProvider: null, items: null);
var validationResults = new List<ValidationResult>();
bool isValid = Validator.TryValidateObject(u, context, validationResults, true);
I made an entry in the Stack Overflow Documentation explaining how to do this:
Validation Context
Any validation needs a context to give some information about what is being validated. This can include various information such as the object to be validated, some properties, the name to display in the error message, etc.
ValidationContext vc = new ValidationContext(objectToValidate); // The simplest form of validation context. It contains only a reference to the object being validated.
Once the context is created, there are multiple ways of doing validation.
Validate an Object and All of its Properties
ICollection<ValidationResult> results = new List<ValidationResult>(); // Will contain the results of the validation
bool isValid = Validator.TryValidateObject(objectToValidate, vc, results, true); // Validates the object and its properties using the previously created context.
// The variable isValid will be true if everything is valid
// The results variable contains the results of the validation
Validate a Property of an Object
ICollection<ValidationResult> results = new List<ValidationResult>(); // Will contain the results of the validation
bool isValid = Validator.TryValidatePropery(objectToValidate.PropertyToValidate, vc, results, true); // Validates the property using the previously created context.
// The variable isValid will be true if everything is valid
// The results variable contains the results of the validation
And More
To learn more about manual validation see:
ValidationContext Class Documentation
Validator Class Documentation
I wrote a wrapper to make this a bit less clunky to work with.
Usage:
var response = SimpleValidator.Validate(model);
var isValid = response.IsValid;
var messages = response.Results;
Or if you only care about checking validity, it's even tighter:
var isValid = SimpleValidator.IsModelValid(model);
Complete source:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace Ether.Validation
{
public static class SimpleValidator
{
/// <summary>
/// Validate the model and return a response, which includes any validation messages and an IsValid bit.
/// </summary>
public static ValidationResponse Validate(object model)
{
var results = new List<ValidationResult>();
var context = new ValidationContext(model);
var isValid = Validator.TryValidateObject(model, context, results, true);
return new ValidationResponse()
{
IsValid = isValid,
Results = results
};
}
/// <summary>
/// Validate the model and return a bit indicating whether the model is valid or not.
/// </summary>
public static bool IsModelValid(object model)
{
var response = Validate(model);
return response.IsValid;
}
}
public class ValidationResponse
{
public List<ValidationResult> Results { get; set; }
public bool IsValid { get; set; }
public ValidationResponse()
{
Results = new List<ValidationResult>();
IsValid = false;
}
}
}
Or at this gist: https://gist.github.com/kinetiq/faed1e3b2da4cca922896d1f7cdcc79b
Since the question is asking specifically about ASP.NET MVC, you can use the TryValidateObject inside your Controller action.
Your desired method overload is TryValidateModel(Object)
Validates the specified model instance.
Returns true if the model validation is successful; otherwise false.
Your modified source code
[HttpPost]
public ActionResult NewUser(UserViewModel userVM)
{
User u = new User();
u.Name = null;
if (this.TryValidateObject(u))
{
TempData["NewUserCreated"] = "New user created sucessfully";
return RedirectToAction("Index");
}
return View();
}
There is another approach to validation, which is more easy reusable - FluentValidation
This library allows to play with inheritance, different rule sets for one model and has many other cool features. Read about advantages here.
With this library definition of validation rules is separated from model and code will look next way:
public class UserValidator:AbstractValidator<User>
{
public UserValidator()
{
RuleFor(x => x.Name).NotEmpty();
}
}
Usage will look next way:
var validator = new UserValidator();
var validationResult = await validator.ValidateAsync(model);

Recursive validation using annotations and IValidatableObject

I am trying to validate nested objects (not models in the MVC senss) using annotations and some custom code.
I found the following post useful
Using Data Annotations Validation Manually and Object Graphs
As suggested in an answer, I've created an extra routine in the container class to validate the nested object. Here's my modified test code
public class Customer : IValidatableObject
{
public Customer()
{
Details = new CustomerDetails();
}
[Required]
[MaxLength(2)]
public string Name
{
get;
set;
}
public CustomerDetails Details
{
get;
private set;
}
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
var context = new ValidationContext(this.Details, validationContext.ServiceContainer, validationContext.Items);
var results = new List<ValidationResult>();
Validator.TryValidateObject(this.Details, context, results);
return results;
}
}
However I have problems getting all the validation errors, even when calling TryValidateObject with validateAllProperties set to true.
var context = new ValidationContext(cs, null, null);
var results = new List<ValidationResult>();
Validator.TryValidateObject(cs, context, results,true);
If there are any errors in the container, only these will show. Only when there are no errors in the container object, errors in the nested object will show. I suspect it has something to do with the Validate rouine returning a full list, and not being able to add to an (existing) list from the container(?)
Are there any modifications I can make to routine to get all errors to show?
See this answer: https://stackoverflow.com/a/3400627/724944
So, there is an error in your class' atributes, and therefore Validate method doesn't get called.
I suggest using CustomValidationAttribute like this:
[CustomValidation(typeof(Customer), "ValidateRelatedObject")]
public CustomerDetails Details
{
get;
private set;
}
public static ValidationResult ValidateRelatedObject(object value, ValidationContext context)
{
var context = new ValidationContext(value, validationContext.ServiceContainer, validationContext.Items);
var results = new List<ValidationResult>();
Validator.TryValidateObject(value, context, results);
// TODO: Wrap or parse multiple ValidationResult's into one ValidationResult
return result;
}

How do I invoke a validation attribute for testing?

I am using the RegularExpressionAttribute from DataAnnotations for validation and would like to test my regex. Is there a way to invoke the attribute directly in a unit test?
I would like to be able to do something similar to this:
public class Person
{
[RegularExpression(#"^[0-9]{3}-[0-9]{3}-[0-9]{4}$")]
public string PhoneNumber { get; set; }
}
Then in a unit test:
[TestMethod]
public void PhoneNumberIsValid
{
var dude = new Person();
dude.PhoneNumber = "555-867-5309";
Assert.IsTrue(dude.IsValid);
}
Or even
Assert.IsTrue(dude.PhoneNumber.IsValid);
I ended up using the static Validator class from the DataAnnotations namespace. My test now looks like this:
[TestMethod]
public void PhoneNumberIsValid()
{
var dude = new Person();
dude.PhoneNumber = "666-978-6410";
var result = Validator.TryValidateObject(dude, new ValidationContext(dude, null, null), null, true);
Assert.IsTrue(result);
}
Just new up a RegularExpressionAttribute object.
var regularExpressionAttribute = new RegularExpressionAttribute("pattern");
Assert.IsTrue(regularExpressionAttribute.IsValid(objToTest));
Sorry for answering late.
I'm new here. If you want test every ValidationAttribute in isolate you can proceed to the next manner for example:
[Test]
public void Test_the_State_value_IsRequired()
{
string value = "Finished";
var propertyInfo = typeof(TimeoffTemporalIncapacityEntry).GetProperty("State");
var attribute = propertyInfo.GetCustomAttributes(typeof(RequiredAttribute), true).Cast<RequiredAttribute>().FirstOrDefault();
Assert.IsTrue(attribute.IsValid(value));
}
I used the #Martin 's suggestion along with a static constants file which allowed me to avoid specifing the regex string locally
[TestMethod]
public void Test_Regex_NationalinsuranceNumber()
{
var regularExpressionAttribute = new RegularExpressionAttribute(Constants.Regex_NationalInsuranceNumber_Validate);
List<string> validNINumbers = new List<string>() { "TN311258F", "QQ123456A" };
List<string> invalidNINumbers = new List<string>() { "cake", "1234", "TS184LZ" };
validNINumbers.ForEach(p => Assert.IsTrue(regularExpressionAttribute.IsValid(p)));
invalidNINumbers.ForEach(p => Assert.IsFalse(regularExpressionAttribute.IsValid(p)));
}
You can use this class for validate any ValidationAttribute type in isolate:
T = class type containing the property,
A = type ValidationAttribute
Example:
string stateValue = "Pendiente";
ValidationAttributeValidator<ConfirmationTemporalIncapacityEntry, RequiredAttribute> validator =
new ValidationAttributeValidator<ConfirmationTemporalIncapacityEntry, RequiredAttribute>();
Assert.IsTrue(validator.ValidateValidationAttribute("State", stateValue));
public class ValidationAttributeValidator<T,A>
{
public ValidationAttributeValidator() { }
public bool ValidateValidationAttribute(string property, object value)
{
var propertyInfo = typeof(T).GetProperty(property);
var validationAttributes = propertyInfo.GetCustomAttributes(true);
if (validationAttributes == null)
{
return false;
}
List<ValidationAttribute> validationAttributeList = new List<ValidationAttribute>();
foreach (object attribute in validationAttributes)
{
if (attribute.GetType() == typeof(A))
{
validationAttributeList.Add((ValidationAttribute)attribute);
}
}
return(validationAttributeList.Exists(x => x.IsValid(value)));
}
}
Building on #Evelio's answer I am going to provide an answer to how do you unit test custom validators since this doesn't seem to be articulated anywhere on the internet and this is one of the top hits that come up when searching for how to do it.
#Evelio's answer is very close, but it could do with a bit more of an explanation.
To test your validation you need to have a class that attaches validation attributes to its member data. Here I am using a new custom validator that makes sense for my project called FeeTimeUnitValidator. This validator takes a range and another attribute as input. If the other attribute is zero, then the attribute the validator is attached to doesn't matter. But if the other attribute is not zero, then this attribute needs to be in the range.
Here is the MockClass I use for testing:
class MockClass
{
public decimal Fee { get; set; }
[FeeTimeUnitValidator(otherPropertyName:"Fee", minValue:1, maxValue:12)]
public int attributeUnderTest { get; set; }
public int badOtherProperty { get; set; }
[FeeTimeUnitValidator(otherPropertyName: "badOtherProperty", minValue: 1, maxValue: 12)]
public int badAttributeUnderTest { get; set; }
[FeeTimeUnitValidator(otherPropertyName: "NotFoundAttribute", minValue: 1, maxValue: 12)]
public int nameNotFoundAttribute { get; set; }
}
Notice the attribute validation:
[FeeTimeUnitValidator(otherPropertyName:"Fee", minValue:1, maxValue:12)]
This says to check the property "Fee" as the Fee property (i.e., it has to be non-zero) and then the range is 1 - 12.
I instantiate class in the unit test class and set it up with a setup method. Since there are three attributes on this class that have the validator, I pass in the name of the attribute into the setup class.
private MockClass classUnderTest;
private ValidationContext context;
FeeTimeUnitValidator setup(string attributeUnderTest)
{
classUnderTest = new MockClass();
classUnderTest.Fee = 0;
var propertyInfo = typeof(MockClass).GetProperty(attributeUnderTest);
var validatorArray = propertyInfo.GetCustomAttributes(typeof(FeeTimeUnitValidator), true);
Assert.AreEqual(1, validatorArray.Length);
var validator = validatorArray[0];
Assert.IsTrue(validator.GetType().Equals(typeof(FeeTimeUnitValidator)));
context = new ValidationContext(classUnderTest, null, null);
return (FeeTimeUnitValidator)validator;
}
There are a few things of interest. I am using #Evelio's approach to extract the validator from the attribute. This is doe in lines 3 and 4 of the setup routine. Then, since this is a unit test method, I do some asserts to make sure that I got what I expected. This actually caught a problem when I transferred this pattern to another unit test class for another validator.
Then the other key is that I create the ValidationContext (since the more complicated validators need a context to find the other attributes they refer to - in my case I use it to find the Fee attribute). When I was researching how to unit test these custom validators, what was tripping me up was the ValidationContext. I couldn't find any information about how to create them. I believe the "context" for the attribute validation is the class in which the attribute lives. This is why I create the validation context with the class instance as the first parameter. This then provides the validator with access to the other attributes on the class so you can do cross attribute validation.
Now that i have the context created and a pointer to a validator, I can jump into the unit test itself to ensure that the validator is doing its job properly:
[TestMethod]
public void TestInRangeIsValidWhenFeeNonZero()
{
// Arrange
var validator = setup("attributeUnderTest");
classUnderTest.Fee = 10;
// Act
ValidationResult value12 = validator.GetValidationResult(12, context);
ValidationResult value1 = validator.GetValidationResult(1, context);
ValidationResult value5 = validator.GetValidationResult(5, context);
// Assert
Assert.AreEqual(ValidationResult.Success, value12);
Assert.AreEqual(ValidationResult.Success, value1);
Assert.AreEqual(ValidationResult.Success, value5);
}
If my validator didn't need a context (i.e., it could validate the attribute without reference to the other attributes), then I could use the simpler interface of IsValid(), but if the validator needs a non-null context, you have to use the GetValidationResult() method like I have done here.
I hope this helps somebody else who might be writing validators and is as religious about unit testing as I am. :)
Here is a good article on creating custom validators.
Extending on #CobraGeek's answer and #Erik's comment, you can use the Validator.TryValidateProperty to validate only that one field instead of the whole object, as so:
var results = new List<ValidationResult>();
Person dude = new Person();
System.ComponentModel.TypeDescriptor.AddProviderTransparent
(new AssociatedMetadataTypeTypeDescriptionProvider(dude.GetType()), dude.GetType());
dude.PhoneNumber = "555-867-5309";
var vc = new ValidationContext(dude, null, null);
vc.MemberName = "PhoneNumber";
bool result = Validator.TryValidateProperty(dude.PhoneNumber, vc, results);
After which result is the boolean indicating success of the validation, and if false results contains the list of the details of the errors thrown.
// You can do something like this.
[TestMethod]
public void PhoneNumberIsValid
{
var propInfo = typeof(Person).GetProperty("PhoneNumber");
var attr = propInfo.GetCustomAttributes(typeof(RegularExpressionAttribute), true);
// Act Assert Positives
Assert.IsTrue(((RegularExpressionAttribute)attr [0]).IsValid("555-55-5555"));
// Act Assert Negative
Assert.IsFalse(((RegularExpressionAttribute)attr[0]).IsValid("123654654654"));
}

Categories

Resources