遗忘海岸

江湖程序员 -Feiph(LM战士)

导航

Asp.net2.0中的提供程序---dashcommerce中的税率计算提供程序

 

  commerce starter kit (commerce Starter kit 随书 asp.net2.0典型项目开发 ---应用篇光盘中的一套程序,他改自dashcommerce) 中,有很多自定义提供程序的应用,比方在线支付模块,税率计算模块等,

 

 在网站App_Code/Services/TaxProvider/下面是相关的代码实现

 

 首先是配置节的代码,来自文件:TaxServiceSection.cs

 

namespace Commerce.Providers
{

 

    public class TaxServiceSection : ConfigurationSection
    {
        [ConfigurationProperty("providers")]
        public ProviderSettingsCollection Providers
        {
            get { return (ProviderSettingsCollection)base["providers"]; }
        }

        [StringValidator(MinLength = 1)]
        [ConfigurationProperty("defaultProvider",
            DefaultValue = "SqlTaxProvider")]
        public string DefaultProvider
        {
            get { return (string)base["defaultProvider"]; }
            set { base["defaultProvider"] = value; }
        }


 
 
    }
}

这里声明了TaxServiceSection类,这个类有一个DefaultProvider属性,以及一个Providers集合属性,集合属性使用的.net框架提供的ProviderSettingsCollection类,对于这个类大家应该不陌生,像MemberShip,RoleManager 配置节里的Providers就是对应这个类的,ProviderSettingsCollection 是ProviderSettings的集合类,ProviderSettings 继承于ConfigurationElement, ProviderSetttings 里面定义了一个name ,跟type属性,而Parameters 属性是一个NameValueCollection 类型的,应此如Membership 配置提供程序中(<providers><add name="" type="" .... /></providers>的其他属性如connectionStringName,enablePasswordRetrieval等多是保存在Parameters中的,参考下面的membership节

 

  <membership defaultProvider="AspNetSqlMembershipProvider" userIsOnlineTimeWindow="15" hashAlgorithmType="">
   <providers>
    <clear/>
    <add connectionStringName="LocalSqlServer" enablePasswordRetrieval="false" enablePasswordReset="true" requiresQuestionAndAnswer="true" applicationName="CSK" requiresUniqueEmail="false" passwordFormat="Hashed" maxInvalidPasswordAttempts="5" passwordAttemptWindow="10" passwordStrengthRegularExpression="" minRequiredPasswordLength="4" minRequiredNonalphanumericCharacters="0" name="AspNetSqlMembershipProvider" type="System.Web.Security.SqlMembershipProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"/>
   </providers>
  </membership>

 

 

 TaxServiceSection 在配置文件中对应的声明如下

 <section name="TaxService" type="Commerce.Providers.TaxServiceSection" allowDefinition="MachineToApplication" restartOnExternalChanges="true" requirePermission="false"/>

 

 具体的配置如下

 <TaxService defaultProvider="StrikeIronTaxProvider">
  <providers>
   <clear />
   <add serviceKey=""   name="StrikeIronTaxProvider" type="Commerce.Providers.StrikeIronTaxProvider" />
  </providers>
 </TaxService>

 

 

 以上是TaxServiceSection的介绍,TaxServiceSection的作用就是从web.config中读取配置数据,

 下面我们来看TaxProvider类,由于各地计算税率的方法不同因此这里把TaxProvider类声明为一个抽象类

  public abstract class TaxProvider : ProviderBase ,这个类继承自ProviderBase,ProviderBase是所有使用提供程序模型类的基类.在TaxProvider类中定义了一些属性如ServiceLogin,ServiceKey等,另外重要的是定义的三个抽象方法如下

        public abstract decimal GetTaxRate(string zip);
        public abstract decimal GetTaxRate(Commerce.Common.USState state);
        public abstract DataSet GetTaxTable(Commerce.Common.USState state);

 这三个抽象方法在具体的TaxProvider类中被实现,Commerce starter kit 给出了三个TaxProvider类的具体实现,他们是 StrikeIronTaxProvider.cs,FlatRateTaxProvider.cs以及ZeroTaxRateProvider.cs.

以StrikeIronTaxProvider为例, 里面首先是实现了 public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config) 方法,这个方法是必需实现的,因为Provider被实例化后,需要设置一些属性,而这些属性就是保存在web.config相应配置节中<providers>元素中的<add .../>元素中的属性,

下面这段代码摘自StrikeIronTaxprovider.cs中的Initialize方法

 

        public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config)
        {
            base.Initialize(name, config);
            try
            {
                this.ServiceKey = config["serviceKey"].ToString();
                //this.ServicePassword = config["servicePassword"].ToString();
                //this.ServiceLogin = config["serviceLogin"].ToString();
            }
            catch
            {
                throw new Exception("The Service Key must be set for the StrikeIronTaxProvider to work");
            }

        }

 

 提供程序模型提供了通过配置来替换具体实现类的编程模型,应此提供程序会通过反射机制来架载,具体实例化的时间是,对于应类被第一次调用时实例化,一般情况下只实例化一次. TaxService.cs文件中定义的TaxService类,封装了TaxProvider类的使用,他对外提供一组静态方法,将具体的TaxProvider类实例保存在静态字段中

 private static TaxProvider _provider = null,静太字段对应类的生命周期等同于应用程序的周期(Asp.net 的 Application  )

