使用存储过程和 WCF 服务模型轮询 Oracle E-Business Suite

可以使用存储过程定期轮询 Oracle 数据库,将 Oracle E-Business 适配器配置为接收定期数据更改消息。 可以将存储过程指定为轮询语句,适配器会定期执行该语句来轮询 Oracle 数据库。

若要启用轮询,必须指定某些绑定属性,如本主题中所述。 有关适配器如何支持轮询的详细信息,请参阅 支持使用轮询的入站调用

使用 Oracle E-Business 适配器绑定属性配置轮询操作

下表汇总了用于配置适配器以接收数据更改消息的 Oracle E-Business 适配器绑定属性。 运行轮询应用程序时,必须指定这些绑定属性。

Binding 属性 说明
InboundOperationType 指定是要执行 轮询 还是 通知 入站操作。 默认值为 轮询
PolledDataAvailableStatement 指定适配器执行的 SQL 语句,以确定是否有任何数据可用于轮询。 仅当记录可用时,才会执行为 PollingInput 绑定属性指定的存储过程。
PollingInterval 指定 Oracle E-Business 适配器执行为 PolledDataAvailableStatement 绑定属性指定的语句的间隔(以秒为单位)。 默认为 30 秒。 轮询间隔确定连续轮询之间的时间间隔。 如果在指定的时间间隔内执行语句,则适配器在间隔中的剩余时间内休眠。
PollingInput 指定轮询语句。 若要使用存储过程进行轮询,必须为此绑定属性指定整个请求消息。 请求消息必须与发送到适配器以调用存储过程作为出站操作的相同。 默认值为 NULL。

必须指定 PollingInput 绑定属性的值才能启用轮询。 仅当有可供轮询的数据(由 PolledDataAvailableStatement 绑定属性确定)时,才会执行轮询语句。
PollingAction 指定轮询操作的操作。 可以使用添加适配器服务参考 Visual Studio 插件从为操作生成的服务接口确定轮询操作。
PostPollStatement 指定在执行 PollingInput 绑定属性指定的语句之后执行的语句块。
PollWhileDataFound 指定 Oracle 电子商务适配器是否忽略轮询间隔并连续执行轮询语句(如果轮询表中的数据可用)。 如果表中没有可用数据,适配器将还原为在指定的轮询间隔内执行轮询语句。 默认值为 false。

有关这些属性的更完整说明,请参阅 阅读有关适用于 Oracle E-Business Suite 绑定属性的 BizTalk 适配器的信息。 有关如何使用 Oracle 电子商务适配器进行轮询的完整说明,请阅读以下部分。

本主题如何演示轮询

在本主题中,为了演示 Oracle 电子商务适配器如何支持使用存储过程接收数据更改消息,请使用 GET_ACTIVITYS 存储过程轮询 Oracle 数据库中的 ACCOUNTACTIVITY 表。 此存储过程可用于 ACCOUNT_PKG 包。 可以运行随示例一起提供的 SQL 脚本,在数据库中创建这些对象。

注意

本主题中的示例轮询 ACCOUNTACTIVITY 表,它是通过运行示例提供的脚本创建的基数据库表。 必须执行本主题中所述的类似过程才能轮询任何其他表,包括接口表。

为了演示轮询操作,我们执行以下操作:

  • PolledDataAvailableStatement 绑定属性指定 SELECT 语句,以确定要轮询的表 (ACCOUNTACTIVITY) 具有任何数据的位置。 在此示例中,可以将此绑定属性设置为:

    SELECT COUNT (*) FROM ACCOUNTACTIVITY
    

    这可确保适配器仅在 ACCOUNTACTIVITY 表具有一些记录时执行轮询语句。

  • 通过提供请求消息作为 PollingInput 绑定属性的一部分来执行存储过程(GET_ACTIVITYS)。 此存储过程将检索 ACCOUNTACTIVITY 表中的所有行,你将从适配器获取响应消息。

  • 作为 PostPollStatement 绑定属性的一部分执行 PL/SQL 块。 此语句会将 ACCOUNTACTIVITY 表中的所有数据移到数据库中的另一个表。 发生这种情况后,下次执行 PollingInput 时,它将不会提取任何数据,因此GET_ACTIVITYS存储过程将返回空响应消息。

  • 在将更多数据添加到 ACCOUNTACTIVITY 表之前,您将继续收到空响应消息,因此必须使用新记录重新填充 ACCOUNTACTIVITY 表。 为此,可以运行示例随附的 more_activity_data.sql 脚本。 运行此脚本后,下一个轮询操作将提取插入到表中的新记录。

