将存储过程与 WCF 服务模型一起轮询 Oracle 电子商务套件

可以通过使用存储过程定期轮询 Oracle 数据库,将 Oracle 电子商务适配器配置为接收定期的数据更改消息。 可以将存储过程指定为适配器定期执行以轮询 Oracle 数据库的轮询语句。

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

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

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

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

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

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

本主题如何演示轮询

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

注意

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

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

  • PolledDataAvailableState (ment 绑定属性指定 SELECT 语句,以确定在 ACCOUNTACTIVITY) 的轮询表位置。 此示例中,可以将此绑定属性设置为:

    SELECT COUNT (*) FROM ACCOUNTACTIVITY
    

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

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

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

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

在 WCF 服务模型中配置轮询

若要将存储过程与 Oracle 电子商务适配器与 WCF 服务模型一起进行轮询,必须:

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

  • 从此接口实现 WCF 服务。

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

关于本主题中使用的示例

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

WCF 服务协定和类

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

WCF 服务协定 (接口)

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

[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 电子商务适配器接收入站轮询消息。

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

  1. 使用"添加适配器服务引用插件"为入站操作 (接口和) 类生成 WCF GET_ACTIVITYS 协定。 有关详细信息,请参阅为 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 电子商务套件凭据。

    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 的实例。 如果指定,则基本连接 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 轮询语句将所有数据从 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 电子商务套件