WCF 서비스 모델을 사용하여 Oracle 데이터베이스 변경 알림 받기1
이 항목에서는 Oracle 데이터베이스에서 쿼리 알림 메시지를 받도록 Oracle 데이터베이스 어댑터를 구성하는 방법을 보여 줍니다. 알림을 보여 주려면 "처리됨" 열이 있는 ACCOUNTACTIVITY 테이블을 고려합니다. 이 테이블에 새 레코드를 삽입하면 상태 열의 값이 'n'으로 설정됩니다. "처리됨" 열이 'n'인 모든 레코드를 검색하는 SQL 문을 사용하여 알림을 등록하여 알림을 받도록 어댑터를 구성할 수 있습니다. NotificationStatement 바인딩 속성에 대한 SQL 문을 지정하여 이 작업을 수행할 수 있습니다. 어댑터 클라이언트가 알림을 받으면 Oracle 데이터베이스에서 후속 작업을 수행하는 논리를 포함할 수 있습니다. 이 예제에서는 간단히 하기 위해 어댑터 클라이언트는 "Processed" 열이 'n'인 테이블의 모든 레코드를 나열합니다.
Oracle 데이터베이스 어댑터 바인딩 속성을 사용하여 알림 구성
아래 표에는 Oracle 데이터베이스에서 알림 수신을 구성하는 데 사용하는 Oracle 데이터베이스 어댑터 바인딩 속성이 요약됩니다. 알림을 받으려면 .NET 애플리케이션을 실행하는 동안 이러한 바인딩 속성을 지정해야 합니다.
Binding 속성 | Description |
---|---|
InboundOperationType | 수행하려는 인바운드 작업을 지정합니다. 알림 메시지를 받으려면 이를 알림으로 설정합니다. |
NotificationPort | Oracle 데이터베이스에서 데이터베이스 변경 알림을 수신 대기하기 위해 ODP.NET 열어야 하는 포트 번호를 지정합니다. |
NotificationStatement | 쿼리 알림을 등록하는 데 사용되는 SELECT 문을 지정합니다. 어댑터는 지정된 SELECT 문의 결과 집합이 변경될 때만 알림 메시지를 받습니다. |
NotifyOnListenerStart | 수신기가 시작될 때 어댑터가 어댑터 클라이언트에 알림을 보낼지 여부를 지정합니다. |
이러한 속성에 대한 자세한 설명은 Oracle Database에 대한 바인딩 속성 구성을 참조하세요. Oracle 데이터베이스 어댑터를 사용하여 Oracle 데이터베이스에서 알림을 받는 방법에 대한 전체 설명은 자세한 내용을 참조하세요.
WCF 서비스 모델을 사용하여 알림 구성
WCF 서비스 모델을 사용하여 알림을 받으려면 다음을 수행해야 합니다.
어댑터가 노출하는 메타데이터에서 알림 작업에 대한 WCF 서비스 계약(인터페이스)을 생성합니다. 이렇게 하려면 어댑터 서비스 참조 플러그 인 추가를 사용할 수 있습니다.
ACCOUNTACTIVITY 테이블에서 선택 작업에 대한 WCF 클라이언트를 생성합니다. 이렇게 하려면 어댑터 서비스 참조 플러그 인 추가를 사용할 수 있습니다.
이 인터페이스에서 WCF 서비스를 구현합니다.
서비스 호스트(System.ServiceModel.ServiceHost)를 사용하여 이 WCF 서비스를 호스트합니다.
WCF 서비스 계약 및 클래스
어댑터 서비스 참조 플러그 인 추가를 사용하여 알림 작업에 대한 WCF 서비스 계약(인터페이스) 및 지원 클래스를 만들 수 있습니다. WCF 서비스 계약을 생성하는 방법에 대한 자세한 내용은 Oracle Database 솔루션 아티팩트용 WCF 클라이언트 또는 WCF 서비스 계약 생성을 참조하세요.
WCF 서비스 계약(인터페이스)
다음 코드는 알림 작업에 대해 생성된 WCF 서비스 계약(인터페이스)을 보여 줍니다.
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
[System.ServiceModel.ServiceContractAttribute(Namespace="http://Microsoft.LobServices.OracleDB/2007/03", ConfigurationName="Notification_OperationGroup")]
public interface Notification_OperationGroup {
// CODEGEN: Generating message contract since the wrapper namespace (http://Microsoft.LobServices.OracleDB/2007/03/Notification/) of message Notification
// does not match the default value (http://Microsoft.LobServices.OracleDB/2007/03)
[System.ServiceModel.OperationContractAttribute(IsOneWay=true, Action="http://Microsoft.LobServices.OracleDB/2007/03/Notification")]
void Notification(Notification request);
}
메시지 계약
다음은 알림 작업에 대한 메시지 계약입니다.
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
[System.ServiceModel.MessageContractAttribute(WrapperName="Notification", WrapperNamespace="http://Microsoft.LobServices.OracleDB/2007/03/Notification/", IsWrapped=true)]
public partial class Notification {
[System.ServiceModel.MessageBodyMemberAttribute(Namespace="http://Microsoft.LobServices.OracleDB/2007/03/Notification/", Order=0)]
public microsoft.lobservices.oracledb._2007._03.Notification.NotificationDetails[] Details;
[System.ServiceModel.MessageBodyMemberAttribute(Namespace="http://Microsoft.LobServices.OracleDB/2007/03/Notification/", Order=1)]
public string Info;
[System.ServiceModel.MessageBodyMemberAttribute(Namespace="http://Microsoft.LobServices.OracleDB/2007/03/Notification/", Order=2)]
public string[] ResourceNames;
[System.ServiceModel.MessageBodyMemberAttribute(Namespace="http://Microsoft.LobServices.OracleDB/2007/03/Notification/", Order=3)]
public string Source;
[System.ServiceModel.MessageBodyMemberAttribute(Namespace="http://Microsoft.LobServices.OracleDB/2007/03/Notification/", Order=4)]
public string Type;
public Notification() {
}
public Notification(microsoft.lobservices.oracledb._2007._03.Notification.NotificationDetails[] Details, string Info, string[] ResourceNames, string Source, string Type) {
this.Details = Details;
this.Info = Info;
this.ResourceNames = ResourceNames;
this.Source = Source;
this.Type = Type;
}
}
WCF 서비스 클래스
어댑터 서비스 참조 추가 플러그 인은 서비스 계약(인터페이스)에서 구현된 WCF 서비스 클래스에 대한 스텁이 있는 파일도 생성합니다. 파일의 이름은 OracleDBBindingService.cs입니다. 이 클래스에 직접 알림 작업을 처리하는 논리를 삽입할 수 있습니다. 다음 코드는 어댑터 서비스 참조 플러그 인 추가에서 생성된 WCF 서비스 클래스를 보여줍니다.
namespace OracleDBBindingNamespace {
public class OracleDBBindingService : Notification_OperationGroup {
// CODEGEN: Generating message contract since the wrapper namespace (http://Microsoft.LobServices.OracleDB/2007/03/Notification/) of message Notification
// does not match the default value (http://Microsoft.LobServices.OracleDB/2007/03)
public virtual void Notification(Notification request) {
throw new System.NotImplementedException("The method or operation is not implemented.");
}
}
}
WCF 서비스 모델을 사용하여 데이터베이스 변경 알림 수신
이 섹션에서는 Oracle 데이터베이스 어댑터를 사용하여 쿼리 알림을 수신하는 .NET 애플리케이션을 작성하는 방법에 대한 지침을 제공합니다.
쿼리 알림을 받으려면
어댑터 서비스 참조 플러그 인 추가를 사용하여 ACCOUNTACTIVITY 테이블에서 선택 작업에 대한 WCF 클라이언트를 생성합니다. 이 클라이언트를 사용하여 알림 메시지를 받은 후 선택 작업을 수행합니다. 새 클래스 TableOperation.cs를 프로젝트에 추가하고 다음 코드 조각을 추가하여 선택 작업을 수행합니다.
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Notification_ServiceModel { class TableOperation { public void TableOp() { ////////////////////////////////////////////////////////////////////// // CREATING THE CLIENT AND SETTING CLIENT CREDENTIALS ////////////////////////////////////////////////////////////////////// SCOTT_Table_ACCOUNTACTIVITYClient client = new SCOTT_Table_ACCOUNTACTIVITYClient("OracleDBBinding_SCOTT_Table_ACCOUNTACTIVITY"); client.ClientCredentials.UserName.UserName = "SCOTT"; client.ClientCredentials.UserName.Password = "TIGER"; //////////////////////////////////////////////////////////////////// // OPENING THE CLIENT ////////////////////////////////////////////////////////////////////// try { Console.WriteLine("Opening the client ..."); client.Open(); } catch (Exception ex) { Console.WriteLine("Exception: " + ex.Message); throw; } //////////////////////////////////////////////////////////////////////////////////////// // SELECTING THE LAST INSERTED VALUE //////////////////////////////////////////////////////////////////////////////////////// Console.WriteLine("The application will now select the last inserted record"); microsoft.lobservices.oracledb._2007._03.SCOTT.Table.ACCOUNTACTIVITY.ACCOUNTACTIVITYRECORDSELECT[] selectRecords; try { selectRecords = client.Select("*", "WHERE PROCESSED = 'n'"); } catch (Exception ex) { Console.WriteLine("Exception: " + ex.Message); throw; } Console.WriteLine("The details of the newly added records are:"); Console.WriteLine("********************************************"); for (int i = 0; i < selectRecords.Length; i++) { Console.WriteLine("Transaction ID : " + selectRecords[i].TID); Console.WriteLine("Account ID : " + selectRecords[i].ACCOUNT); Console.WriteLine("Processed Status : " + selectRecords[i].PROCESSED); Console.WriteLine(); } Console.WriteLine("********************************************"); } } }
어댑터 서비스 참조 플러그 인 추가를 사용하여 알림 작업에 대한 WCF 서비스 계약(인터페이스) 및 도우미 클래스를 생성합니다.
자세한 내용은 Oracle Database 솔루션 아티팩트용 WCF 클라이언트 또는 WCF 서비스 계약 생성을 참조하세요. 필요에 따라 서비스 계약 및 도우미 클래스를 생성하는 동안 바인딩 속성을 지정할 수 있습니다. 이렇게 하면 생성된 구성 파일에서 올바르게 설정됩니다.
2단계에서 생성된 인터페이스 및 도우미 클래스에서 WCF 서비스를 구현합니다. 이 클래스의 Notification 메서드는 알림 작업에서 받은 데이터를 처리하는 동안 오류가 발생하면 예외를 throw 하여 작업을 중단할 수 있습니다. 그렇지 않으면 메서드는 아무것도 반환하지 않습니다. 다음과 같이 WCF 서비스 클래스의 특성을 지정해야 합니다.
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
Notification 메서드 내에서 애플리케이션 논리를 직접 구현할 수 있습니다. 이 클래스는 OracleDBBindingService.cs에서 찾을 수 있습니다. 이 예제의 이 코드는 OracleDBBindingService 클래스를 하위 클래스로 지정합니다. 이 코드에서 받은 알림 메시지는 콘솔에 기록됩니다. 또한 TableOperation 클래스 내의 TableOp 메서드가 호출되어 Select 작업을 수행합니다.
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)] public class NotificationService : OracleDBBindingNamespace.OracleDBBindingService { public override void Notification(Notification request) { Console.WriteLine("\nNew Notification Received"); Console.WriteLine("*************************************************"); Console.WriteLine(request.Info); Console.WriteLine(request.Source); Console.WriteLine(request.Type); Console.WriteLine("*************************************************"); TableOperation Ops = new TableOperation(); Ops.TableOp(); } }
Oracle 데이터베이스에 대한 자격 증명을 전달하려면 다음 클래스를 구현해야 합니다. 애플리케이션의 후반부에서는 이 클래스를 인스턴스화하여 자격 증명을 전달합니다.
class NotificationCredentials : 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 NotificationCredentials(); clone.UserName.UserName = this.UserName.UserName; clone.UserName.Password = this.UserName.Password; return clone; } }
OracleDBBinding을 만들고 바인딩 속성을 지정하여 쿼리 알림을 받도록 어댑터를 구성합니다. 코드에서 명시적으로 또는 구성에서 선언적으로 이 작업을 수행할 수 있습니다. 최소한 InboundOperationType 및 NotificationStatement 바인딩 속성을 지정해야 합니다.
OracleDBBinding binding = new OracleDBBinding(); binding.InboundOperationType = InboundOperation.Notification; binding.NotificationStatement = "SELECT TID,ACCOUNT,PROCESSED FROM APPS.ACCOUNTACTIVITY WHERE PROCESSED = 'n'"; binding.NotifyOnListenerStart = true; binding.NotificationPort = 10;
중요
NotificationPort 바인딩 속성 값은 Windows 방화벽 예외 목록에 추가해야 하는 동일한 포트 번호로 설정해야 합니다. Windows 방화벽 예외 목록에 포트를 추가하는 방법에 대한 지침은 을 참조하세요 https://go.microsoft.com/fwlink/?LinkId=196959.
중요
NotificationPort 바인딩 속성을 설정하지 않으면 어댑터는 이 바인딩 속성에 대해 기본값 -1을 가정합니다. 이러한 경우 알림 메시지를 수신하려면 Windows 방화벽을 완전히 사용하지 않도록 설정해야 합니다.
4단계에서 만든 NotificationCredentials 클래스를 인스턴스화하여 Oracle 데이터베이스 자격 증명을 지정합니다.
NotificationCredentials credentials = new NotificationCredentials(); credentials.UserName.UserName = "SCOTT"; credentials.UserName.Password = "TIGER";
3단계에서 만든 WCF 서비스의 instance 만듭니다.
// create service instance NotificationService service = new NotificationService();
WCF 서비스 및 기본 연결 URI를 사용하여 System.ServiceModel.ServiceHost의 instance 만듭니다. 또한 여기에서 자격 증명을 지정해야 합니다.
// Enable service host Uri[] baseUri = new Uri[] { new Uri("oracledb://adapter") }; ServiceHost serviceHost = new ServiceHost(service, baseUri); serviceHost.Description.Behaviors.Add(credentials);
서비스 호스트에 서비스 엔드포인트를 추가합니다. 가상 하드 디스크 파일에 대한 중요 정보를 제공하려면
5단계에서 만든 바인딩을 사용합니다.
자격 증명을 포함하는 연결 URI를 지정하고 필요한 경우 인바운드 ID를 지정합니다.
계약을 "Notification_OperationGroup"로 지정합니다.
// Add service endpoint: be sure to specify Notification_OperationGroup as the contract Uri ConnectionUri = new Uri("oracledb://adapter"); serviceHost.AddServiceEndpoint("Notification_OperationGroup", binding, ConnectionUri);
알림 메시지를 받으려면 서비스 호스트를 엽니다.
// Open the service host to begin receiving notifications serviceHost.Open();
알림 수신을 중지하려면 서비스 호스트를 닫습니다.
serviceHost.Close();
예제
다음 예제에서는 ACCOUNTACTIVITY 테이블에 대한 알림 메시지를 수신하는 .NET 애플리케이션을 보여줍니다.
참고
다음 코드 조각은 TableOperation.cs 클래스를 인스턴스화하고 TableOp 메서드를 호출합니다. 클래스와 메서드는 1단계에서 설명합니다.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Adapters.OracleDB;
using Microsoft.ServiceModel.Channels;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.ServiceModel.Channels;
using System.Collections.ObjectModel;
namespace Notification_ServiceModel
{
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class NotificationService : OracleDBBindingNamespace.OracleDBBindingService
{
public override void Notification(Notification request)
{
Console.WriteLine("\nNew Notification Received");
Console.WriteLine("*************************************************");
Console.WriteLine(request.Info);
Console.WriteLine(request.Source);
Console.WriteLine(request.Type);
Console.WriteLine("*************************************************");
TableOperation Ops = new TableOperation();
Ops.TableOp();
}
}
class NotificationCredentials : 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 NotificationCredentials();
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 receiving notifications...");
Console.ReadLine();
OracleDBBinding binding = new OracleDBBinding();
binding.InboundOperationType = InboundOperation.Notification;
binding.NotificationStatement = "SELECT TID,ACCOUNT,PROCESSED FROM APPS.ACCOUNTACTIVITY WHERE PROCESSED = 'n'";
binding.NotifyOnListenerStart = true;
binding.NotificationPort = 10;
// 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("oracledb://adapter");
// 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("oracledb://adapter") };
NotificationCredentials credentials = new NotificationCredentials();
credentials.UserName.UserName = "SCOTT";
credentials.UserName.Password = "TIGER";
Console.WriteLine("Opening service host...");
NotificationService service = new NotificationService();
serviceHost = new ServiceHost(service, baseUri);
serviceHost.Description.Behaviors.Add(credentials);
serviceHost.AddServiceEndpoint("Notification_OperationGroup", binding, ConnectionUri);
serviceHost.Open();
Console.WriteLine("Service host opened...");
Console.WriteLine("Waiting for notification...");
Console.WriteLine("\nHit <RETURN> to stop receiving notification");
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();
}
}
}
}