使用存储过程和 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 适配器接收入站轮询消息。
使用存储过程接收轮询消息
使用添加适配器服务引用插件为 GET_ACTIVITYS入站操作 生成 WCF 服务协定 (接口) 和帮助程序类。 有关详细信息,请参阅 为 Oracle E-Business Suite 解决方案项目生成 WCF 客户端或 WCF 服务协定。 可以选择在生成服务协定和帮助程序类时指定绑定属性。 这可以保证在生成的配置文件中正确设置它们。
从步骤 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"); } }
必须实现以下类,以避免将凭据作为 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; } }
创建 OracleEBSBinding 并通过指定绑定属性来配置轮询操作。 可以在代码中显式执行此操作,也可以在配置中以声明方式执行此操作。 至少必须指定 InboundOperationType、 PolledDataAvailableStatement、 PollingInput 和 PollingAction 绑定属性。
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 存储过程作为出站操作的请求消息。
重要
在此示例中,由于轮询数据库表,因此无需设置应用程序上下文。 但是,如果要轮询接口表,则必须通过指定 OracleUserName、 OraclePassword 和 OracleEBSResponsibilityName 绑定属性来设置应用程序上下文。 有关应用程序上下文的详细信息,请参阅 设置应用程序上下文。
通过实例化在步骤 3 中创建的 PollingCredentials 类来指定 Oracle 电子商务套件凭据。
PollingCredentials credentials = new PollingCredentials(); credentials.UserName.UserName = "<Enter user name here>"; credentials.UserName.Password = "<Enter password here>";
创建在步骤 2 中创建的 WCF 服务的实例。
// create service instance PollingService service = new PollingService();
使用 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);
将服务终结点添加到服务主机。 为此,请按以下步骤操作:
使用在步骤 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);
若要接收轮询数据,请打开服务主机。 每当查询返回结果集时,适配器都将返回数据。
// Open the service host to begin polling serviceHost.Open();
若要终止轮询,请关闭服务主机。
重要
适配器将继续轮询,直到服务主机关闭。
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();
}
}
}
}