• 微软企业库源码解析——DAAB(二)DatabaseFactory(ConfigurationNameMappingStrategy篇)


    其实跟踪到这里我就已经崩溃了,不过为了让问题水落石出,我们祭出Reflactor继续追踪下去。

       1: public TTypeToBuild BuildUp<TTypeToBuild>(IReadWriteLocator locator, ILifetimeContainer lifetime, IPolicyList policies, IStrategyChain strategies, object buildKey, object existing)
       2: {
       3:     return (TTypeToBuild) this.BuildUp(locator, lifetime, policies, strategies, buildKey, existing);
       4: }
       5:  
       6: public object BuildUp(IReadWriteLocator locator, ILifetimeContainer lifetime, IPolicyList policies, IStrategyChain strategies, object buildKey, object existing)
       7: {
       8:     Guard.ArgumentNotNull(strategies, "strategies");
       9:     BuilderContext context = new BuilderContext(strategies, locator, lifetime, policies, buildKey, existing);
      10:     return strategies.ExecuteBuildUp(context);
      11: }

    这里面的Guard是用来检测各种参数的,其中提供了类似于ArgumentNotNull之类的一系列方法,也是比较常用的,将参数检测集中起来的方法,值得大家学习。

    可以看到,Builder用了StrategyChain进行的装配。所以,我们又得返回去看看StrategyChain里面都有什么东西。

    找到EnterpriseLibraryFactory的构造函数,发现StrategyChain是这么出来的

       1: StagedStrategyChain<BuilderStage> stagedStrategyChain = new StagedStrategyChain<BuilderStage>();
       2: stagedStrategyChain.AddNew<ConfigurationNameMappingStrategy>(BuilderStage.PreCreation);
       3: stagedStrategyChain.AddNew<LocatorLookupStrategy>(BuilderStage.PreCreation);
       4: stagedStrategyChain.AddNew<ConfiguredObjectStrategy>(BuilderStage.PreCreation);
       5: stagedStrategyChain.AddNew<InstrumentationStrategy>(BuilderStage.PostInitialization);
       6: strategyChain = stagedStrategyChain.MakeStrategyChain();

    据我猜测,BuilderStage应该是说明在何时这个策略参与对象的装配的。

    我们从第一个Strategy开始看,即ConfigurationNameMappingStrategy。

       1: /// <summary>
       2: /// Implementation of <see cref="IBuilderStrategy"/> which maps null instance names into a different name.
       3: /// </summary>
       4: /// <remarks>
       5: /// The strategy is used to deal with default names.
       6: /// </remarks>
       7: /// <seealso cref="ConfigurationNameMapperAttribute"/>
       8: /// <seealso cref="IConfigurationNameMapper"/>
       9: public class ConfigurationNameMappingStrategy : EnterpriseLibraryBuilderStrategy
      10: {
      11:     /// <summary>
      12:     /// Override of <see cref="IBuilderStrategy.PreBuildUp"/>. Updates the instance name using a name mapper associated to type 
      13:     /// to build so later strategies in the build chain will use the updated instance name.
      14:     /// </summary>
      15:     /// <remarks>
      16:     /// Will only update the instance name if it is <see langword="null"/>.
      17:     /// </remarks>
      18:     /// <param name="context">The <see cref="IBuilderContext"/> that represents the current building process.</param>
      19:     /// <exception cref="System.Configuration.ConfigurationErrorsException"> when the configuration required to do the mapping is not present or is 
      20:     /// invalid in the configuration source.</exception>
      21:     public override void PreBuildUp(IBuilderContext context)
      22:     {
      23:         base.PreBuildUp(context);
      24:  
      25:         NamedTypeBuildKey key = (NamedTypeBuildKey) context.BuildKey;
      26:  
      27:         if (key.Name == null)
      28:         {
      29:             ConfigurationReflectionCache reflectionCache = GetReflectionCache(context);
      30:  
      31:             IConfigurationNameMapper mapper = reflectionCache.GetConfigurationNameMapper(key.Type);
      32:             if (mapper != null)
      33:             {
      34:                 context.BuildKey = new NamedTypeBuildKey(key.Type, mapper.MapName(null, GetConfigurationSource(context)));
      35:             }
      36:         }
      37:     }
      38: }

    从注释我们可以看出,这个策略是为了将默认名称取出来并替换null名称的。刚刚我们CreateDatabase的时候如果不加上实例名的话,传递的就是null。这个策略就是将这个null的实例名替换成被标记为Default的实例名,以便下面的装配工作顺利进行。

    我们看到PreBuildUp方法是override的,同时还调用了base.PreBuildUp。在ConfigurationNameMappingStrategy的父类EnterpriseLibraryBuilderStrategy中没有覆盖PreBuildUp方法。我用Reflactor查看了EnterpriseLibraryBuilderStrategy的父类BuilderStrategy,看到了BuilderStrategy的四个方法

       1: public virtual void PostBuildUp(IBuilderContext context);
       2: public virtual void PostTearDown(IBuilderContext context);
       3: public virtual void PreBuildUp(IBuilderContext context);
       4: public virtual void PreTearDown(IBuilderContext context);

    都是空方法

    NamedTypeBuildKey key实际上就是在EnterpriseLibraryFactory中调用BuildUp时传递的参数NamedTypeBuildKey.Make<T>(),而这个方法得到的是NamedTypeBuildKey(typeof(T))。

    GetReflectionCache方法是在其父类EnterpriseLibraryBuilderStrategy中定义的

       1: protected static ConfigurationReflectionCache GetReflectionCache(IBuilderContext context)
       2: {
       3:     IReflectionCachePolicy policy
       4:         = context.Policies.Get<IReflectionCachePolicy>(typeof(IReflectionCachePolicy));
       5:  
       6:     if (policy == null)
       7:         return new ConfigurationReflectionCache();
       8:     else
       9:         return policy.ReflectionCache;
      10: }

    reflectionCache.GetConfigurationNameMapper(key.Type)这里经过了一系列复杂的变化后得到了Database标记中包含的Attribute中指定的ConfigurationNameMapper:DatabaseMapper。这里,我们不用太担心反射的性能,因为微软企业库对反射后的结果等都进行了缓存,大大提高了效率,所以,我们完全可以在需要时随时创建Database实例。

    再来看mapper.MapName

       1: /// <summary>
       2: /// This method supports the Enterprise Library infrastructure and is not intended to be used directly from your code.
       3: /// Returns the default database name from the configuration in the <paramref name="configSource"/>, if the
       4: /// value for <paramref name="name"/> is <see langword="null"/> (<b>Nothing</b> in Visual Basic).
       5: /// </summary>
       6: /// <param name="name">The current name.</param>
       7: /// <param name="configSource">The source for configuration information.</param>
       8: /// <returns>The default database name if <paramref name="name"/> is <see langword="null"/> (<b>Nothing</b> in Visual Basic),
       9: /// otherwise the original value for <b>name</b>.</returns>
      10: public string MapName(string name, IConfigurationSource configSource)
      11: {
      12:     if (name != null)
      13:         return name;
      14:  
      15:     return new DatabaseConfigurationView(configSource).DefaultName;
      16: }

    继续跟踪到DatabaseConfigurationView

       1: /// <summary>
       2: /// <para>Gets the <see cref="DatabaseSettings"/> configuration data.</para>
       3: /// </summary>
       4: /// <returns>
       5: /// <para>The <see cref="DatabaseSettings"/> configuration data.</para>
       6: /// </returns>
       7: public DatabaseSettings DatabaseSettings
       8: {
       9:     get { return (DatabaseSettings)configurationSource.GetSection(DatabaseSettings.SectionName); }
      10: }
      11:  
      12: /// <summary>
      13: /// <para>Gets the name of the default configured <see cref="Database"/>.</para>
      14: /// </summary>
      15: /// <returns>
      16: /// <para>The name of the default configured <see cref="Database"/>.</para>
      17: /// </returns>
      18: public string DefaultName
      19: {
      20:     get
      21:     {
      22:         DatabaseSettings settings = this.DatabaseSettings;
      23:         string databaseName = settings != null ? settings.DefaultDatabase : null;
      24:         return databaseName;
      25:     }
      26: }

    这里的configurationSource就是一开始在DatabaseFactory中ConfigurationSourceFactory.Create()得到的,后来经过各种传递用在这里了。

    接下来我们只好跑到ConfigurationSourceFactory中看看是怎么回事了。

       1: /// <summary>
       2: /// Creates a new configuration sources based on the default configuration information from the 
       3: /// application's default configuration file.
       4: /// </summary>
       5: /// <returns>The new configuration source instance described as the default in the configuration file,
       6: /// or a new instance of <see cref="SystemConfigurationSource"/> if the is no configuration sources configuration.</returns>
       7: /// <exception cref="ConfigurationSourceSection">when there is a configuration section but it does not define
       8: /// a default configurtion source, or when the configuration for the defined default configuration source is not found.</exception>
       9: public static IConfigurationSource Create()
      10: {
      11:     ConfigurationSourceSection configurationSourceSection
      12:         = ConfigurationSourceSection.GetConfigurationSourceSection();
      13:  
      14:     if (configurationSourceSection != null)
      15:     {
      16:         string systemSourceName = configurationSourceSection.SelectedSource;
      17:         if (!string.IsNullOrEmpty(systemSourceName))
      18:         {
      19:             return Create(systemSourceName);
      20:         }
      21:         else
      22:         {
      23:             throw new ConfigurationErrorsException(Resources.ExceptionSystemSourceNotDefined);
      24:         }
      25:     }
      26:  
      27:     return new SystemConfigurationSource();
      28: }

    接下来先得看看这句

    ConfigurationSourceSection configurationSourceSection = ConfigurationSourceSection.GetConfigurationSourceSection();

    这句得到的configurationSourceSection如果是null的话,就直接返回了SystemConfigurationSource

    /// <summary>
    /// Configuration section for the configuration sources.
    /// </summary>
    /// <remarks>
    /// This configuration must reside in the application's default configuration file.
    /// </remarks>
    public class ConfigurationSourceSection : SerializableConfigurationSection
    {
    private const string selectedSourceProperty = "selectedSource";
    private const string sourcesProperty = "sources";

    /// <summary>
    /// This field supports the Enterprise Library infrastructure and is not intended to be used directly from your code.
    /// </summary>
    public const string SectionName = "enterpriseLibrary.ConfigurationSource";

    /// <summary>
    /// Returns the <see cref="ConfigurationSourceSection"/> from the application's default configuration file.
    /// </summary>
    /// <returns>The section from the configuration file, or <see langword="null"/> (<b>Nothing</b> in Visual Basic) if the section is not present in the configuration file.</returns>
    public static ConfigurationSourceSection GetConfigurationSourceSection()
    {
    return (ConfigurationSourceSection)ConfigurationManager.GetSection(SectionName);
    }

    /// <summary>
    /// Gets or sets the name for the default configuration source.
    /// </summary>
    [ConfigurationProperty(selectedSourceProperty, IsRequired=true)]
    public string SelectedSource
    {
    get
    {
    return (string)this[selectedSourceProperty];
    }
    set
    {
    this[selectedSourceProperty] = value;
    }
    }

    /// <summary>
    /// Gets the collection of defined configuration sources.
    /// </summary>
    [ConfigurationProperty(sourcesProperty, IsRequired = true)]
    public NameTypeConfigurationElementCollection<ConfigurationSourceElement, ConfigurationSourceElement> Sources
    {
    get
    {
    return (NameTypeConfigurationElementCollection<ConfigurationSourceElement, ConfigurationSourceElement>)this[sourcesProperty];
    }

    }
    }

    可以看出,这里实际上是从App.config或者Web.config中读取节enterpriseLibrary.ConfigurationSource

    这是Entlib4里面新加入的功能,可以把配置节独立出来存储到别的文件中,没有尝试过的朋友可以打开微软企业库4.1试一试

    image

    这样的话,也就是说,如果选择了ConfigurationSource的话就从该位置读取配置,否则就从SystemConfigurationSource读取配置

    之后自然是顺理成章的从dataConfiguration配置节中读取DefaultDatabase,并且替换context中的BuildKey中的null值。

    EntLib的庞大实在是令我崩溃,只好把这个策略再划分出来了。

    不过不得不佩服ObjectBuilder的设计者,太强大了。

  • 相关阅读:
    day6 面向对象(2)
    day5 面向对象
    day4 函数重载
    sqlserver 存储过程 增加
    sqlserver 存储过程 修改
    sqlserver 存储过程 删除
    sqlserver 存储过程 查询
    上篇: php 微信公众号 基于Thinkphp3.2框架开发
    bzoj 2726: [SDOI2012]任务安排
    bzoj 4199 [NOI2015]寿司晚宴
  • 原文地址:https://www.cnblogs.com/HCOONa/p/1522781.html
Copyright © 2020-2023  润新知