Click here to Skip to main content
Email Password   helpLost your password?

Introduction

There is a basic need for validation of objects at all levels. ASP.NET provides this service at the presentation layer, but there is really no formal way to achieve this at the business layer.

Problem

I've been playing around a lot with O/R mappers and have come to realize that it sucks with hand-coding validation all the time. It is quite repetitive and boiler plate code that simply gets boring. Castle's Active Record(which is quite good) has a validation system built in. It is based on Ruby(on Rails)'s ActiveRecord pattern which has an even more robust validation system. The problem I have found is that the validation framework is tightly coupled with the underlying entity model. There is no way to leverage the framework elsewhere.

Initial Shoutouts

I've been working for a little while on this and recently found another Validation Framework and read through the source code. It had some good ideas which I selectively incorporated into this framework (the CompareValidator is the main one). He mentions an article he read which, by circumstance, I also read and got some ideas.

ValidationManager

There is really just one main class, the ValidationManager. This class represents the central repository for validators. It has 2 static methods: one to add a Validator to a type and one to validate an instance. For example:

Validator val = RequiredValidator.CreateValidator<TestClass>("TestProperty");
ValidationManager.AddGlobalValidator<TestClass>(val);
TestClass tc = new TestClass();
if(ValidationManager.IsValid(tc))
   MessageBox.Show("WOO HOO");

This code has now registered a validator with the type TestClass for all new instances. In addition, any instances of TestClass already created are notified of the addition and they refresh their validator cache. (This method of validation is not preferable because of the overhead involved in instantiating a ValidationManager instance.)

Hmmm... what is the validator cache? The validator cache is an instance reference to the global validator cache for the instance's type. This means that we can also add instance validators as opposed to global validators. For example:

TestClass tc = new TestClass();
ValidationManager vm = new ValidationManager(tc);

Validator val = RequiredValidator.CreateValidator<TestClass>("TestProperty");
vm.AddValidator(val);

This code has now registered a validator with the tc instance. No other instance of TestClass has this required validator on it. You'll also notice that ValidationManager takes an object as a constructor parameter. This instance of ValidationManager is tied to the instance of the object passed in. For example:

TestClass tc = new TestClass();
ValidationManager vm = new ValidationManager(tc);
if(vm.IsValid())
   MessageBox.Show("WOO HOO");

Attribute Based Validation

The framework, while able to be manipulated programatically, is much easier to use through attributes. For example:

public class TestClass
{
    private string testProperty;

    [RequiredValidator]
    [LengthValidator(1,10)]
    public string TestProperty
    {
        get { return this.testProperty; }
        set { this.testProperty = value; }
    }
}
...


TestClass tc = new TestClass();
ValidationManager vm = new ValidationManager(tc);

if(vm.IsValid())
   MessageBox.Show("WOO HOO");

When a ValidationManager is instantiated, it scans TestClass via reflection for ValidatorAttributes and adds them to the global validator cache. It only does this once per type so the overhead happens only once.

Each validator attribute contains the common properties ErrorMessage and Order. These can be specified as named parameters but are not required. ErrorMessage has a default and Order indicates the order in which to process the validator if there are more than one. (SIDENOTE: order only applies to attributes and not to instance validators).

Validators

Currently there are 7 validators. Each validator has a corresponding attribute to use. In addition, each attribute has static methods that facilitate the creation of itself programatically.

  1. Compare Validator

    Applicable to types that implement IComparable.

    Tests by comparing the instance value with a constant value using a specified comparison operator.

    ...
    [CompareValidator(ComparisonOperator.GreaterThan,0)]
    public int TestProperty
    {
        get { return this.testProperty; }
        set { this.testProperty = value; }
    }
    ...
    
  2. Custom Validator

    Applicable to any type.

    Takes a method name as a parameter that exists in the type. The method must conform to the Predicate<T> delegate signature.

    ...
    [CustomValidator("TestProperty cannot contain a period.","NoPeriodTest")]
    public string TestProperty
    {
        get { return this.testProperty; }
        set { this.testProperty = value; }
    }
    
    private bool NoPeriodTest(string value)
    {
        return !value.Contains(".");
    }
    ...
    
  3. Length Validator

    Applicable to System.String and types that implement ICollection.

    Tests by ensuring that the string's length or collection's count falls within the length restriction.

    ...
    [LengthValidator(1,10)]
    public string TestProperty
    {
        get { return this.testProperty; }
        set { this.testProperty = value; }
    }
    ...
    
  4. NotEmpty Validator

    Applicable to types that implement IEnumerable.

    Tests by ensuring that there is at least 1 element to enumerate.

    ...
    [NotEmptyValidator]
    public string TestProperty
    {
        get { return this.testProperty; }
        set { this.testProperty = value; }
    }
    ...
    
  5. Range Validator

    Applicable to types that implement IComparable.

    Tests by checking whether the value falls within the minvalue and the maxvalue. [inclusively].

    ...
            [RangeValidator(1,10)]
            public int TestProperty
            {
                get { return this.testProperty; }
                set { this.testProperty = value; }
            }
    ...
    
  6. Regular Expression Validator

    Applicable to strings.

    Tests by checking whether the value matches the regular expression.

    ...
    [RegexValidator(@"\d+")]
    public string TestProperty
    {
        get { return this.testProperty; }
        set { this.testProperty = value; }
    }
    ...
    
  7. Required Validator

    Applicable to any type. However, value types such as int will always pass validation. Nullable types work as expected.

    Tests for null. Therefore, while it is applicable to value types, it will always return true. Nullable types behave normally.

    ...
            [RequiredValidator]
            public string TestProperty
            {
                get { return this.testProperty; }
                set { this.testProperty = value; }
            }
    ...
    