在 WCF 服务模型中配置轮询

若要将存储过程与 Oracle E-Business 适配器与 WCF 服务模型配合使用进行轮询,必须:

  • 为要轮询的存储过程 (接口) 生成 WCF 服务协定。 对于此示例,必须为 GET_ACTIVITYS 存储过程生成 WCF 服务协定作为入站操作。 为此,可以使用“添加适配器服务引用”插件。

  • 从此接口实现 WCF 服务。

  • 使用服务主机 (System.ServiceModel.ServiceHost) 托管此 WCF 服务。

关于本主题中使用的示例

本主题中的示例使用GET_ACTIVITYS存储过程轮询 ACCOUNTACTIVITY 数据库表。 一个用于生成表和存储过程的脚本随示例一起提供。 有关示例的详细信息,请参阅 Oracle EBS 适配器的示例。 Oracle 电子商务适配器示例还提供了基于本主题 StoredProcPolling_ServiceModel示例。

WCF 服务协定和类

可以使用添加适配器服务引用插件创建 WCF 服务协定, (接口) 和支持类 GET_ACTIVITYS 入站操作。 有关生成 WCF 服务协定的详细信息,请参阅 为 Oracle E-Business Suite 解决方案项目生成 WCF 客户端或 WCF 服务协定

WCF 服务协定 (接口)

以下代码演示为GET_ACTIVITYS入站操作生成的 WCF 服务协定 (接口)

