Damon Armstrong

Caffeine Induced Tirades about .NET and Life
And don't forget to check out my latest Simple-Talk articles
Add to Technorati Favorites      Add to Google     

Accessing the Web.Config at Design Time for ASP.NET 2.0 Controls

Published Friday, October 20, 2006 3:20 AM

One of the things that has always bothered me about the ASP.NET validation controls is that it sometimes takes two or three of them to validate a particular field. Say, for example, that you have a password field you want to validate. Normally it's required, has a minimum and maximum length, and needs to be compared against a confirmation field. It takes about three controls to make that happen:

<asp:RequiredFieldValidator runat="server"
   
ID="reqPassword" ControlToValidate="txtPassword" Text="*"
   
ErrorMessage="Password is required" />
<asp:RegularExpressionValidator runat="server"
   
ID="regexPassword" ControlToValidate="txtPassword" Text="*"
    ErrorMessage
="Password length must be at least 6 characters" />
<asp:CompareValidator runat="server" ID="comparePassword"
    ControlToValidate
="txtPassword"
    ControlToCompare
="txtPasswordConfirm" Text="*"
    ErrorMessage
="Confirm password" />

I’m currently in the process of building my own validation control that allows you to put one control on the page, then add as many “Validation Rules” to the control as you wish. So my validation control looks something more like this:

<rd:ValidationRuleSet runat="server" id="vrs1"   
  ControlToValidate
="TextBox1" >
     <
rd:RequiredRule Text="*" Message="Password is Required" />
    
<rd:RegexRule Text="*" Regex=".{6,}"
       
Message="Password length must be at least 6 characters" />
    
<rd:CompareRule Text="*" DataType="String"
       
Message="Confirm Password" 
CompareOperator="Equal"
        ControlToCompare
="txtPasswordConfirm" />
</rd:ValidationRuleSet>

This approach also makes it easy to programmatically add rules to the control without having to worry about dynamically adding controls to the page, which is important because the end goal is to make it easy to store validation rules and error messages in a database where they can be easily maintained. But I digress…

I’ve got the basic validation rules down: required, compare, regular expression, data range, etc. But I also wanted to give developers the ability to create custom rules for their projects by extending a base validation rule class (and then write a lot of JavaScript for the Client-Side support). Offering design-time support for a collection of items (in this case rules) is fairly easy. All you have to do is create a class that inherits from System.ComponentModel.Design.CollectionEditor and override the Type[] CreateNewItemTypes() method. From the CreateNewItemTypes() method, you just return an array of the various types you want to allow developers to add to your collection through the collection editor. The issue that I was running into was how to offer design time support for a custom rule? Especially since custom rules would probably only be used on a project-by-project basis.

I decided the best thing to do is allow developers to define their custom rules in the web.config, allowing for rule definitions on a project-by-project basis. Which leads me to the issue of how do you access the web.config at design time? At run time it’s fairly easy to find the web.config because you have a lot of nice methods for locating files using the Server object or the Page.ResolveUrl method. Design-time is a bit less obvious. And I wasn’t (and am still not) very familiar with design-time code. So I set out to find a way to do it.

Google did its job and landed me at Josh Flanagan’s blog entry titled Accessing web.config at Design Time in .NET 2.0. It’s got all the code you need if you want to access the web.config from a System.Web.UI.Design.ControlDesigner (Thanks Josh!). Unfortunately, I was needing a way to access it from a CollectionEditor. But the examples proved very useful, so after digging around the object available to me using Intellisense, I came up with the following solution inside the CreateNewItemTypes method of a class that derives from CollectionEditor:

protected override Type[] CreateNewItemTypes()
{
    ValidationConfiguration validationConfig = null;

    if (Context != null && Context.Container != null &&
      Context.Container.Components != null
)
   
{
        IWebApplication webApp = null;
        int componentIndex = 0;

        while (webApp == null && componentIndex <
          Context.Container.Components.Count)
       
{
           
webApp = Context.Container.Components
              [componentIndex].Site.GetService
              (
typeof(IWebApplication)) as IWebApplication;

           
componentIndex++;
       
}

       
if (webApp != null)
       
{
           
IProjectItem item = webApp.GetProjectItemFromUrl
              ("~/web.config"
);

            if
(item != null
)
           
{
               
System.Xml.XmlDocument doc = 
                  new System.Xml.XmlDocument
();

                
doc.Load(item.PhysicalPath);

                ValidationConfigurationSectionHandler handler = 
                 
new ValidationConfigurationSectionHandler();

                validationConfig = handler.Create(null, null
                    doc.SelectSingleNode
                      ("configuration/rebelDeveloper/validation")) 
                        as ValidationConfiguration
;
            

        
}
    
}

    if (validationConfig == null)
    
{
        
validationConfig = new ValidationConfiguration();
    
}

    
validationConfig.AddPrimaryType(typeof(MinLengthRule), 
        typeof(MaxLengthRule), typeof(RegexRule), typeof(CompareRule),
        typeof(RangeRule), typeof(RequiredRule
));

    
return validationConfig.CreateSupportedTypeArray();

}

The general idea is that you are looking for an IWebApplication service, and you get it by calling the Site.GetService method on an IComponent object. In Josh’s examples, there is a property called Component in the ControlDesigner class that offers the method. In the CollectionEditor class you have to dig a bit deeper, and you ultimately get a collection of components. Since I don’t really know much about design time stuff, I figured I would just loop through all of them looking for a IWebApplication service (though it does seem to always come back from the first component in the collection. I figure that at design time, it’s okay to be a bit inefficient and redundant.

So now I’ve got configurable design-time rules.

by Damon
Filed Under:

Comments

 

pfarrell said:

how bout a follow up to this article Damon?  I've had a similar idea for a personal web application I want to try to make money off of.  I also want to include access to other rules output as variables.  

I think this is a pretty complex task you're undertaking.  By complex,  I mean interesting.

Have you thought about how you're going to handle text parsing for the rules?  Are you going to use prebuilt library or roll your own?
February 28, 2007 3:29 PM
 

Damon said:

As is, the idea is to have an Interface that defines what  "rule" is, and then to have different types of rules and initialization strings for each rule.  For example, a "MaxLength" rule may only need only "12" as its initialization string, whereas something more complicated may require a comma-delimited list of configuration values or even XML.  Each type would then be responsible for handling it's initialization string on an internal basis, e.g., handle the parsing however it deems necessary as to not lock in a rigid parsing scheme that may not be flexible enough for future rules.  
This gives the ability to store rules in a database, to roll your own library of rules, and to allow for the creation of additional rules for individual projects or situations.  Should be fun.  
March 1, 2007 9:17 AM
You need to sign in to comment on this blog

















<October 2006>
SuMoTuWeThFrSa
24252627282930
1234567
891011121314
15161718192021
22232425262728
2930311234
A SysAdmin's Guide to Change Management
 In the first in a series of monthly articles, ‘Confessions of a Sys Admin’, Matt describes the issues... Read more...

Exchange: Recovery Storage Groups
 It can happen at any time: You get a request, as Admin, from your company, to provide the contents of... Read more...

Build Your Own Virtualized Test Lab
 Desmon explains the fundamentals of building a test lab for Windows servers and Enterprise applications... Read more...

Rendering Hierarchical Data with the Treeview
 It sometimes happens that Web Server controls that visualize data don't quite fit with the way that... Read more...

SQL Server 2008: Performance Data Collector
 With Performance Data Collector in SQL Server 2008, you can now store performance data from a number of... Read more...