使用 WCF 服务模型的存储过程轮询 Oracle 电子商务套件

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

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

使用 Oracle 电子商务适配器绑定属性配置轮询操作

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

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

必须为 PollingInput 绑定属性指定一个值才能启用轮询。 仅当有数据可用于轮询时,才会执行轮询语句,该数据由 PolledDataAvailableStatement 绑定属性确定。
PollingAction 指定轮询操作的动作。 可以使用“添加适配器服务引用 Visual Studio 插件”从为操作生成的服务接口确定轮询操作。
投票后声明 指定在执行 PollingInput 绑定属性指定的语句后执行的语句块。
PollWhileDataFound 指定 Oracle E-Business 适配器是否忽略轮询间隔并连续执行轮询语句(如果正在轮询的表中的数据可用)。 如果表中没有可用数据,适配器将还原为按指定的轮询间隔执行轮询语句。 默认值为 false。

有关这些属性的更完整说明,请参阅 有关 Oracle 电子商务套件绑定属性的 BizTalk 适配器的信息。 要了解如何使用 Oracle 电子商务适配器进行轮询的详细说明,请阅读以下章节。

本主题如何演示轮询

在本主题中,为了演示 Oracle E-Business 适配器如何支持使用存储过程接收数据更改消息,请使用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 电子商务适配器和 WCF 服务模型进行存储过程轮询,必须:

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

  • 从此接口实现 WCF 服务。

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

关于本主题中使用的示例

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

WCF 服务协定和类

可以使用“添加适配器服务引用插件”来为 GET_ACTIVITYS 入站操作创建 WCF 服务契约(接口)和支持类。 有关生成 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_ACTIVITIES 操作。 以下代码显示了由“添加适配器服务引用插件”生成的 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 电子商务套件解决方案项目生成 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 E-Business Suite 凭据。

    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 表读取所有数据,而 post poll 语句会将所有数据从 ACCOUNTACTIVITY 移动到 ACTIVITYHISTORY 表。

第一条轮询消息提供了 ACCOUNTACTIVITY 表中的所有记录。 后续轮询消息将不包含任何记录,因为轮询后语句会删除这些记录。 在向 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 电子商务套件