下面是这个类的代码:

 

 public class TaxService
    {
        #region Provider-specific bits
        private static TaxProvider _provider = null;
        private static object _lock = new object();

        public TaxProvider Provider
        {
            get { return _provider; }
        }

        public static TaxProvider Instance
        {
            get
            {
                LoadProviders();
                return _provider;
            }
        }
        private static void LoadProviders()
        {
            // Avoid claiming lock if providers are already loaded
            if (_provider == null)
            {
                lock (_lock)
                {
                    // Do this again to make sure _provider is still null
                    if (_provider == null)
                    {
                        // Get a reference to the <TaxServiceSection> section
                        TaxServiceSection section = (TaxServiceSection)
                            WebConfigurationManager.GetSection
                            ("TaxService");

                        // Only want one provider here
                         _provider = (TaxProvider)ProvidersHelper.InstantiateProvider
                            (section.Providers[0], typeof(TaxProvider));

                       
                        if (_provider == null)
                            throw new ProviderException
                                ("Unable to load default TaxProvider");
                    }
                }
            }
        }
        #endregion


        public static decimal CalculateAmountByZIP(string zipCode, decimal subTotal) {
            decimal dOut = 0;
   try {
    decimal dRate = Instance.GetTaxRate(zipCode);
    dOut = subTotal * dRate;
   } catch(Exception x) {
    throw new ApplicationException("Tax calculation failed: " + x.Message, x);
   }
            return dOut;
        }
        public static decimal GetUSTaxRate(string zipCode)
        {
            return Instance.GetTaxRate(zipCode);

        }
        public static decimal GetUSTaxRate(Commerce.Common.USState state)
        {
            return Instance.GetTaxRate(state);
        }
    }

 

我们注意到LoadProviders()方法中使用双lock语句来保证只实例化一个TaxProvider 的具体类(具体看配置文件),LoadProviders()中首先获取TaxServieceSection 节中的配置数据

         // Get a reference to the <TaxServiceSection> section
  TaxServiceSection section = (TaxServiceSection)WebConfigurationManager.GetSection("TaxService");

 接着是实例化

                        // Only want one provider here
                         _provider = (TaxProvider)ProvidersHelper.InstantiateProvider
                            (section.Providers[0], typeof(TaxProvider));

这里使用ProvidersHelper.InstantiateProvider() 方法

 

 下面是通过Reflector 找到的实现代码

 

public static class ProvidersHelper
{
    // Methods
    [AspNetHostingPermission(SecurityAction.Demand, Level=AspNetHostingPermissionLevel.Low)]
    public static ProviderBase InstantiateProvider(ProviderSettings providerSettings, Type providerType)
    {
        ProviderBase base2 = null;
        try
        {
            string str = (providerSettings.Type == null) ? null : providerSettings.Type.Trim();
            if (string.IsNullOrEmpty(str))
            {
                throw new ArgumentException(SR.GetString("Provider_no_type_name"));
            }
            Type c = ConfigUtil.GetType(str, "type", providerSettings, true, true);
            if (!providerType.IsAssignableFrom(c))
            {
                throw new ArgumentException(SR.GetString("Provider_must_implement_type", new object[] { providerType.ToString() }));
            }
            base2 = (ProviderBase) HttpRuntime.CreatePublicInstance(c);
            NameValueCollection parameters = providerSettings.Parameters;
            NameValueCollection config = new NameValueCollection(parameters.Count, StringComparer.Ordinal);
            foreach (string str2 in parameters)
            {
                config[str2] = parameters[str2];
            }
            base2.Initialize(providerSettings.Name, config);
        }
        catch (Exception exception)
        {
            if (exception is ConfigurationException)
            {
                throw;
            }
            throw new ConfigurationErrorsException(exception.Message, providerSettings.ElementInformation.Properties["type"].Source, providerSettings.ElementInformation.Properties["type"].LineNumber);
        }
        return base2;
    }

    [AspNetHostingPermission(SecurityAction.Demand, Level=AspNetHostingPermissionLevel.Low)]
    public static void InstantiateProviders(ProviderSettingsCollection configProviders, ProviderCollection providers, Type providerType)
    {
        foreach (ProviderSettings settings in configProviders)
        {
            providers.Add(InstantiateProvider(settings, providerType));
        }
    }
}

 

 注意红色的代码,这几行代码的功能是通过反射实例化类,并且将ProviderSettings中的Parameters属性复制到config中,

完成后调用ProviderBase的initialize方法, base2.Initialize(providerSettings.Name, config);

 

 //===================

internal static object CreateNonPublicInstance(Type type, object[] args)
{
    return Activator.CreateInstance(type, BindingFlags.CreateInstance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance, null, args, null);
}

最终调用的是 Activator.CreateInstance方法

 

 

posted on 2008-08-13 14:53  遗忘海岸  阅读(742)  评论(4编辑  收藏  举报