Stuff Left to Do

I feel the library is fairly complete. There are 25 unit tests utilizing the NUnit framework. (I am by no means a unit test writer, so don't laugh at them.) The one thing I do wish to do is incorporate another way of registering validators with the framework. I am pursuing doing that through the app/web config files. This would completely decouple business logic validation from the domain model.

In addition, I'm not sure if the library is thread-safe. I think it is, because the underlying dictionary objects are thread-safe, but I'm not an expert in this area and will defer to another's judgement.

Finally, I'm still debating whether or not to allow the removal of validators at run-time from types and/or instances. I have not required this functionality, but perhaps others may find it useful.

Interesting Properties

Something I realized later, after having worked on the framework for some time, is that there is an inversion of control property associated with the framework. For instance, we can pass a ValidationManager and instance of any object, register some validators, and assure ourselves of its validity. For example:

ArrayList list = new ArrayList();
ValidationManager vm = new ValidationManager(list);
vm.AddValidator(NotEmptyValidator.CreateValidator(typeof(ArrayList),"Count"));
Assert.IsFalse(vm.IsValid());
list.Add(2);
Assert.IsTrue(vm.IsValid());

Obviously, this is a trivial example, but the illustration is made.

Conclusion

We have created a formal validation framework that is extensible and easy to use. I hope you enjoyed the article, but hope that you enjoy using the library even more. Please keep me in the loop regarding code changes/enhancements/bug fixes that you make.

You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
GeneralValidationManager causes MemoryLeak
Zecic
5:36 2 Sep '09  
Hi!

At first thanks for your great works! You really saved me from a lot of work Wink

Unfortunately your ValidationManager caused a little memory leak in our application. I think the source of the problem is the static event "GlobalValidatorsChanged".

Let’s assume you have two classes extending the abstract class ValidatableBase (I think the same applies if you implement IValidatable since you will use ValidationManager, too):

ClassA : ValidatableBase
ClassB : ValidatableBase

Next I create two Lists (List<ClassA> and List<ClassB> ) and populate them with instances of my two classes. After that I call Clean() for List<A> sothat all instances of ClassA will be collected by GC.

Because we have a static event in ValidationManager this will not occure since intances of ClassB share the same event object and therfore the same handlers. Although instances of ClassA are not referenced by the List<ClassA> anymore, there is still a reference to the other ValidationManagers contained by ClassB instances.

To solve the problem, I used the Mediator implementation by Sacha Barbers MVVM Project found here:

WPF: If Heineken did MVVM Frameworks by Sacha Barbers
The Mediator uses WeakReferences to hold the handler references internally – object can be reclaimed by GC although a reference exists.

Best regards,
Dominik
GeneralReally great work
seesharper
11:26 15 Mar '07  
I think you have done a great job here.

I have just one suggestion. In order for the object to be validatable it has to derive from validationbase.

Properties like IsValid should have the Browsable(false) attribute so that it will be hidden in databinding scenarios.

I've just been playing around implementing the ITypedList interface in a ORM framework I'm developing.
It's common for implementors of this interface to display all properties, except those with Browsable(false)

It's not often you see such a great idea implemented in such a nice way. Code is well structured too.

This is gonna fit like a glove into my ORM framework.

Another thing that might improve performance, is the way you ask for the property value.

The line "this.propertyInfo.GetValue(instance, null);" has the potential to be called very often and while reflection is a great thing, it is not something that should be executed to often.

I used this code as a basis for my property setters/getters and it works like a charm.

http://codecube.net/item.asp?cc_itemid=307[^]

It uses emits the code needed to access properties.
Anyway keep up the good work.

"May the source be with you"
//seesharper




GeneralRe: Really great work
Craig G. Wilson
11:36 15 Mar '07  
Thanks for the comments. To address your first concern, the object does not have to inherit from ValidattionBase, it just can. This is there for short circuiting some common scenarios. I don't use data binding that often, so I didn't place the attribute on the object, but you're right, it should be there.

As I've mentioned before, I have rewritten this entire library and integrated it into a much larger framework we use in my department. It is configurable via XML and most of all, it caches all the reflection and uses DynamicMethods to speed up the "reflective" calls. I assume when you say you use emits, you are referring to DynamicMethod creation at runtime.

Anyways, thanks for your comments and let me know if I can aid you in your framework building.
GeneralRe: Really great work
seesharper
10:34 24 Apr '07  
Hi again Craig!

I've just starting looking at the sourcecode for you validation framework.

First of all I must say that your code is very organized and well structured.

There's one thing that bothers me, though.

ValidationBase implements the IDataErrorInfo interface.

I see that validation for the property is performed when the indexer is accessed.

My experience is that the datasource that is binded to the ErrorProvider, calls this indexer very often.

This may cause a performance problem as I see it.

Would it be a better solution to perform the actual validation in the property setters?







//seesharper
GeneralRe: Really great work
Craig G. Wilson
11:49 24 Apr '07  
That is always a solution, but think about the repercussions. Any property you want validated would need to fire a method that it has changed and subsequently validate that property. But, what if another property's validation scenario is based upon another property. How would we know we need to fire that validation scenario as well?

For instance, given a User class with two properties, Password and ConfirmPassword. Obviously, ConfirmPassword depends on Password. Let's say we change password and then check to see if everything is ok. Somehow, we would need to know that ConfirmPassword's validation scenario is tied to Password's value and that when Password changes, we need to rerun ConfirmPassword's validation scenario as well. This could all be hooked up manually, but using a framework like this makes it much more difficult to do out of the box.

Let me know if you have any ideas about this...

GeneralRe: Really great work
seesharper
0:27 25 Apr '07  
Hi again.

I do have an idea how this can be solved.

First of all another reason why validation should not be done in the IDataErrorInfo indexer.
If you choose to use the LenghtValidator that includes a minimum length parameter.
This is a problem if the ex password member variable is set to null by default.
This would cause the errorprovide to display its icon next to the control even before the user gets at chance to edit the data.



Moving the validation from the indexer proposes another challenge. Intercepting that the property setter has been called.

I do this by a using a property setter method, like SetPropertyValue(string propertyname,T value), implemented in the base class.

This method will in turn use the DynamicMethodCompiler to set the member variable.

This method (SetPropertyValue) would be the natural place to call the validation sequence.

In my ORM framework, this is how I fake/avoid proxying the objects.

Anyway, back to the password scenario.

As you mentioned there is a dependency between password and confirmedpassword.

I think one should implement a PropertyCompareValidator that takes the ComparisonOperator and the target propertyname as the parameters.

Then the validator could be installed both on the Password property and the target property (ConfirmedPassword).

There should also be possible to put this attribute on both propeties in case you want to have distinct error message for the properties.

In addition it should only perform validation if both the properties has been modified. This to avoid the validation to trigger when the user has entered the password and not yet the confirmed password.

When all this is done I think you would have a validation framework that takes care of 90% of the normal tidious validation logic.

Another think I'm currently working on is using the attributes when it comes to databinding.

Ex. If the MaxLength is set to 10 characters, the textbox in the UI should restrict the user so that the validation error never occours. The validation still has to reside in the businessobject because the value can also be changed from code.

Also when it comes to numeric values. I'm thinking of a numeric format attribute. If this could be set on the property you would be sure to have a consise edit and display format for this property in whatever context it may be used(grid, textbox, ..)








seesharper
GeneralRe: Really great work
Craig G. Wilson
3:19 25 Apr '07  
Your ideas sound great. What ORM framework do you use?

You mentioned SetPropertyValue... Are you then storing your properties in a field or an IDictionary or something? WPF stores properties in a dictionary somewhere and has GetValue and SetValue members.

You mentioned a DynamicMethodCompiler. I also have one those in my core libraries, but if you are creating one of those for every instance, that is a lot of overhead. I would hope you are doing it statically, once per type.

So, back to your ideas. I think if you were to begin to use dependencies between your properties and all that jazz, you are going to need to use some graphing theory in order to eliminate cyclic validation. It would also be a brand new way to store validation scenarios... Graphs with vectors and edges.

I would like to caution here though... At some point, we are getting beyond a simple, easy to use validation framework and into a full blown Rules Engine which is a completely different animal.
GeneralRe: Really great work
seesharper
3:39 25 Apr '07  
I use my own ORM since my needs for a particular project was rather extreme.

I'm planning to post an article here when I get it finished enoughSmile

I needed to load 120000 record/objects into memory in one operation. The object model is a complex tree structure that has a lot of parent/child relations.

While Nhibernate did it in 3 hours, I did it in 30 seconds.

The basics of my ORM is to reduce the need to access the datastore so often.

Yes, of cource, I do cache the dynamic methods per type/property so that the overhead is minimal.

You are absolutely right aboubt keeping things simple. But you know how tempting it is to just put one more thing in thereSmile

I'm gonna give it a try anyway. I will let you know if I get it to work.

I think I know a way to implement the PropertyCompareValidator without the need for graphs and so on.



//seesharper
GeneralRe: Really great work
Henry Nguyen
5:07 30 Jul '07  
First of all, big thanks to C. Wilson for a great article.

To seesharper, have you done your implementation?
If you have, could you post it here so I can get some insight on how to implement the ORM?

Thank you in advance.

Henry
GeneralGreat! Note on Error Messages
StevieGen
9:39 21 Feb '07  
I really like the library.

I did overload the constructor on some of the validators - thought you might consider it as well if you ever release this again:


ie.
public CompareValidatorAttribute(ComparisionOperator comparisionOperator, objct valueToCompare, string errorMessage)

This way, you can pass in your own error message.

Works great!

Thanks
Steve
GeneralRe: Great! Note on Error Messages
Craig G. Wilson
9:54 21 Feb '07  
Yeah, my rewrite exposes some of that better including localization resource keys.

Thanks for the comments.
QuestionCustomValidator with Strongly Typed Dataset
rgu123
11:35 7 Feb '07  
Hi Craig

First of all, thank you for your awesome work. Your framework is great!

I'm trying to implement it with a Strongly Typed Dataset and so far it is working very well.

As an example, the following code in my dataset partial class add a RequiredValidator to one DataColumn (EGR_A_NameEN) of a DataRow definition (EGR_EnumerationGroupRow) in my dataset:

Validator val = RequiredValidator.CreateValidator(this.EGR_EnumerationGroup.EGR_A_NameENColumn.ColumnName);
ValidationManager.AddGlobalValidator(val);

This work just fine.

The only problem I've got is with CustomValidator.

When I add this CustomValidator:

Validator val = CustomValidator.CreateValidator("Custom error", this.EGR_EnumerationGroup.EGR_A_NameENColumn.ColumnName, "MyMethod");
ValidationManager.AddGlobalValidator(val);

Followed by this method definition:

public bool MyMethod(string value)
{
if (value.ToString() == "test")
return false;
else return true;
}

I've got the following error when I try to validate one of those DataRow:

"Error binding to target method."

The exception occured at the following line in DoIsValid method of CustomValidator class:

Delegate d = Delegate.CreateDelegate(this.methodType,instance,this.methodName);
If we take a look in the dataset designer code for the corresponding property definition, we find this:

[System.Diagnostics.DebuggerNonUserCodeAttribute()]
public string EGR_A_NameEN {
get {
try {
return ((string)(this[this.tableEGR_EnumerationGroup.EGR_A_NameENColumn]));
}
catch (System.InvalidCastException e) {
throw new System.Data.StrongTypingException("The value for column \'EGR_A_NameEN\' in table \'EGR_EnumerationGroup\' is DBNull.", e);
}
}
set {
this[this.tableEGR_EnumerationGroup.EGR_A_NameENColumn] = value;
}
}

The property is a string as well as the parameter of MyMethod. I don't know what I'm doing wrong. Do you see something?

Thank you

Raoul Guenette
QC, Canada
AnswerRe: CustomValidator with Strongly Typed Dataset
Craig G. Wilson
11:47 7 Feb '07  
Thanks for your comments. It's been a little while since I looked at this and I don't use DataSets so I'll do my best. Be sure that the method you are attempting to use to validate exists inside the class that is being validated. The only other thing I can really think of is that it simply can't find the method, and I think that that is fairly obvious.

Why don't you stick a breakpoint on the CreateDelegate line and see the parameters that are being passed in. I think you'll find that one of them is a little awry.

Sorry I can't be of more assistance. Let me know if you find some more things out that may help narrow the problem down.




GeneralRe: CustomValidator with Strongly Typed Dataset
rgu123
12:14 7 Feb '07  

I found the problem (with your help)...

MyMethod was not accessible to the CreateDelegate because it was not in the same class. The wizard of VS create many classes for a strongly typed dataset and there is one for each DataRow definition. So the solution was to declare MyMethod in a partial class for that row. And it's now working.

public partial class EGR_EnumerationGroupRow
{
public bool MyMethod(System.String value)
{
if (value.ToString() == "test")
return false;
else return true;
}
}

Thank's again!

Raoul Guenette
QC, Canada
GeneralGood work! & localization question
bxb
2:57 23 Nov '06  
Good work! I was searching yesterday for an attribute based validation framework and finaly take one by bltoolkit[^]

Yours and his programmability fits better to me but have you any idea how to implement a resource based localization for errors messages?



Cordialement,
GeneralRe: Good work! & localization question
Craig G. Wilson
13:52 23 Nov '06  
Thanks. Yeah, you basically use resources for all your messages. I used resources for my default messages, but you would simply make some location specific ones and "change" the culture info. I've never really done it, but there are plenty of articles here on Code Project to help you out.

Bltoolkit has some good stuff from what I've seen in the past.


GeneralRe: Good work! & localization question
bxb
0:20 28 Nov '06  
Yes but for business specific messages (ex: "the rule must containt at least one constraint" ), localized messages cannot be done in attributes cause they accept only fixed string.

The work-around I found what to pass only a resource key to the attribute and localize messages just after the validation process.

So if somebody has a much elegant method with the attributes, I'll take it! ;o)
GeneralGood Work
Keith Barrett
12:24 17 Nov '06  
I had intended to implement something very like this myself, but you've beaten me to it. Well done.

I do have some ideas for further work though. The first relates to XML serialisation. In an XML schema you can specify constraints on element values, for example by specifying a pattern that the value must conform to. However, when you use the XSD utility to generate a C# wrapper class the restrictions are ignored. Your validation attributes would be a way for preserving the restrictions in the wrapper class.

There is a similar problem with wrapping relational tables. Any check constraints on the column values could be translated to validation attributes. Then we could validate property values in the business logic before attempting to update the database and getting a constraint violation error.

Finally it would be neat if the validation attributes could be used to automatically generate a Windows Form for editing an object. For each public property of the class we could generate a appropriate control, then use the validation attribute to set the properties of the control. For example, if we had an integer property with a range validation on it we could automaticaly create a spin control and set the upper and lower bound.

Anyone out there with some time on their hands?
GeneralRe: Good Work
Craig G. Wilson
12:41 17 Nov '06  
Some great ideas. I say run with it. I have refactored a lot of the code an added Xml configuration to the validators so that instead of attributes, it could be specified in an xml file (or the config file). With your line of thinking, given the Xml, you could write a build provider to generate the classes for you.

Keep up the good thoughts Smile
GeneralRe: Good Work
D Aguilar
23:21 14 May '07  
Hi Craig,

One more thank you for your great work!

Just wondering if that "refactored" version of your framework with Xml configuration is the one available for download in your article.
If not, would you consider making it available?

thanks a lot!

GeneralRe: Good Work
Craig G. Wilson
4:11 15 May '07  
It is not the one downloadable from here. The refactored version is a part of a much larger framework now with a dependency on a common library. Since I cannot release the core just yet, releasing the validation portion is rather useless... At some point though, the core will get open sourced.
GeneralRe: Good Work
Josh Smith
12:54 17 Nov '06  
Keith Barrett wrote:
Finally it would be neat if the validation attributes could be used to automatically generate a Windows Form for editing an object.

On a side note, my article about my Fluid Geometry[^] application does something like that. Maybe it is similar to what you have in mind.

:josh:
My WPF Blog[^]

QuestionLicense?
markeric
10:04 15 Nov '06  
I have downloaded and just started looking at your code. Thanks for making this available!

I didn't see anything about a license on use.

Is it free like a BSD license? Is it now public domain? I would like some clarification on this before I think seriously about incorporating it into any projects.

Thanks,
-Mark E.
AnswerRe: License?
Craig G. Wilson
11:17 15 Nov '06  
Let's go with public domain. Do anything you want with it. In a future revision, I might stick a license on it, but for this copy, use it how you like Smile .


GeneralRe: License?
markeric
12:01 15 Nov '06  
Thanks for that clarification. I look forward to seeing how my projects can benefit from it.

Do you have any plans to make a dedicated/hosted OpenSource project out of it? Just wondering as it could be useful to several projects (OpenSource and Commercial) that provide some sort of Object / Relation mapping.

Thanks again for the great article and your contribution.

-Mark E.


Last Updated 10 Nov 2006 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2010