programming4us
programming4us
ENTERPRISE

Microsoft Enterprise Library : Banishing Validation Complication - Diving in With Some Simple Examples (part 1)

- How To Install Windows Server 2012 On VirtualBox
- How To Bypass Torrent Connection Blocking By Your ISP
- How To Install Actual Facebook App On Kindle Fire
11/15/2013 6:53:28 PM

The application uses three versions of a class that stores product information. All of these implement an interface named IProduct, as illustrated in Figure 1. Each has a string property that is designed to be set to a value from an enumeration called ProductType that defines the valid set of product type names.

The product classes used in the examples

Figure 1. The product classes used in the examples

The Product class is used primarily with the example that demonstrates using a configured rule set, and contains no validation attributes. The AttributedProduct class contains Validation block attributes, while the AnnotatedProduct class contains .NET Data Annotation attributes. The latter two classes also contain self-validation routines—the extent depending on the capabilities of the type of validation attributes they contain.

The different ways that you can use the Validation block:

  • Validating Objects and Collections of Objects. This is the core topic for using the Validation block, and is likely to be the most common scenario in your applications. It shows how you can create type validators to validate instances of your custom classes, how you can dive deeper into the ValidationResults instance that is returned, how you can use the Object Validator, and how you can validate collections of objects.

  • Using Validation Attributes. This section describes how you can use attributes applied to your classes to enable validation of members of these classes. These attributes use the Validation block validators and the .NET Data Annotation attributes.

  • Creating and Using Individual Validators. This section shows how you can create and use the validators provided with the block to validate individual values and members of objects.

  • WCF Service Validation Integration . This section describes how you can use the block to validate parameters within a WCF service.

1. Validating Objects and Collections of Objects

The most common scenario when using the Validation block is to validate an instance of a class in your application. The Validation block uses the combination of rules defined in a rule set and validators added as attributes to test the values of members of the class, and the result of executing any self-validation methods within the class.

The Validation block makes it easy to validate entire objects (all or a subset of its members) using a specific type validator or by using the Object validator. You can also validate all of the objects in a collection using the Object Collection validator. We will look at the Object validator and the Object Collection validator later. For the moment, we'll concentrate on creating and using a specific type validator.

Creating a Type Validator using the ValidatorFactory

You can resolve a ValidatorFactory instance through the Enterprise Library container and use it to create a validator for a specific target type. This validator will validate objects using a rule set, and/or any attributes and self-validation methods the target object contains. To obtain an instance of the ValidatorFactory class, you can use the following code.

ValidatorFactory valFactory
= EnterpriseLibraryContainer.Current.GetInstance<ValidatorFactory>();

You can then create a validator for any type you want to validate. For example, this code creates a validator for the Product class and then validates an instance of that class named myProduct.

Validator<Product> pValidator = valFactory.CreateValidator<Product>();
ValidationResults valResults = pValidator.Validate(myProduct);

By default, the validator will use the default rule set defined for the target type (you can define multiple rule sets for a type, and specify one of these as the default for this type). If you want the validator to use a specific rule set, you specify this as the single parameter to the CreateValidator method, as shown here.

Validator<Product> productValidator
= valFactory.CreateValidator<Product>("RuleSetName");
ValidationResults valResults = productValidator.Validate(myProduct);

The example named Using a Validation Rule Set to Validate an Object creates an instance of the Product class that contains invalid values for all of the properties, and then uses the code shown above to create a type validator for this type and validate it. It then displays details of the validation errors contained in the returned ValidationResults instance. However, rather than using the simple technique of iterating over the ValidationResults instance displaying the top-level errors, it uses code to dive deeper into the results to show more information about each validation error, as you will see in the next section.

Delving Deeper into ValidationResults

You can check if validation succeeded, or if any validation error were detected, by examining the IsValid property of a ValidationResults instance and displaying details of any validation errors that occurred. However, when you simply iterate over a Validation Results instance , we displayed just the top-level errors. In many cases, this is all you will require. If the validation error occurs due to a validation failure in a composite (And or Or) validator, the error this approach will display is the message and details of the composite validator.

