其实跟踪到这里我就已经崩溃了,不过为了让问题水落石出,我们祭出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试一试
这样的话,也就是说,如果选择了ConfigurationSource的话就从该位置读取配置,否则就从SystemConfigurationSource读取配置
之后自然是顺理成章的从dataConfiguration配置节中读取DefaultDatabase,并且替换context中的BuildKey中的null值。
EntLib的庞大实在是令我崩溃,只好把这个策略再划分出来了。
不过不得不佩服ObjectBuilder的设计者,太强大了。
转载于:https://www.cnblogs.com/HCOONa/archive/2009/07/13/1522781.html
相关资源:JAVA上百实例源码以及开源项目