使用 Unity 容器创建自定义解析程序
可以使用 Unity 应用程序块 (Unity) (https://go.microsoft.com/fwlink/?LinkId=188286) 创建自定义解析逻辑和元数据源的运行时依赖项注入。
事实提供者
事实提供程序是实现 IFactProvider 接口的类的实例。 此接口公开名为 RegisterFact 的方法的三个不同重载。 此方法采用消息、解析程序配置,在某些情况下,还采用管道上下文,并返回 对象。 此对象可能是以某种方式从输入中提取的信息,可能是某种形式的计算,也可以是从某个外部源查找的。 事实提供程序返回的每个对象都可以称为事实,通常由解析容器添加到列表中,供事实翻译人员稍后使用。
Unity 解析程序可能具有零个或多个事实提供程序,这些提供程序可以随时通过单个配置更改来添加或删除。
以下代码是事实提供程序中包含的逻辑示例。 还可以在 ESB 的 ItineraryStaticFactProvider.cs 文件中找到此代码。Resolver.Itinerary.Facts 项目。 它是 ITINERARY-STATIC 冲突解决程序中的一个组件,用于从冲突解决程序连接字符串收集行程的名称和版本。
public object RegisterFact(IBaseMessage message, IPipelineContext pipelineContext, Dictionary\<string, string\> resolverContents)
{
return GetItineraryFactFromResolver(resolverContents);
}
private static object GetItineraryFactFromResolver(Dictionary\<string, string\> resolverContents)
{
if (resolverContents == null)
throw new ArgumentNullException("resolverContents");
ItineraryFact itineraryFact = new ItineraryFact();
itineraryFact.Name = ResolverMgr.GetConfigValue(resolverContents, true, "name");
string version = ResolverMgr.GetConfigValue(resolverContents, false, "version");
if (!string.IsNullOrEmpty(version))
{
EventLogger.Write(string.Format("Version set with value {0}.", version));
itineraryFact.SetVersion(version);
}
return itineraryFact;
}
事实翻译器
事实翻译器是实现 IFactTranslator 接口的类的实例。 此接口公开名为 TranslateFact 的单个方法。 此方法采用对象数组,该数组包含事实列表和解析程序字典,解析程序稍后将使用事实翻译器返回这些字典。 事实翻译器负责以有意义的方式处理事实提供程序提供的事实,然后填充解析程序字典。
Unity 解析程序可能具有零个或多个事实翻译器,只需更改一次配置即可随时添加或删除这些事实翻译器。
以下代码是事实翻译器中包含的逻辑示例。 还可以在 ESB 的 ItineraryStaticFactTranslator.cs 文件中找到此代码。Resolver.Itinerary.Facts 项目。 它是 ITINERARY-STATIC 解析程序中的一个组件,用于执行数据库查询,以按名称和版本(可选)收集行程的行程 XML。
public void TranslateFact(object[] facts, Dictionary\<string, string\> factDictionary)
{
#region Argument Check
if (null == facts) throw new ArgumentNullException("fact");
if (null == factDictionary) throw new ArgumentNullException("factDictionary");
#endregion
#region Local Variables
Version version = new Version(1, 0);
#endregion
try
{
foreach (object fact in facts)
{
if (fact is ItineraryFact)
{
ItineraryFact targetFact = (ItineraryFact)fact;
factDictionary.Add(_FactKeyItineraryName, targetFact.Name ?? string.Empty);
if (null != targetFact.Version)
{
factDictionary.Add(_FactKeyItineraryVersion, targetFact.Version.ToString(2) ?? string.Empty);
version = targetFact.Version;
}
string xml = null;
if (targetFact.Version != null)
{
xml = _repositoryProvider.GetItinerary(targetFact.Name, version.Major, version.Minor);
}
else
{
xml = _repositoryProvider.GetItinerary(targetFact.Name);
}
if (!string.IsNullOrEmpty(xml))
{
factDictionary.Add(_FactKeyItinerary, xml);
}
break;
}
}
}
#region Exception Handling
catch (System.Exception)
{
throw;
}
#endregion
}
#endregion
解析容器
解析容器是实现 IResolveContainer 接口的类。 通常,它还实现 IResolveProvider 接口。 IResolveContainer 接口公开了一个名为 Initialize 的方法,该方法采用 IUnityContainer。 传递给此方法的容器将包含 (的所有依赖项,即 IFactProvider 和 IFactTranslator 类的实例,以及解析程序完成其处理所需的任何其他类型) 。
以下代码是 IResolveContainer 接口的实现示例。 还可以在 ESB 的 StaticItineraryResolveContainer.cs 文件中找到此代码。Resolver.行程项目。
#region IResolveContainer Members
private static IUnityContainer Container;
// <summary>
// This is used by the Unity resolver to initialize the current
// object with the Unity container.
// </summary>
// <param name="container">Unity container used for dependency
// injection.</param>
// <remarks>
// The container used is the ITINERARY container, although this is
// configurable in Esb.config.
// </remarks>
public void Initialize(IUnityContainer container)
{
if (container == null)
throw new ArgumentNullException("container");
Container = container;
}
#endregion
在解析容器中,在 IResolveProvider 接口的 Resolve 方法的实现中,必须循环访问 Unity 容器中的所有事实提供程序和事实翻译程序,以允许每个事实提供者执行其处理。
以下代码是 Resolve 容器中包含的逻辑示例。 还可以在 ESB 的 StaticItineraryResolveContainer.cs 文件中找到此代码。Resolver.行程项目。
public Dictionary\<string, string\> Resolve(ResolverInfo resolverInfo,
XLANGMessage message)
{
#region Argument Check
if (String.IsNullOrEmpty(resolverInfo.Config))
throw new ArgumentNullException("resolverInfo.Config");
if (String.IsNullOrEmpty(resolverInfo.Resolver))
throw new ArgumentNullException("resolverInfo.Resolver");
if (null == message)
throw new ArgumentNullException("message");
#endregion Argument Check
return ResolveStatic(resolverInfo.Config, resolverInfo.Resolver,
(factProvider, resolverContent) =>
factProvider.RegisterFact(message, resolverContent)
);
}
private Dictionary\<string, string\> ResolveStatic(string config, string resolver,
Func<IFactProvider, Dictionary\<string, string\>, object> RegisterFact)
{
try
{
EventLogger.Write(string.Format("Received {0} value in ITINERARY STATIC resolver.", config));
Dictionary\<string, string\> queryParams =
ResolverMgr.GetFacts(config, resolver);
List<object> facts = new List<object>();
foreach (IFactProvider factProvider in Container.ResolveAll<IFactProvider>())
{
facts.Add(RegisterFact(factProvider, queryParams));
}
Dictionary\<string, string\> resolverDictionary = new Dictionary\<string, string\>();
object[] convertedFacts = facts.ToArray();
foreach (IFactTranslator translator in Container.ResolveAll<IFactTranslator>())
{
translator.TranslateFact(convertedFacts, resolverDictionary);
}
return resolverDictionary;
}
catch (System.Exception ex)
{
EventLogger.Write(MethodInfo.GetCurrentMethod(), ex);
throw;
}
}
配置自定义 Unity 冲突解决程序
若要配置自定义 Unity 冲突解决程序,应采用与创建自定义冲突解决程序时相同的配置步骤;但是,必须包含一些其他配置才能正确注册构成解析程序的组件。
首先,在 Esb.config 文件中,在解析程序声明下,必须使用以下两个属性添加 resolverConfig 节点:
unitySectionName。 此属性包含配置文件中的配置节的名称,该配置文件包含 Unity 应用程序块的配置;默认情况下,此属性的值为 esb.resolver。
unityContainerName。 此属性包含特定于自定义解析程序的 Unity 配置中定义的 Unity 容器的名称。
以下 XML 是 解析程序 节点中所需配置的一个示例。
<resolver name="ITINERARY-STATIC" type="Microsoft.Practices.ESB.Resolver.Unity.ResolveProvider, Microsoft.Practices.ESB.Resolver.Unity, Version=2.0.0.0, Culture=neutral, PublicKeyToken=c62dd63c784d6e22">
<resolverConfig>
<add name="unitySectionName" value="esb.resolver" />
<add name="unityContainerName" value="ITINERARY" />
</resolverConfig>
</resolver>
以下 XML 是 esb.resolver 节点下所需的配置示例。
<typeAliases>
<!-- Lifetime manager types -->
<typeAlias alias="singleton" type="Microsoft.Practices.Unity.ContainerControlledLifetimeManager, Microsoft.Practices.Unity, Version=1.2.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
<!-- std type providers -->
<typeAlias alias="string" type="System.String, mscorlib"/>
<typeAlias alias="int" type="System.Int32, mscorlib"/>
<!-- repository providers -->
<typeAlias alias="IRepositoryProvider" type="Microsoft.Practices.ESB.Resolver.Itinerary.Facts.Repository.IRepositoryProvider, Microsoft.Practices.ESB.Resolver.Itinerary.Facts, Version=2.0.0.0, Culture=neutral, PublicKeyToken=c62dd63c784d6e22"/>
<typeAlias alias="SqlRepositoryProvider" type="Microsoft.Practices.ESB.Resolver.Itinerary.DataAccess.SqlRepositoryProvider, Microsoft.Practices.ESB.Resolver.Itinerary.DataAccess, Version=2.0.0.0, Culture=neutral, PublicKeyToken=c62dd63c784d6e22"/>
<!-- fact providers -->
<typeAlias alias="IFactProvider" type="Microsoft.Practices.ESB.Resolver.Facts.IFactProvider, Microsoft.Practices.ESB.Resolver.Facts, Version=2.0.0.0, Culture=neutral, PublicKeyToken=c62dd63c784d6e22"/>
<typeAlias alias="IFactTranslator" type="Microsoft.Practices.ESB.Resolver.Facts.IFactTranslator, Microsoft.Practices.ESB.Resolver.Facts, Version=2.0.0.0, Culture=neutral, PublicKeyToken=c62dd63c784d6e22"/>
<typeAlias alias="ItineraryFactProvider" type="Microsoft.Practices.ESB.Resolver.Itinerary.Facts.ItineraryFactProvider, Microsoft.Practices.ESB.Resolver.Itinerary.Facts, Version=2.0.0.0, Culture=neutral, PublicKeyToken=c62dd63c784d6e22"/>
<typeAlias alias="ItineraryStaticFactProvider" type="Microsoft.Practices.ESB.Resolver.Itinerary.Facts.ItineraryStaticFactProvider, Microsoft.Practices.ESB.Resolver.Itinerary.Facts, Version=2.0.0.0, Culture=neutral, PublicKeyToken=c62dd63c784d6e22"/>
<typeAlias alias="ItineraryHeaderFactProvider" type="Microsoft.Practices.ESB.Resolver.Itinerary.Facts.ItineraryHeaderFactProvider, Microsoft.Practices.ESB.Resolver.Itinerary.Facts, Version=2.0.0.0, Culture=neutral, PublicKeyToken=c62dd63c784d6e22"/>
<typeAlias alias="ResolutionFactProvider" type="Microsoft.Practices.ESB.Resolver.Itinerary.Facts.ResolutionFactProvider, Microsoft.Practices.ESB.Resolver.Itinerary.Facts, Version=2.0.0.0, Culture=neutral, PublicKeyToken=c62dd63c784d6e22"/>
<typeAlias alias="DefaultFactTranslator" type="Microsoft.Practices.ESB.Resolver.Facts.DefaultFactTranslator, Microsoft.Practices.ESB.Resolver.Facts, Version=2.0.0.0, Culture=neutral, PublicKeyToken=c62dd63c784d6e22"/>
<typeAlias alias="ItineraryFactTranslator" type="Microsoft.Practices.ESB.Resolver.Itinerary.Facts.ItineraryFactTranslator, Microsoft.Practices.ESB.Resolver.Itinerary.Facts, Version=2.0.0.0, Culture=neutral, PublicKeyToken=c62dd63c784d6e22"/>
<!-- resolve providers -->
<typeAlias alias="IResolveProvider" type="Microsoft.Practices.ESB.Resolver.IResolveProvider, Microsoft.Practices.ESB.Resolver, Version=2.0.0.0, Culture=neutral, PublicKeyToken=c62dd63c784d6e22"/>
<typeAlias alias="ItineraryResolveProvider" type="Microsoft.Practices.ESB.Resolver.Itinerary.BREItineraryResolverContainer,Microsoft.Practices.ESB.Resolver.Itinerary, Version=2.0.0.0, Culture=neutral, PublicKeyToken=c62dd63c784d6e22 "/>
<typeAlias alias="StaticItineraryResolveProvider" type="Microsoft.Practices.ESB.Resolver.Itinerary.StaticItineraryResolveContainer,Microsoft.Practices.ESB.Resolver.Itinerary, Version=2.0.0.0, Culture=neutral, PublicKeyToken=c62dd63c784d6e22 "/>
</typeAliases>
<containers>
<container name="ITINERARY">
<types>
<type type="IResolveProvider" mapTo="StaticItineraryResolveProvider" />
<type type="IRepositoryProvider" mapTo="SqlRepositoryProvider" name="CurrentRepositoryProvider">
<lifetime type="singleton" />
<typeConfig extensionType="Microsoft.Practices.Unity.Configuration.TypeInjectionElement,Microsoft.Practices.Unity.Configuration, Version=1.2.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
<constructor>
<param name="connectionStringName" parameterType="string">
<value value="ItineraryDb"/>
</param>
<param name="cacheManagerName" parameterType="string">
<value value="Itinerary Cache Manager"/>
</param>
<param name="cacheTimeout" parameterType="string">
<value value="120" />
</param>
</constructor>
</typeConfig>
</type>
<type type="IFactProvider" mapTo="ResolutionFactProvider" name="ResolutionFactProvider" />
<type type="IFactProvider" mapTo="ItineraryHeaderFactProvider" name="HeaderFactProvider" />
<type type="IFactProvider" mapTo="ItineraryStaticFactProvider" name="StaticFactProvider" />
<type type="IFactTranslator" mapTo="DefaultFactTranslator" name="DefaultFactTranslator">
<lifetime type="singleton" />
</type>
<type type="IFactTranslator" mapTo="ItineraryFactTranslator" name="ItineraryFactTranslator">
<lifetime type="singleton" />
<typeConfig extensionType="Microsoft.Practices.Unity.Configuration.TypeInjectionElement,Microsoft.Practices.Unity.Configuration, Version=1.2.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
<constructor>
<param name="repositoryProvider" parameterType="IRepositoryProvider">
<dependency name="CurrentRepositoryProvider"/>
</param>
</constructor>
</typeConfig>
</type>
</types>
</container>
</containers>
有关 esb.resolvers 节点中所需的配置的详细信息,请参阅 MSDN 上 Unity 应用程序块 (https://go.microsoft.com/fwlink/?LinkId=188288) 的源架构。
创建自定义 Unity 冲突解决程序
创建自定义 Unity 解析程序
(可选) 使用实现 IFactProvider 接口的类创建程序集或程序集,并包含 RegisterFact 方法,该方法提供进行解析所需的信息。
(可选) 使用实现 IFactTranslator 接口的类创建程序集或程序集,并包含 TranslateFact 方法,该方法将提供的事实转换为解析程序字典中的键/值对。
使用实现 IResolveContainer 和 IResolveProvider 接口的类创建程序集,该类包含 一个 Resolve 方法,该方法验证解析程序配置、从事实提供程序收集所有事实、执行任何专用处理、使用事实翻译器翻译它们,并将已翻译的事实作为 Dictionary 类的实例返回。
通过使用<包含根名字对象作为 name 属性和完全限定程序集名称作为类型属性的解析程序元素,将解析程序>添加到 Esb.config 配置文件来注册解析程序。
将特定于 Unity 的配置添加到此冲突解决程序的 Esb.config 文件。
(可选) 创建定义根名字对象和查询参数的架构,然后将其保存在 ESB 中。Schemas.Resolvers 文件夹。 名称应遵循现有的 ESB 命名约定;这意味着它应使用追加“_Resolution.xsd”的根名字对象的名称。
(可选) 从新架构生成类并将其保存在自定义冲突解决程序程序集中。 这将在自定义冲突解决程序中公开类型化参数。
在全局程序集缓存中注册所有程序集。