Introduction
I've talked about custom configuration sections for a while now; now we are going to focus on using them with custom providers. When developing a custom provider, you often need a provision for a <providers> element, where you can specify the provider you want to use for a particular operation. For instance, the <membership> element has a providers collection, and a provision to specify the default provider that does the work, which is used by the Membership class. But how do you then do this in your configuration section? We will look at this next.
Provider Collections
The .NET framework providers (such as MembershipProvider) all define custom configuration sections to specify the properties to use with the provider. For instance, the membership element specifies the connectionStringName to use and all of the rules and regulations that go with membership (how long the passwords are, how strong they need to be, how many times a user can login, etc). When developing custom providers, you need to have these configuration settings for this purpose. How do you set this up with a custom provider?
We will first look at the collections used to expose providers. I created a generic base class that can be used; you provide the type of provider to it, as in the code below:
public class ProviderCollection<T> : ProviderCollection where T : ProviderBase
{
#region " Properties "
new public T this[string name]
{
get { return (T)base[name]; }
}
#endregion
#region " Methods "
public override void
Add(System.Configuration.Provider.ProviderBase provider)
{
if (provider == null)
throw new ArgumentNullException("provider", "Provider has not been provided");
if (!(provider is T))
throw new ArgumentException("The
provider type is invalid", "provider");
base.Add(provider);
}
public void CopyTo(T[] array, int index)
{
base.CopyTo(array, index);
}
#endregion
}
This makes it simpler to create the actual strongly-typed provider collection. For this article, I changed the way rendering article works; I have an article provider that will be used to retrieve the articles. The custom collection I used to expose this is this class:
public class ArticleProviderCollection :
ProviderCollection<ArticleProvider> { }
Because I used the generic class, I don't need to define anything; it makes defining a provider collection a lot easier, because it will all be defined for us. This collection is exposed through the configuration section, which I defined two extra properties. Below is a portion of the new configuration section:
[
ConfigurationProperty("defaultProvider", DefaultValue = "AspNetSqlArticleProvider"),
StringValidator(MinLength = 1)
]
public string DefaultProvider
{
get { return (string)this["defaultProvider"]; }
set { this["defaultProvider"] = value; }
}
[ConfigurationProperty("providers")]
public ProviderSettingsCollection Providers
{
get { return (ProviderSettingsCollection)this["providers"]; }
}
The providers collection exposes a different provider; it returns a ProviderSettingsCollection collection, which I will show you later how that gets converted to our custom provider collection. This is important though, because this handles the representation in the configuration file for us. We also specify a default provider that is required, which allows us to provide the name of a provider in the collection that will be the one used by default.
Provider Classes
We next need to look at how providers work. There are two classes; one is a base ArticleProvider class, which exposes all of the methods needed for the provider (all detailed provider classes inherit from this). There is also a static class, which exposes all of these methods, plus additional overloaded methods that may be useful. I'm only going to touch the necessities to understand how it works. The abstract base class looks like this:
public abstract class ArticleProvider :
ProviderBase
{
private string _applicationName;
public string ApplicationName
{
get { return _applicationName; }
set { _applicationName = value; }
}
public abstract Article[] GetArticles();
}
The GetArticles method is currently the only method defined. Any derived classes inherit from this to provide specific functionality (I have a SqlArticleProvider class defined, but all it does is return a dummy record; in actuality, it would connect to a data store to get the articles). The static class does several things. First, it converts the provider sections over to the actual implementations in our ArticleProviderCollection. Second, it exposes our provider methods statically, so they are accessible throughout. Let's take a look at it; I have a StaticArticles class that is a static class that exposes the ArticleProvider methods. The following is the GetArticles method declaration:
public static Article[] GetArticles()
{
return StaticArticles.DefaultProvider.GetArticles();
}
It uses the default provider property to return the GetArticles array. But where is that defined? Statically, in this class as shown below:
public static ArticleProvider DefaultProvider
{
get
{
StaticArticles.Initialize();
return StaticArticles._defaultProvider;
}
}
The conversion from ProviderSettingsCollection to ArticleProviderCollection is shown below. It uses a helper method to perform the conversion, which makes it easy to do. InitializeProvider is called from the Initialize method shown above, which both are my methods defined in the static class:
private static void InitializeProviders()
{
AuthorSection section = AuthorSection.Current;
//Make sure that there is a custom section, and that the providers exist; if not setup, throw an
error
if (section == null || string.IsNullOrEmpty(section.DefaultProvider) || section.Providers.Count == 0)
throw new ProviderException("The author section
hasn't been setup correctly");
//Instantiate the providers collection to store the collection with
_providers = new ArticleProviderCollection();
//Instantiate the providers collection using the helper method defined in the framework
ProvidersHelper.InstantiateProviders(section.Providers, _providers, typeof(ArticleProvider));
//Get the default provider to use
_defaultProvider = _providers[section.DefaultProvider];
//If the default provider is null, throw an error
if (_defaultProvider == null)
throw new ProviderException("The default
provider couldn't be instantiated");
}
Summary
This is how you defined custom configuration sections for custom providers, from the provider section of it. There are more details when implementing a custom provider; however, the focus of this was to look at the provider end. It's actually pretty easy to implement this in your application.
References
See this MSDN Custom Provider article for more information.
Other Articles
Custom Configuration Collections
Configuration Section Validators
2.0 Custom Configuration Sections
About Brian Mains
 |
Brian Mains is an application developer consultant with Computer Aid Inc. He formerly worked with the Department of Public Welfare.
In both places of business, he developed both windows and web applications, small and large, using the latest .NET technologies. In addition, he had spent many hou...
View complete profile
|
Top Articles in this category
Custom Configuration Collections
Custom configuration sections support the use of collections. These collections are similar to the add, remove, and clear sections that you see defined for connectionStrings, appSettings, or other sections.
2.0 Custom Configuration Sections
.NET 2.0 produced a new way to create custom configuration sections, one that is even easier than before. It involves a custom class that inherits from the ASP.NET ConfigurationSection class, defining usable properties that map to the properties for the configuration element.
Configuration Section Validators
.NET 2.0 has a new means to create custom configuration sections. It also provides a means to validate the content within it, using a custom validator inheriting from ConfigurationValidatorBase. In addition, the validator links to the property in the configuration section through an attribute, which you will see in this article.
|
|
Please login to rate or to leave a comment.