优秀代码学习之configSections

Microsoft.AppFabricCAT.Samples.Azure.TransientFaultHandling库是针对Windows Azure下部分服务,由于网络瞬断导致应用出错而推出的一套重试解决方案,这些服务包括SQL Azure/ServiceBus/AzureStorage等;本篇主要是学习该类库下的配置方式,其核心功能代码暂不涉及。

下面是Demo中给出的配置示例,configSections节点集定义了一个节点,该节点自身有属性,还有一组子节点,看起来应该是一个数组。当然这不是最完整的,知道该库的实现方式以及支持哪些功能,还得从代码中入手。

<configuration>
  <configSections>
    <section name="RetryPolicyConfiguration" type="SubjectConfiguration.Configuration.RetryPolicyConfigurationSettings, SubjectConfiguration" />
  </configSections>
  <RetryPolicyConfiguration defaultPolicy="FixedIntervalDefault" defaultSqlConnectionPolicy="FixedIntervalDefault" defaultSqlCommandPolicy="FixedIntervalDefault" defaultStoragePolicy="IncrementalIntervalDefault" defaultCommunicationPolicy="IncrementalIntervalDefault">
    <add name="FixedIntervalDefault" maxRetryCount="3" retryInterval="3000"/>
    <add name="IncrementalIntervalDefault" maxRetryCount="3" retryInterval="100" retryIncrement="50" />
    <add name="ExponentialIntervalDefault" maxRetryCount="3" minBackoff="100" maxBackoff="1000" deltaBackoff="100" />
  </RetryPolicyConfiguration>
</configuration>

RetryPolicyConfigurationSettings类是整个配置的入口,类的最上面部分定义了一些常量,包括私有和公有的。私有常量DefaultXXXX的值,与App.Config中的RetryPolicyConfiguration节点属性一一对应。公有常量SectionName则是自身的名称。

class RetryPolicyConfigurationSettings
{
    #region Private members
    private const string DefaultPolicyProperty = "defaultPolicy";
    private const string DefaultSqlConnectionPolicyProperty = "defaultSqlConnectionPolicy";
    private const string DefaultSqlCommandPolicyProperty = "defaultSqlCommandPolicy";
    private const string DefaultStoragePolicyProperty = "defaultStoragePolicy";
    private const string DefaultCommunicationPolicyProperty = "defaultCommunicationPolicy";
    #endregion

    #region Public members
    /// <summary>
    /// The name of the configuration section represented by this type.
    /// </summary>
    public const string SectionName = "RetryPolicyConfiguration";
    #endregion
}

紧接着是一批public属性,这些属性借助自定义Attribute:ConfigurationProperty,实现从配置文件到对象的映射,并且提供了IsRequired检查;属性Policies对应的是一个ConfigurationCollection,集合的类型是RetryPolicyCollection,而集合内的每一个元素又是一个个RetryPolicyInfo对象。也就是说,想要在App.Config(Web.config)中配置一个强类型的数组,需要创建两个类支持。

class RetryPolicyConfigurationSettings
{
    #region Public properties
    /// <summary>
    /// Gets or sets the name of the default general-purpose retry policy.
    /// </summary>
    [ConfigurationProperty(DefaultPolicyProperty, IsRequired = true)]
    public string DefaultPolicy
    {
        get { return (string)base[DefaultPolicyProperty]; }
        set { base[DefaultPolicyProperty] = value; }
    }

    /// <summary>
    /// Gets or sets the name of a retry policy dedicated to handling transient conditions with SQL connections.
    /// </summary>
    [ConfigurationProperty(DefaultSqlConnectionPolicyProperty, IsRequired = false)]
    public string DefaultSqlConnectionPolicy
    {
        get { return (string)base[DefaultSqlConnectionPolicyProperty]; }
        set { base[DefaultSqlConnectionPolicyProperty] = value; }
    }

    /// <summary>
    /// Gets or sets the name of a retry policy dedicated to handling transient conditions with SQL commands.
    /// </summary>
    [ConfigurationProperty(DefaultSqlCommandPolicyProperty, IsRequired = false)]
    public string DefaultSqlCommandPolicy
    {
        get { return (string)base[DefaultSqlCommandPolicyProperty]; }
        set { base[DefaultSqlCommandPolicyProperty] = value; }
    }

    /// <summary>
    /// Gets or sets the name of a retry policy dedicated to handling transient conditions in Windows Azure storage services.
    /// </summary>
    [ConfigurationProperty(DefaultStoragePolicyProperty, IsRequired = false)]
    public string DefaultStoragePolicy
    {
        get { return (string)base[DefaultStoragePolicyProperty]; }
        set { base[DefaultStoragePolicyProperty] = value; }
    }

    /// <summary>
    /// Gets or sets the name of a retry policy dedicated to handling transient conditions in the WCF communication infrastructure.
    /// </summary>
    [ConfigurationProperty(DefaultCommunicationPolicyProperty, IsRequired = false)]
    public string DefaultCommunicationPolicy
    {
        get { return (string)base[DefaultCommunicationPolicyProperty]; }
        set { base[DefaultCommunicationPolicyProperty] = value; }
    }

    /// <summary>
    /// Returns a collection of retry policy definitions represented by the <see cref="RetryPolicyInfo"/> object instances.
    /// </summary>
    [ConfigurationProperty("", Options = ConfigurationPropertyOptions.IsDefaultCollection)]
    [ConfigurationCollection(typeof(RetryPolicyInfo))]
    public RetryPolicyCollection Policies
    {
        get { return (RetryPolicyCollection)base[String.Empty]; }
    }
    #endregion
}

