其实跟踪到这里我就已经崩溃了,不过为了让问题水落石出,我们祭出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篇)

