• 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方法

  • 相关阅读:
    Azure Application Gateway (6) 使用OpenSSL创建SAN证书
    MySQL常用函数整理,建议收藏!
    设计一个接口至少要考虑这14点!
    SpringBoot 常用读取配置文件的 3 种方法!
    MyBatisPlus常用功能总结!(附项目示例)
    接口测试神器Apifox,亲测好用!
    huey在windows下使用的坑
    sublimeCodeIntel在windows下安装的坑
    字符串生成二维码
    android中EditText有光标不弹出软键盘处理(转)
  • 原文地址:https://www.cnblogs.com/wdfrog/p/1266923.html
Copyright © 2020-2023  润新知