最后是一个公开的方法,根据policyName在Policies中找到匹配的RetryPolicyInfo,然后通过RetryPolicyInfo创建一个RetryPolicy<T>;如果找不到匹配项,则返回Null。

class RetryPolicyConfigurationSettings
{
    /// <summary>
    /// Returns an instance of the <see cref="RetryPolicy"/> object initialized for a given policy name.
    /// </summary>
    /// <typeparam name="T">The type implementing the <see cref="ITransientErrorDetectionStrategy"/> interface which is responsible for detecting transient conditions.</typeparam>
    /// <param name="policyName">The name under which a retry policy definition is registered in the application configuration.</param>
    /// <returns>The retry policy initialized from the specified policy definition, or a null reference if no such policy definition was found.</returns>
    public RetryPolicy<T> GetRetryPolicy<T>(string policyName) where T : ITransientErrorDetectionStrategy, new()
    {
        RetryPolicy<T> retryPolicy = null;

        if (!String.IsNullOrEmpty(policyName) && Policies.Contains(policyName))
        {
            RetryPolicyInfo retryPolicyInfo = Policies.Get(policyName);
            return retryPolicyInfo != null ? retryPolicyInfo.CreatePolicy<T>() : null;
        }

        return retryPolicy;
    }
}

这一系列配置如何在内存中缓存起来呢?常规的做法一般都是声明一个单例类。ApplicationConfiguration中的私有变量currentConfiguration是自己唯一的一个实例,public static属性Current利用double check实现了单例。正如类注释所说,提供强类型配置节点的缓存访问,适用于同类场景,代码能够高度重用。

namespace SubjectConfiguration.Configuration
{
    #region Using references
    using System;
    using System.Collections.Generic;
    using System.Configuration;
    #endregion

    /// <summary>
    /// Helper class that exposes all strongly-typed configuration sections and also provides an ability to save the
    /// configuration changes for custom sections.
    /// </summary>
    public sealed class ApplicationConfiguration
    {
        #region Private members
        /// <summary>
        /// A pre-initialized instance of the current configuration.
        /// </summary>
        private static volatile ApplicationConfiguration currentConfiguration;

        /// <summary>
        /// A lock object.
        /// </summary>
        private static readonly object initLock = new object();

        /// <summary>
        /// A dictionary object containing cached instances of the configuration sections.
        /// </summary>
        private readonly IDictionary<string, ConfigurationSection> configSectionCache;

        /// <summary>
        /// A lock object for the configuration section cache.
        /// </summary>
        private readonly object configSectionCacheLock = new object();
        #endregion

        #region Constructors
        /// <summary>
        /// Initializes a new instance of the ApplicationConfiguration class using default configuration source.
        /// </summary>
        private ApplicationConfiguration()
        {
            this.configSectionCache = new Dictionary<string, ConfigurationSection>(16);
        }
        #endregion

        #region Public properties
        /// <summary>
        /// Returns an instance of the ApplicationConfiguration class by enforcing a singleton design pattern with a lazy initialization.
        /// </summary>
        public static ApplicationConfiguration Current
        {
            get
            {
                if (null == currentConfiguration)
                {
                    lock (initLock)
                    {
                        // If we were the second process attempting to initialize the currentConfiguration member, when we enter
                        // this critical section let's check once again if the member was not already initialized by the another
                        // process. As the lock will ensure a serialized execution, we need to make sure that we don't attempt
                        // to re-initialize the ready-to-go instance.
                        if (null == currentConfiguration)
                        {
                            currentConfiguration = new ApplicationConfiguration();
                        }
                    }
                }

                return currentConfiguration;
            }
        }
        #endregion

        #region Public methods
        /// <summary>
        /// Returns a configuration section defined by the given type <typeparamref name="T"/> and name that corresponds to the type's fully qualified name.
        /// </summary>
        /// <typeparam name="T">The type of the configuration section.</typeparam>
        /// <returns>An instance of the type <typeparamref name="T"/> containing the configuration section or a null reference if configuration section was not found.</returns>
        public T GetConfigurationSection<T>() where T : ConfigurationSection
        {
            return GetConfigurationSection<T>(typeof(T).FullName);
        }

        /// <summary>
        /// Returns a configuration section defined by the given type <typeparamref name="T"/> and specified section name.
        /// </summary>
        /// <typeparam name="T">The type of the configuration section.</typeparam>
        /// <param name="sectionName">The name of the configuration section.</param>
        /// <returns>An instance of the type <typeparamref name="T"/> containing the configuration section or a null reference if configuration section was not found.</returns>
        public T GetConfigurationSection<T>(string sectionName) where T: ConfigurationSection
        {
            ConfigurationSection configSection = null;

            if (!this.configSectionCache.TryGetValue(sectionName, out configSection))
            {
                lock (this.configSectionCacheLock)
                {
                    if (!this.configSectionCache.TryGetValue(sectionName, out configSection))
                    {
                        configSection = ConfigurationManager.GetSection(sectionName) as ConfigurationSection;

                        if (configSection != null)
                        {
                            this.configSectionCache.Add(sectionName, configSection);
                        }
                    }
                }
            }
            return configSection as T;
        }

        /// <summary>
        /// Signals the configuration manager to unload all currently loaded configuration sections by removing them from in-memory cache.
        /// </summary>
        public void Unload()
        {
            lock (this.configSectionCacheLock)
            {
                this.configSectionCache.Clear();
            }
        }
        #endregion
    }
}
posted @ 2013-12-08 15:46  冯翔  阅读(249)  评论(0)    收藏  举报