使用 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。 传递给此方法的容器将包含 (的所有依赖项,即 IFactProviderIFactTranslator 类的实例,以及解析程序完成其处理所需的任何其他类型) 。

以下代码是 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 解析程序

  1. (可选) 使用实现 IFactProvider 接口的类创建程序集或程序集,并包含 RegisterFact 方法,该方法提供进行解析所需的信息。

  2. (可选) 使用实现 IFactTranslator 接口的类创建程序集或程序集,并包含 TranslateFact 方法,该方法将提供的事实转换为解析程序字典中的键/值对。

  3. 使用实现 IResolveContainerIResolveProvider 接口的类创建程序集,该类包含 一个 Resolve 方法,该方法验证解析程序配置、从事实提供程序收集所有事实、执行任何专用处理、使用事实翻译器翻译它们,并将已翻译的事实作为 Dictionary 类的实例返回。

  4. 通过使用<包含根名字对象作为 name 属性和完全限定程序集名称作为类型属性的解析程序元素,将解析程序>添加到 Esb.config 配置文件来注册解析程序。

  5. 将特定于 Unity 的配置添加到此冲突解决程序的 Esb.config 文件。

  6. (可选) 创建定义根名字对象和查询参数的架构,然后将其保存在 ESB 中。Schemas.Resolvers 文件夹。 名称应遵循现有的 ESB 命名约定;这意味着它应使用追加“_Resolution.xsd”的根名字对象的名称。

  7. (可选) 从新架构生成类并将其保存在自定义冲突解决程序程序集中。 这将在自定义冲突解决程序中公开类型化参数。

  8. 在全局程序集缓存中注册所有程序集。