[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
[System.ServiceModel.ServiceContractAttribute(Namespace="http://schemas.microsoft.com/OracleEBS/", ConfigurationName="PollingPackageApis_APPS_ACCOUNT_PKG")]
public interface PollingPackageApis_APPS_ACCOUNT_PKG {

    // CODEGEN: Generating message contract since the wrapper namespace (https://schemas.microsoft.com/OracleEBS/2008/05/PollingPackageApis/APPS/ACCOUNT_PKG) of message GET_ACTIVITYS
    // does not match the default value (https://schemas.microsoft.com/OracleEBS/)
    [System.ServiceModel.OperationContractAttribute(IsOneWay=true, Action="PollingPackageApis/APPS/ACCOUNT_PKG/GET_ACTIVITYS")]
    void GET_ACTIVITYS(GET_ACTIVITYS request);
}

消息协定

下面是 GET_ACTIVITYS 入站操作的消息协定。

[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
[System.ServiceModel.MessageContractAttribute(WrapperName="GET_ACTIVITYS", WrapperNamespace="http://schemas.microsoft.com/OracleEBS/2008/05/PollingPackageApis/APPS/ACCOUNT_PK" +
    "G", IsWrapped=true)]
public partial class GET_ACTIVITYS {

    [System.ServiceModel.MessageBodyMemberAttribute(Namespace="http://schemas.microsoft.com/OracleEBS/2008/05/PollingPackageApis/APPS/ACCOUNT_PK" +
        "G", Order=0)]
    public schemas.microsoft.com.OracleEBS._2008._05.RecordTypes.APPS.ACCOUNT_PKG.GET_ACTIVITYS.OUTRECSRecord[] OUTRECS;

    public GET_ACTIVITYS() {
    }

    public GET_ACTIVITYS(schemas.microsoft.com.OracleEBS._2008._05.RecordTypes.APPS.ACCOUNT_PKG.GET_ACTIVITYS.OUTRECSRecord[] OUTRECS) {
        this.OUTRECS = OUTRECS;
    }
}

WCF 服务类

添加适配器服务引用插件还会生成一个文件,该文件具有从服务协定 (接口) 实现的 WCF 服务类的存根。 文件的名称为 OracleEBSBindingService.cs。 可以插入逻辑以直接在此类中处理 GET_ACTIVITYS 操作。 以下代码显示由添加适配器服务引用插件生成的 WCF 服务类。

namespace OracleEBSBindingNamespace {

    public class OracleEBSBindingService : PollingPackageApis_APPS_ACCOUNT_PKG {

        // CODEGEN: Generating message contract since the wrapper namespace (https://schemas.microsoft.com/OracleEBS/2008/05/PollingPackageApis/APPS/ACCOUNT_PKG) of message GET_ACTIVITYS
        // does not match the default value (https://schemas.microsoft.com/OracleEBS/)
        public virtual void GET_ACTIVITYS(GET_ACTIVITYS request) {
            throw new System.NotImplementedException("The method or operation is not implemented.");
        }
    }
}

使用存储过程接收用于轮询的入站消息

本部分介绍如何编写 .NET 应用程序以使用 Oracle E-Business 适配器接收入站轮询消息。

使用存储过程接收轮询消息

  1. 使用添加适配器服务引用插件为 GET_ACTIVITYS入站操作 生成 WCF 服务协定 (接口) 和帮助程序类。 有关详细信息,请参阅 为 Oracle E-Business Suite 解决方案项目生成 WCF 客户端或 WCF 服务协定。 可以选择在生成服务协定和帮助程序类时指定绑定属性。 这可以保证在生成的配置文件中正确设置它们。

  2. 从步骤 1 中生成的接口和帮助程序类实现 WCF 服务。 如果处理从 GET_ACTIVITYS 操作接收的数据时遇到错误,此类的 GET_ACTIVITYS 方法可能会引发异常来中止轮询事务;否则, 方法不返回任何内容。 必须按如下所示对 WCF 服务类进行属性设置:

    [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
    

    GET_ACTIVITYS 方法中,可以直接实现应用程序逻辑。 此类可在 OracleEBSBindingService.cs 中找到。 此示例中的此代码子类为 OracleEBSBindingService 类。 在此代码中,轮询消息已接收并写入控制台。

    [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
    
    public class PollingService : OracleEBSBindingNamespace.OracleEBSBindingService
    {
        public override void  GET_ACTIVITYS(GET_ACTIVITYS request)
        {
            Console.WriteLine("\nNew Polling Records Received");
            Console.WriteLine("*************************************************");
            Console.WriteLine("Tx Id\tAccount\tAmount\tProcessed");
            for (int i = 0; i < request.OUTRECS.Length; i++)
            {
                Console.WriteLine("{0}\t{1}\t{2}\t{3}",
                request.OUTRECS[i].TID,
                request.OUTRECS[i].ACCOUNT,
                request.OUTRECS[i].AMOUNT,
                request.OUTRECS[i].PROCESSED);
            }
            Console.WriteLine("*************************************************");
            Console.WriteLine("\nHit <RETURN> to stop polling");
        }
    }
    
  3. 必须实现以下类,以避免将凭据作为 URI 的一部分传递。 在应用程序的后一部分中,你将实例化此类以传递凭据。

    class PollingCredentials : ClientCredentials, IServiceBehavior
    {
        public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
        {
            bindingParameters.Add(this);
        }
    
        public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
        { }
    
        public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
        { }
    
        protected override ClientCredentials CloneCore()
        {
            ClientCredentials clone = new PollingCredentials();
            clone.UserName.UserName = this.UserName.UserName;
            clone.UserName.Password = this.UserName.Password;
            return clone;
        }
    }
    
  4. 创建 OracleEBSBinding 并通过指定绑定属性来配置轮询操作。 可以在代码中显式执行此操作,也可以在配置中以声明方式执行此操作。 至少必须指定 InboundOperationTypePolledDataAvailableStatementPollingInputPollingAction 绑定属性。

    OracleEBSBinding binding = new OracleEBSBinding();
    binding.InboundOperationType = InboundOperation.Polling;
    binding.PolledDataAvailableStatement = "SELECT COUNT (*) FROM ACCOUNTACTIVITY";
    binding.PollingInput = "<GET_ACTIVITYS xmlns='http://schemas.microsoft.com/OracleEBS/2008/05/PackageApis/APPS/ACCOUNT_PKG'><INRECS>OPEN ? FOR SELECT * FROM ACCOUNTACTIVITY</INRECS></GET_ACTIVITYS>";
    binding.PollingAction = "PollingPackageApis/APPS/ACCOUNT_PKG/GET_ACTIVITYS";
    binding.PostPollStatement = "BEGIN ACCOUNT_PKG.PROCESS_ACTIVITY(); END;";
    

    注意

    请注意, PollingInput 绑定属性的值包含用于调用 GET_ACTIVITYS 存储过程作为出站操作的请求消息。

    重要

    在此示例中,由于轮询数据库表,因此无需设置应用程序上下文。 但是,如果要轮询接口表,则必须通过指定 OracleUserNameOraclePasswordOracleEBSResponsibilityName 绑定属性来设置应用程序上下文。 有关应用程序上下文的详细信息,请参阅 设置应用程序上下文

  5. 通过实例化在步骤 3 中创建的 PollingCredentials 类来指定 Oracle 电子商务套件凭据。

    PollingCredentials credentials = new PollingCredentials();
    credentials.UserName.UserName = "<Enter user name here>";
    credentials.UserName.Password = "<Enter password here>";
    
  6. 创建在步骤 2 中创建的 WCF 服务的实例。

    // create service instance
    PollingService service = new PollingService();
    
  7. 使用 WCF 服务和基本连接 URI 创建 System.ServiceModel.ServiceHost 的实例。 基连接 URI 不能包含入站 ID(如果已指定)。 还必须在此处传递凭据。

    // Enable service host
    Uri[] baseUri = new Uri[] { new Uri("oracleebs://ebs_instance_name") };
    ServiceHost serviceHost = new ServiceHost(service, baseUri);
    serviceHost.Description.Behaviors.Add(credentials);
    
    
  8. 将服务终结点添加到服务主机。 为此,请按以下步骤操作:

    • 使用在步骤 4 中创建的绑定。

    • 指定包含凭据的连接 URI,并根据需要指定入站 ID。

    • 将协定指定为“PollingPackageApis_APPS_ACCOUNT_PKG”以轮询MS_SAMPLE_EMPLOYEE接口表。

    // Add service endpoint: be sure to specify PollingPackageApis_APPS_ACCOUNT_PKG as the contract
    Uri ConnectionUri = new Uri("oracleebs://ebs_instance_name");
    serviceHost.AddServiceEndpoint("PollingPackageApis_APPS_ACCOUNT_PKG", binding, ConnectionUri);
    
  9. 若要接收轮询数据,请打开服务主机。 每当查询返回结果集时,适配器都将返回数据。

    // Open the service host to begin polling
    serviceHost.Open();
    
  10. 若要终止轮询,请关闭服务主机。

    重要

    适配器将继续轮询,直到服务主机关闭。

    serviceHost.Close();
    

示例

以下示例演示使用 GET_ACTIVITYS 存储过程轮询 ACCOUNTACTIVITY 数据库表的轮询应用程序。 PollingInput 绑定属性包含请求消息以调用GET_ACTIVITYS存储过程,该过程从 ACCOUNTACTIVITY 表读取所有数据,而投票后语句会将所有数据从 ACCOUNTACTIVITY 移动到 ACTIVITYHISTORY 表。

第一条轮询消息提供 ACCOUNTACTIVITY 表中的所有记录。 后续轮询消息将不包含任何记录,因为 post 轮询语句会删除记录。 在将更多数据添加到 ACCOUNTACTIVITY 表之前,不会收到任何轮询消息,因此必须使用新记录重新填充 ACCOUNTACTIVITY 表。 为此,可以运行示例随附的 more_activity_data.sql 脚本。

运行此脚本后,下一个轮询操作将提取插入到表中的新记录。 适配器将继续轮询,直到你通过按 <RETURN>关闭服务主机。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Adapters.OracleEBS;
using Microsoft.ServiceModel.Channels;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.ServiceModel.Channels;
using System.Collections.ObjectModel;

namespace StoredProcPolling_ServiceModel
{
    [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]

    public class PollingService : OracleEBSBindingNamespace.OracleEBSBindingService
    {
        public override void  GET_ACTIVITYS(GET_ACTIVITYS request)
        {
            Console.WriteLine("\nNew Polling Records Received");
            Console.WriteLine("*************************************************");
            Console.WriteLine("Tx Id\tAccount\tAmount\tProcessed");
            for (int i = 0; i < request.OUTRECS.Length; i++)
            {
                Console.WriteLine("{0}\t{1}\t{2}\t{3}",
                request.OUTRECS[i].TID,
                request.OUTRECS[i].ACCOUNT,
                request.OUTRECS[i].AMOUNT,
                request.OUTRECS[i].PROCESSED);
            }
            Console.WriteLine("*************************************************");
            Console.WriteLine("\nHit <RETURN> to stop polling");
        }
    }

    class PollingCredentials : ClientCredentials, IServiceBehavior
    {
        public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
        {
            bindingParameters.Add(this);
        }

        public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
        { }

        public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
        { }

        protected override ClientCredentials CloneCore()
        {
            ClientCredentials clone = new PollingCredentials();
            clone.UserName.UserName = this.UserName.UserName;
            clone.UserName.Password = this.UserName.Password;
            return clone;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            ServiceHost serviceHost = null;
            try
            {
                Console.WriteLine("Sample started...");
                Console.WriteLine("Press any key to start polling...");
                Console.ReadLine();

                OracleEBSBinding binding = new OracleEBSBinding();
                binding.InboundOperationType = InboundOperation.Polling;
                binding.PolledDataAvailableStatement = "SELECT COUNT (*) FROM ACCOUNTACTIVITY";
                binding.PollingInput = "<GET_ACTIVITYS xmlns='http://schemas.microsoft.com/OracleEBS/2008/05/PackageApis/APPS/ACCOUNT_PKG'><INRECS>OPEN ? FOR SELECT * FROM ACCOUNTACTIVITY</INRECS></GET_ACTIVITYS>";
                binding.PollingAction = "PollingPackageApis/APPS/ACCOUNT_PKG/GET_ACTIVITYS";
                binding.PostPollStatement = "BEGIN ACCOUNT_PKG.PROCESS_ACTIVITY(); END;";

                // This URI is used to specify the address for the ServiceEndpoint
                // It must contain the InboundId that was used to generate
                // the WCF service callback interface
                Uri ConnectionUri = new Uri("oracleebs://ebs_instance_name");

                // This URI is used to initialize the ServiceHost. It cannot contain
                // an InboundID; otherwise,an exception is thrown when
                // the ServiceHost is initialized.
                Uri[] baseUri = new Uri[] { new Uri("oracleebs://ebs_instance_name") };

                PollingCredentials credentials = new PollingCredentials();
                credentials.UserName.UserName = "<Enter user name here>";
                credentials.UserName.Password = "<Enter password here>";

                Console.WriteLine("Opening service host...");
                PollingService service = new PollingService();
                serviceHost = new ServiceHost(service, baseUri);
                serviceHost.Description.Behaviors.Add(credentials);
                serviceHost.AddServiceEndpoint("PollingPackageApis_APPS_ACCOUNT_PKG", binding, ConnectionUri);
                serviceHost.Open();
                Console.WriteLine("Service host opened...");
                Console.WriteLine("Polling started...");
                Console.ReadLine();
            }
            catch (Exception e)
            {
                Console.WriteLine("Exception :" + e.Message);
                Console.ReadLine();

                /* If there is an error it will be specified in the inner exception */
                if (e.InnerException != null)
                {
                    Console.WriteLine("InnerException: " + e.InnerException.Message);
                    Console.ReadLine();
                }
            }
            finally
            {
                // IMPORTANT: you must close the ServiceHost to stop polling
                if (serviceHost.State == CommunicationState.Opened)
                    serviceHost.Close();
                else
                    serviceHost.Abort();
            }
        }
    }
}

另请参阅

使用 WCF 服务模型轮询 Oracle 电子商务套件