However, sometimes you may wish to delve deeper into the contents of a Validation Results instance to learn more about the errors that occurred. This is especially the case when you use nested validators inside a composite validator. The code we use in the example provides richer information about the errors. When you run the example, it displays the following results (we've removed some repeated content for clarity).

The following 6 validation errors were detected:
+ Target object: Product, Member: DateDue
- Detected by: OrCompositeValidator
- Tag value: Date Due
- Validated value was: '23/11/2010 13:45:41'
- Message: 'Date Due must be between today and six months time.'
+ Nested validators:
- Detected by: NotNullValidator
- Validated value was: '23/11/2010 13:45:41'
- Message: 'Value can be NULL or a date.'
- Detected by: RelativeDateTimeValidator
- Validated value was: '23/11/2010 13:45:41'
- Message: 'Value can be NULL or a date.'
+ Target object: Product, Member: Description
- Detected by: OrCompositeValidator
- Validated value was: '-'
- Message: 'Description can be NULL or a string value.'
+ Nested validators:
- Detected by: StringLengthValidator
- Validated value was: '-'
- Message: 'Description must be between 5 and 100 characters.'
- Detected by: NotNullValidator
- Validated value was: '-'
- Message: 'Value can be NULL.'
...
...
+ Target object: Product, Member: ProductType
- Detected by: EnumConversionValidator
- Tag value: Product Type
- Validated value was: 'FurryThings'
- Message: 'Product Type must be a value from the 'ProductType' enumeration.'

You can see that this shows the target object type and the name of the member of the target object that was being validated. It also shows the type of the validator that performed the operation, the Tag property values, and the validation error message. Notice also that the output includes the validation results from the validators nested within the two OrCompositeValidator validators. To achieve this, you must iterate recursively through the ValidationResults instance because it contains nested entries for the composite validators.

The code we used also contains a somewhat contrived feature: to be able to show the value being validated, some examples that use this routine include the validated value at the start of the message using the {0} token in the form: [{0}] validation error message. The example code parses the Message property to extract the value and the message when it detects that this message string contains such a value. It also encodes this value for display in case it contains malicious content.

While this may not represent a requirement in real-world application scenarios, it is useful here as it allows the example to display the invalid values that caused the validation errors and help you understand how each of the validators works. We haven't listed the code here, but you can examine it in the example application to see how it works, and adapt it to meet your own requirements. You'll find it in the ShowValidationResults, ShowValidatorDetails, and GetTypeNameOnly routines located in the region named Auxiliary routines at the end of the main program file.

Using the Object Validator

An alternative approach to validating objects is to programmatically create an Object Validator by calling its constructor. You specify the type that it will validate and, optionally, a rule set to use when performing validation. If you do not specify a rule set name, the validator will use the default rule set. When you call the Validate method of the Object validator, it creates a type-specific validator for the target type you specify, and you can use this to validate the object, as shown here.

Validator pValidator = new ObjectValidator(typeof(Product), "RuleSetName");
ValidationResults valResults = pValidator.Validate(myProduct);

Alternatively, you can call the default constructor of the Object validator. In this case, it will create a type-specific validator for the type of the target instance you pass to the Validate method. If you do not specify a rule set name in the constructor, the validation will use the default rule set defined for the type it is validating.

Validator pValidator = new ObjectValidator("RuleSetName");
ValidationResults valResults = pValidator.Validate(myProduct);

The validation will take into account any applicable rule sets, and any attributes and self-validation methods found within the target object.

Differences Between the Object Validator and the Factory-Created Type Validators

While the two approaches you've just seen to creating or obtaining a validator for an object achieve the same result, there are some differences in their behavior:

  • If you do not specify a target type when you create an Object Validator programmatically, you can use it to validate any type. When you call the Validate method, you specify the target instance, and the Object validator creates a type-specific validator for the type of the target instance. In contrast, the validator you obtain from a factory can only be used to validate instances of the type you specify when you obtain the validator. However, it can also be used to validate subclasses of the specified type, but it will use the rules defined for the specified target type.

  • The Object Validator will always use rules in configuration for the type of the target object, and attributes and self-validation methods within the target instance. In contrast, you can use a specific factory class type to obtain validators that only validate the target instance using one type of rule source (in other words, just configuration rule sets, or just one type of attributes).

  • The Object Validator will acquire a type-specific validator of the appropriate type each time you call the Validate method, even if you use the same instance of the Object validator every time. In contrast, a validator obtained from one of the factory classes does not need to do this, and will offer improved performance.

As you can see from the flexibility and performance advantages listed above, you should generally consider using the ValidatorFactory approach for creating validators to validate objects rather than creating individual Object Validator instances.

Validating Collections of Objects

Before we leave the topic of validation of objects, it is worth looking at how you can validate collections of objects. The Object Collection validator can be used to check that every object in a collection is of the specified type, and to perform validation on every member of the collection. You can apply the Object Collection validator to a property of a class that is a collection of objects using a Validation block attribute if you wish, as shown in this example that ensures that the ProductList property is a collection of Product instances, and that every instance in the collection contains valid values.

[ObjectCollectionValidator(typeof(Product))]
public Product[] ProductList { get; }

You can also create an Object Collection validator programmatically, and use it to validate a collection held in a variable. The example named Validating a Collection of Objects demonstrates this approach. It creates a List named productList that contains two instances of the Product class, one of which contains all valid values, and one that contains invalid values for some of its properties. Next, the code creates an Object Collection validator for the Product type and then calls the Validate method.

// Create an Object Collection Validator for the collection type.
Validator collValidator
= new ObjectCollectionValidator(typeof(Product));
// Validate all of the objects in the collection.
ValidationResults results = collValidator.Validate(productList);

Finally, the code displays the validation errors using the same routine as in earlier examples. As the invalid Product instance contains the same values as the previous example, the result is the same. You can run the example and view the code to verify that this is the case.

Other  
  •  Microsoft Enterprise Library : Banishing Validation Complication - How Do I Use The Validation Block?
  •  Microsoft Enterprise Library : Banishing Validation Complication - What Does the Validation Block Do? (part 2)
  •  Microsoft Enterprise Library : Banishing Validation Complication - What Does the Validation Block Do? (part 1)
  •  Microsoft Enterprise Library : A Cache Advance for Your Applications - How Do I Use the Caching Block (part 4) - Refreshing the Cache, Loading the Cache
  •  Microsoft Enterprise Library : A Cache Advance for Your Applications - How Do I Use the Caching Block (part 3) - Removing Items from and Flushing the Cache
  •  Microsoft Enterprise Library : A Cache Advance for Your Applications - How Do I Use the Caching Block (part 2)
  •  Microsoft Enterprise Library : A Cache Advance for Your Applications - How Do I Use the Caching Block (part 1) - Adding Items to and Retrieving Items from the Cache
  •  Microsoft Enterprise Library : A Cache Advance for Your Applications - How Do I Configure the Caching Block?
  •  Microsoft Visual Studio 2010 : Data Parallelism - Unrolling Sequential Loops into Parallel Tasks (part 4) - Handling Exceptions
  •  Microsoft Visual Studio 2010 : Data Parallelism - Unrolling Sequential Loops into Parallel Tasks (part 3) - Interrupting a Loop
  •  
    Top 10
    - Microsoft Visio 2013 : Adding Structure to Your Diagrams - Finding containers and lists in Visio (part 2) - Wireframes,Legends
    - Microsoft Visio 2013 : Adding Structure to Your Diagrams - Finding containers and lists in Visio (part 1) - Swimlanes
    - Microsoft Visio 2013 : Adding Structure to Your Diagrams - Formatting and sizing lists
    - Microsoft Visio 2013 : Adding Structure to Your Diagrams - Adding shapes to lists
    - Microsoft Visio 2013 : Adding Structure to Your Diagrams - Sizing containers
    - Microsoft Access 2010 : Control Properties and Why to Use Them (part 3) - The Other Properties of a Control
    - Microsoft Access 2010 : Control Properties and Why to Use Them (part 2) - The Data Properties of a Control
    - Microsoft Access 2010 : Control Properties and Why to Use Them (part 1) - The Format Properties of a Control
    - Microsoft Access 2010 : Form Properties and Why Should You Use Them - Working with the Properties Window
    - Microsoft Visio 2013 : Using the Organization Chart Wizard with new data
    REVIEW
    - First look: Apple Watch

    - 3 Tips for Maintaining Your Cell Phone Battery (part 1)

    - 3 Tips for Maintaining Your Cell Phone Battery (part 2)
    programming4us programming4us
    programming4us
     
     
    programming4us