One of the things I noticed when I first started digging around in ASP.NET 2.0’s provider framework was that there were a number of areas that didn’t do a very good job at reusing code. The one that stood out the most was the Initialize method on several of the provider managers (this is what I call the classes that initialize and provide static methods for accessing providers, e.g. the ProfileManager, Roles, and Membership classes). Load up Reflector and take a look at Roles.Initialize() and Membership.Initialize() (beta 2 or earlier), and you’ll see what I mean—they’re almost identical, and in my book, that’s a problem.
Below is one solution to this problem that utilizes generics. The responsibility for initializing providers has been taken away from the provider managers, and given to a class aptly named ProviderInitializer. This class takes care of the gory details of initializing providers, e.g. thread locking, careful exception handling, etc. I’ve used it in several custom providers and have found it to meet my needs—but it can be easily made more flexible by extracting blocks of code from the Initialize method into their own virtual methods.
using System;
using System.Collections.Generic;
using System.Text;
using System.Configuration.Provider;
using Johnson.Common.Configuration;
using System.Configuration;
using System.Web.Configuration;
namespace Johnson.Common.Providers;
{
/// <summary>
/// Provides initializtion logic for provider manager classes
/// </summary>
/// <typeparam name=”ProviderType”>The Type of the provider to initialize.</typeparam>
/// <typeparam name=”ProviderCollectionType”>The provider’s corresponding collection
/// type.</typeparam>
/// <typeparam name=”ConfigSectionType”>The Type of the config section for reading
/// provider settings.</typeparam>
public class ProviderInitializer<ProviderType, ProviderCollectionType, ConfigSectionType>
where ProviderType : ProviderBase
where ProviderCollectionType : SafeProviderCollection<ProviderType>, new()
where ConfigSectionType : ProviderConfigurationSection
{
private bool _initialized;
private Exception _initializeException;
private object _lock;
private string _configSectionName;
/// <summary>
/// Ctor
/// </summary>
public ProviderInitializer(string configSectionName)
{
_configSectionName = configSectionName;
_initialized = false;
_lock = new object();
}
/// <summary>
/// Initializes the provider.
/// </summary>
/// <param name=”provider”>The default provider.</param>
/// <param name=”providers”>The provider collection.</param>
public void Initialize(ref ProviderType provider, ref SafeProviderCollection<ProviderType> providers)
{
// check if initialize was already attempted and failed - if so, no reason to attempt
// initializing again
if (_initialized)
{
if (_initializeException != null)
{
throw _initializeException;
}
}
else
{
lock (_lock)
{
try
{
// check again to make sure another thread hasn’t initialized provider
// since lock was acquired
if (_initialized)
{
if (_initializeException != null)
{
throw _initializeException;
}
return;
}
// Get provider section from config file
ConfigSectionType config = GetProviderConfigurationSection();
// GetProviderConfigurationSection() could have set _initializeException, check if null
if (_initializeException == null)
{
if (string.IsNullOrEmpty(config.DefaultProvider))
{
_initializeException = new ProviderException(“Default provider not specified.”);
}
else
{
providers = new SafeProviderCollection<ProviderType>();
ProvidersHelper.InstantiateProviders(config.Providers, providers, typeof(ProviderType));
providers.SetReadOnly();
// set default provider
if (providers[config.DefaultProvider] == null)
{
_initializeException = new ProviderException(“Default provider not specified.”);
}
else
{
// this is a safe cast, as each provider added to the providers collection has
// already been checked to ensure it derives from the appropriate Provider Type.
provider = (ProviderType)providers[config.DefaultProvider];
}
}
}
}
catch (Exception ex)
{
_initializeException = ex;
}
_initialized = true;
if (_initializeException != null)
{
throw _initializeException;
}
}
}
}
/// <summary>
/// Gets the config section for the provider.
/// </summary>
/// <returns></returns>
protected virtual ConfigSectionType GetProviderConfigurationSection()
{
if (string.IsNullOrEmpty(_configSectionName))
{
_initializeException = new ProviderException(“configSectionName cannot be null or empty”);
return null;
}
ConfigSectionType config = ConfigurationManager.GetSection(_configSectionName) as ConfigSectionType;
if (config == null)
{
_initializeException = new ProviderException(“Unable to load provider configuration settings.”);
}
return config;
}
}
}