Parts 1 and 2 added structure and data, but this is often not enough, one needs valid data—the domain of the data for your configuration being narrower than the domain of the underlying type of the parsed values. Hence the need to extend the custom types representing the structure with more types and attributes to limit the ranges of those values.
Checking single values is the easiest part, albeit needing two custom types to be defined for each distinct kind of validation, but at least each case can be parameterised (i.e. if you need to check a date range, you only need two types, not four).
First: The Validator
The first type is derived from
This will actually do the validation in two steps:
CanValidateto confirm that the type can be validated. Return
trueto indicate you can, otherwise validation will fail. This override is usually used to confirm the type of the value matches the expectations of the next method.
Validateto perform the validation. To indicate failure throw an exception (which will be wrapped by the configuration runtime, into a
NB. the validation needs to handle the default (before the configuration
content is parsed) value. E.g. a
DateTime value will be initially
validated with a value of
DateTime.MinValue before being called
again with the value read from the configuration file. (For types which can
be used with code attributes—see §17.1.3 of the C# specification,
the default value is an optional parameter for the
attribute. For other types, the code attribute based declaration of configuration
values can be replaced with a more programmatic one, which I should cover later
in this series.)
For instance to check that a
DateTime value has a minimum year the
following validation code will work (note the check to allow
Second: The Attribute
The second type is derived from
(which itself derives from
System.Attribute). This is used to
(1) annotate the configuration property in the
ConfigurationSection) type, and (2) be an object factory for the first,
validation, type. Additional parameters can be passed from this attribute to
The key override is
ValidatorInstance which needs to return an
initialised instance of the validator class.
For example, to support the
DateTimeValidator type, the following:
The only change is to annotate the configuration property with the just defined attribute, passing any necessary additional parameters, e.g.:
Note the use of an explicit lower limit for
StartDate but not for
DateTime is not a type with literals
available for attribute parameters, hence just using the year here.)
What Happens on Validation Failure?
Validation is performed when the configuration section is read, i.e. when
called. Importantly this means that if configuration sections are not used,
validation will not be performed, and no errors will be reported, however many
invalid, missing or extra values there are. Also note that extra attributes
and elements will be reported as errors, this behaviour can be modified by
When an error, validation or otherwise (including malformed XML) occurs a
ConfigurationErrorsException is thrown. If this is based
on another exception being thrown internally (e.g. on malformed XML, a
XmlException) then that original exception may be the
(sometimes this seems to be the base, other times not—I don’t see
Any reporting of this to the user or logging is up to the application, this is not trivial as while the text of the message does include key information (like where the error was in the configuration file) it is not exactly in a user friendly format. But then configuration files are not targeted at (typical) end user direct editing.
Checking Values Together
There is no direct support for further validation after
each individual value has been loaded and (given suitable attributes)
ConfigurationElement does have the
PostDeserialize method, which is called at the right time.
But the error message is not ideal, starting with the text: “An error occurred creating the configuration section handler for ‹section-name›”. But it does work.
See the code for AppConfigSection.cs for an example