其實跟蹤到這里我就已經崩潰了,不過為了讓問題水落石出,我們祭出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的設計者,太強大了。
微軟企業庫源碼解析——DAAB(二)DatabaseFactory(ConfigurationNameMappingStrategy篇)
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
