병합 아티클에 대한 비즈니스 논리 처리기 구현Implement a Business Logic Handler for a Merge Article

이 항목에서는 복제 프로그래밍 또는 RMO(복제 관리 개체)를 사용하여 SQL Server 2017SQL Server 2017 의 병합 아티클에 대한 비즈니스 논리 처리기를 구현하는 방법에 대해 설명합니다.This topic describes how to implement a business logic handler for a merge article in SQL Server 2017SQL Server 2017 by using replication programming or Replication Management Objects (RMO).

Microsoft.SqlServer.Replication.BusinessLogicSupport 네임스페이스는 병합 복제 동기화 프로세스 중에 발생하는 이벤트를 처리하는 복잡한 비즈니스 논리를 작성할 수 있게 해주는 인터페이스를 구현합니다.The Microsoft.SqlServer.Replication.BusinessLogicSupport namespace implements an interface that enables you to write complex business logic to handle events that occur during the merge replication synchronization process. 비즈니스 논리 처리기의 메서드는 동기화 중에 복제되는 각 변경된 행에 대해 복제 프로세스에서 호출할 수 있습니다.Methods in the business logic handler can be invoked by the replication process for each changed row that is replicated during synchronization.

비즈니스 논리 처리기 구현을 위한 일반적인 프로세스는 다음과 같습니다.The general process for implementing a business logic handler is:

  1. 비즈니스 논리 처리기 어셈블리를 만듭니다.Create the business logic hander assembly.

  2. 어셈블리를 배포자에 등록합니다.Register the assembly at the Distributor.

  3. 병합 에이전트가 실행되는 서버에 어셈블리를 배포합니다.Deploy the assembly at the server on which the Merge Agent runs. 끌어오기 구독의 경우 에이전트가 구독자에서 실행되고 밀어넣기 구독의 경우에는 배포자에서 실행됩니다.For a pull subscription the agent runs on the Subscriber, and for a push subscription the agent runs on the Distributor. 웹 동기화를 사용할 경우 에이전트는 웹 서버에서 실행됩니다.When you are using Web synchronization, the agent runs on the Web server.

  4. 비즈니스 논리 처리기를 사용하는 아티클을 만들거나 기존 아티클을 수정하여 비즈니스 논리 처리기를 사용하도록 합니다.Create an article that uses the business logic handler or modify an existing article to use the business logic handler.

    사용자가 지정하는 비즈니스 논리 처리기는 동기화되는 모든 행에 대해 실행됩니다.The business logic handler you specify is executed for every row that is synchronized. 네트워크 서비스 또는 다른 응용 프로그램에 대한 호출과 복잡한 논리가 성능에 영향을 줄 수 있습니다.Complex logic and calls to other applications or network services can impact performance. 비즈니스 논리 처리기에 대한 자세한 내용은 병합 동기화 중 비즈니스 논리 실행을 참조하세요.For more information about business logic handlers, see Execute Business Logic During Merge Synchronization.

    항목 내용In This Topic

복제 프로그래밍 사용 Using Replication Programming

비즈니스 논리 처리기를 만들고 배포하려면To create and deploy a business logic handler

  1. MicrosoftMicrosoft Visual Studio에서 비즈니스 논리 처리기를 구현하는 코드를 포함하는 .NET 어셈블리에 대한 새 프로젝트를 만듭니다.In MicrosoftMicrosoft Visual Studio, create a new project for the .NET assembly that contains the code that implements the business logic handler.

  2. 다음 네임스페이스에 대해 이 프로젝트에 대한 참조를 추가합니다.Add references to the project for the following namespaces.

    어셈블리 참조Assembly Reference 위치Location
    Microsoft.SqlServer.Replication.BusinessLogicSupport C:\Program Files\Microsoft SQL Server\130\C:\Program Files\Microsoft SQL Server\130\COM(기본 설치)COM (default installation)
    System.Data GAC(.NET Framework 구성 요소)GAC (component of the .NET Framework)
    System.Data.Common GAC(.NET Framework 구성 요소)GAC (component of the .NET Framework)
  3. BusinessLogicModule 클래스를 덮어쓰는 클래스를 추가합니다.Add a class that overrides the BusinessLogicModule class.

  4. HandledChangeStates 속성을 구현하여 처리된 변경 유형을 나타냅니다.Implement the HandledChangeStates property to indicate the types of changes that are handled.

  5. BusinessLogicModule 클래스의 다음 메서드 중 하나 이상을 덮어씁니다.Override one or more of the following methods of the BusinessLogicModule class:

    • CommitHandler - 동기화 중에 데이터 변경이 커밋되는 경우 호출됩니다.CommitHandler - invoked when a data change is committed during synchronization.

    • DeleteErrorHandler - DELETE 문이 업로드 또는 다운로드될 때 오류가 발생하는 경우 호출됩니다.DeleteErrorHandler - invoked when an error occurs when a DELETE statement is being uploaded or downloaded.

    • DeleteHandler - DELETE 문이 업로드 또는 다운로드될 때 호출됩니다.DeleteHandler - invoked when DELETE statements are being uploaded or downloaded.

    • InsertErrorHandler - INSERT 문이 업로드 또는 다운로드될 때 오류가 발생하는 경우 호출됩니다.InsertErrorHandler - invoked when an error occurs when an INSERT statement is being uploaded or downloaded.

    • InsertHandler - INSERT 문이 업로드 또는 다운로드될 때 호출됩니다.InsertHandler - invoked when INSERT statements are being uploaded or downloaded.

    • UpdateConflictsHandler - 게시자와 구독자에서 UPDATE 문이 충돌하는 경우 호출됩니다.UpdateConflictsHandler - invoked when conflicting UPDATE statements occur at the Publisher and Subscriber.

    • UpdateDeleteConflictHandler - 게시자와 구독자에서 UPDATE 문이 DELETE 문과 충돌하는 경우 호출됩니다.UpdateDeleteConflictHandler - invoked when UPDATE statements conflict with DELETE statements at the Publisher and Subscriber.

    • UpdateErrorHandler - UPDATE 문이 업로드 또는 다운로드될 때 오류가 발생하는 경우 호출됩니다.UpdateErrorHandler - invoked when an error occurs when an UPDATE statement is being uploaded or downloaded.

    • UpdateHandler - UPDATE 문이 업로드 또는 다운로드될 때 호출됩니다.UpdateHandler - invoked when UPDATE statements are being uploaded or downloaded.

  6. 프로젝트를 빌드하여 비즈니스 논리 처리기 어셈블리를 만듭니다.Build the project to create the business logic handler assembly.

  7. 어셈블리를 병합 에이전트 실행 파일(replmerg.exe)이 있는 디렉터리(기본 설치의 경우 C:\Program Files\Microsoft SQL Server\130\C:\Program Files\Microsoft SQL Server\130\COM)에 배포하거나 .NET GAC(전역 어셈블리 캐시)에 설치합니다.Deploy the assembly in the directory that contains the Merge Agent executable file (replmerg.exe), which for a default installation is C:\Program Files\Microsoft SQL Server\130\C:\Program Files\Microsoft SQL Server\130\COM, or install it in the .NET global assembly cache (GAC). 병합 에이전트 이외의 다른 응용 프로그램에서 어셈블리에 액세스해야 하는 경우 어셈블리를 GAC에만 설치해야 합니다.You should only install the assembly in the GAC if applications other than the Merge Agent require access to the assembly. .NET Framework SDK에 제공되는 전역 어셈블리 캐시 도구(Gacutil.exe )를 사용하여 GAC에 어셈블리를 설치할 수 있습니다.The assembly can be installed into the GAC using the Global Assembly Cache tool (Gacutil.exe) provided in the .NET Framework SDK.

    참고

    비즈니스 논리 처리기는 병합 에이전트가 실행되는 모든 서버에 배포해야 합니다. 여기에는 웹 동기화를 사용할 때 replisapi.dll을 호스팅하는 IIS 서버도 포함됩니다.A business logic handler must be deployed on every server on which the Merge Agent runs, which includes the IIS server that hosts the replisapi.dll when using Web synchronization.

비즈니스 논리 처리기를 등록하려면To register a business logic handler

  1. 게시자에서 sp_enumcustomresolvers(Transact-SQL)를 실행하여 어셈블리가 이미 비즈니스 논리 처리기로 등록되어 있지 않은지 확인합니다.At the Publisher, execute sp_enumcustomresolvers (Transact-SQL) to verify that the assembly has not already been registered as a business logic handler.

  2. @article_resolver에 비즈니스 논리 처리기의 이름, @is_dotnet_assemblytrue 값, @dotnet_assembly_name에 어셈블리 이름, @dotnet_class_nameBusinessLogicModule을 재정의하는 클래스의 정규화된 이름을 지정하여 sp_registercustomresolver(Transact-SQL)를 실행합니다.At the Distributor, execute sp_registercustomresolver (Transact-SQL), specifying a friendly name for the business logic handler for @article_resolver, a value of true for @is_dotnet_assembly, the name of the assembly for @dotnet_assembly_name, and the fully-qualified name of the class that overrides BusinessLogicModule for @dotnet_class_name.

    참고

    어셈블리가 병합 에이전트 실행 파일과 같은 디렉터리, 병합 에이전트를 동기적으로 시작하는 응용 프로그램과 같은 디렉터리, 또는 GAC(전역 어셈블리 캐시)에 배포되지 않은 경우 @dotnet_assembly_name을 참조하세요.If the assembly is not deployed in the same directory as the Merge Agent executable, in the same directory as the application that synchronously starts the Merge Agent, or in the global assembly cache (GAC), you need to specify the full path with the assembly name for @dotnet_assembly_name. 웹 동기화를 사용하는 경우 웹 서버에서 어셈블리의 위치를 지정해야 합니다.When using Web synchronization, you must specify the location of assembly at the Web server.

새 테이블 아티클에서 비즈니스 논리 처리기를 사용하려면To use a business logic handler with a new table article

  1. @article_resolver에 비즈니스 논리 처리기의 이름을 지정하여 sp_addmergearticle(Transact-SQL)을 실행하여 아티클을 정의합니다.Execute sp_addmergearticle (Transact-SQL) to define an article, specifying the friendly name of the business logic handler for @article_resolver. 자세한 내용은 Define an Article을 참조하세요.For more information, see Define an Article.

기존 테이블 아티클에서 비즈니스 논리 처리기를 사용하려면To use a business logic handler with an existing table article

  1. @publication, @article, @propertyarticle_resolver 값, @value에 비즈니스 논리 처리기의 이름을 지정하여 sp_changemergearticle(Transact-SQL)을 실행합니다.Execute sp_changemergearticle (Transact-SQL), specifying @publication, @article, a value of article_resolver for @property, and the friendly name of the business logic handler for @value.

예(복제 프로그래밍) Examples (Replication Programming)

이 예에서는 감사 로그를 만드는 비즈니스 논리 처리기를 보여 줍니다.This example shows a business logic handler that creates an audit log.

using System;
using System.Text;
using System.Data;
using System.Data.Common;
using Microsoft.SqlServer.Replication.BusinessLogicSupport;
using Microsoft.Samples.SqlServer.BusinessLogicHandler;

namespace Microsoft.Samples.SqlServer.BusinessLogicHandler
{
	public class OrderEntryBusinessLogicHandler :
	  Microsoft.SqlServer.Replication.BusinessLogicSupport.BusinessLogicModule
	{
		// Variables to hold server names.
		private string publisherName;
		private string subscriberName;

		public OrderEntryBusinessLogicHandler()
		{
		}

		// Implement the Initialize method to get publication 
		// and subscription information.
		public override void Initialize(
			string publisher,
			string subscriber,
			string distributor,
			string publisherDB,
			string subscriberDB,
			string articleName)
		{
			// Set the Publisher and Subscriber names.
			publisherName = publisher;
			subscriberName = subscriber;
		}

		// Declare what types of row changes, conflicts, or errors to handle.
		override public ChangeStates HandledChangeStates
		{
			get
			{
				// Handle Subscriber inserts, updates and deletes.
				return ChangeStates.SubscriberInserts |
				  ChangeStates.SubscriberUpdates | ChangeStates.SubscriberDeletes;
			}
		}

		public override ActionOnDataChange InsertHandler(SourceIdentifier insertSource,
		  DataSet insertedDataSet, ref DataSet customDataSet, ref int historyLogLevel,
		  ref string historyLogMessage)
		{
			if (insertSource == SourceIdentifier.SourceIsSubscriber)
			{
				// Build a line item in the audit message to log the Subscriber insert.
				StringBuilder AuditMessage = new StringBuilder();
				AuditMessage.Append(String.Format("A new order was entered at {0}. " +
				  "The SalesOrderID for the order is :", subscriberName));
				AuditMessage.Append(insertedDataSet.Tables[0].Rows[0]["SalesOrderID"].ToString());
				AuditMessage.Append("The order must be shipped by :");
				AuditMessage.Append(insertedDataSet.Tables[0].Rows[0]["DueDate"].ToString());

				// Set the reference parameter to write the line to the log file.
				historyLogMessage = AuditMessage.ToString();
				
				// Set the history log level to the default verbose level.
				historyLogLevel = 1;

				// Accept the inserted data in the Subscriber's data set and 
				// apply it to the Publisher.
				return ActionOnDataChange.AcceptData;
			}
			else
			{
				return base.InsertHandler(insertSource, insertedDataSet, ref customDataSet,
				  ref historyLogLevel, ref historyLogMessage);
			}
		}

		public override ActionOnDataChange UpdateHandler(SourceIdentifier updateSource,
		  DataSet updatedDataSet, ref DataSet customDataSet, ref int historyLogLevel,
		  ref string historyLogMessage)
		{
			if (updateSource == SourceIdentifier.SourceIsPublisher)
			{
				// Build a line item in the audit message to log the Subscriber update.
				StringBuilder AuditMessage = new StringBuilder();
				AuditMessage.Append(String.Format("An existing order was updated at {0}. " +
				  "The SalesOrderID for the order is ", subscriberName));
				AuditMessage.Append(updatedDataSet.Tables[0].Rows[0]["SalesOrderID"].ToString());
				AuditMessage.Append("The order must now be shipped by :");
				AuditMessage.Append(updatedDataSet.Tables[0].Rows[0]["DueDate"].ToString());

				// Set the reference parameter to write the line to the log file.
				historyLogMessage = AuditMessage.ToString();
				// Set the history log level to the default verbose level.
				historyLogLevel = 1;

				// Accept the updated data in the Subscriber's data set and apply it to the Publisher.
				return ActionOnDataChange.AcceptData;
			}
			else
			{
				return base.UpdateHandler(updateSource, updatedDataSet,
				  ref customDataSet, ref historyLogLevel, ref historyLogMessage);
			}
		}

		public override ActionOnDataDelete DeleteHandler(SourceIdentifier deleteSource,
		  DataSet deletedDataSet, ref int historyLogLevel, ref string historyLogMessage)
		{
			if (deleteSource == SourceIdentifier.SourceIsSubscriber)
			{
				// Build a line item in the audit message to log the Subscriber deletes.
				// Note that the rowguid is the only information that is 
				// available in the dataset.
				StringBuilder AuditMessage = new StringBuilder();
				AuditMessage.Append(String.Format("An existing order was deleted at {0}. " +
				  "The rowguid for the order is ", subscriberName));
				AuditMessage.Append(deletedDataSet.Tables[0].Rows[0]["rowguid"].ToString());

				// Set the reference parameter to write the line to the log file.
				historyLogMessage = AuditMessage.ToString();
				// Set the history log level to the default verbose level.
				historyLogLevel = 1;

				// Accept the delete and apply it to the Publisher.
				return ActionOnDataDelete.AcceptDelete;
			}
			else
			{
				return base.DeleteHandler(deleteSource, deletedDataSet,
				  ref historyLogLevel, ref historyLogMessage);
			}
		}
	}
}
using System;
using System.Text;
using System.Data;
using System.Data.Common;
using Microsoft.SqlServer.Replication.BusinessLogicSupport;
using Microsoft.Samples.SqlServer.BusinessLogicHandler;

namespace Microsoft.Samples.SqlServer.BusinessLogicHandler
{
	public class OrderEntryBusinessLogicHandler :
	  Microsoft.SqlServer.Replication.BusinessLogicSupport.BusinessLogicModule
	{
		// Variables to hold server names.
		private string publisherName;
		private string subscriberName;

		public OrderEntryBusinessLogicHandler()
		{
		}

		// Implement the Initialize method to get publication 
		// and subscription information.
		public override void Initialize(
			string publisher,
			string subscriber,
			string distributor,
			string publisherDB,
			string subscriberDB,
			string articleName)
		{
			// Set the Publisher and Subscriber names.
			publisherName = publisher;
			subscriberName = subscriber;
		}

		// Declare what types of row changes, conflicts, or errors to handle.
		override public ChangeStates HandledChangeStates
		{
			get
			{
				// Handle Subscriber inserts, updates and deletes.
				return ChangeStates.SubscriberInserts |
				  ChangeStates.SubscriberUpdates | ChangeStates.SubscriberDeletes;
			}
		}

		public override ActionOnDataChange InsertHandler(SourceIdentifier insertSource,
		  DataSet insertedDataSet, ref DataSet customDataSet, ref int historyLogLevel,
		  ref string historyLogMessage)
		{
			if (insertSource == SourceIdentifier.SourceIsSubscriber)
			{
				// Build a line item in the audit message to log the Subscriber insert.
				StringBuilder AuditMessage = new StringBuilder();
				AuditMessage.Append(String.Format("A new order was entered at {0}. " +
				  "The SalesOrderID for the order is :", subscriberName));
				AuditMessage.Append(insertedDataSet.Tables[0].Rows[0]["SalesOrderID"].ToString());
				AuditMessage.Append("The order must be shipped by :");
				AuditMessage.Append(insertedDataSet.Tables[0].Rows[0]["DueDate"].ToString());

				// Set the reference parameter to write the line to the log file.
				historyLogMessage = AuditMessage.ToString();
				
				// Set the history log level to the default verbose level.
				historyLogLevel = 1;

				// Accept the inserted data in the Subscriber's data set and 
				// apply it to the Publisher.
				return ActionOnDataChange.AcceptData;
			}
			else
			{
				return base.InsertHandler(insertSource, insertedDataSet, ref customDataSet,
				  ref historyLogLevel, ref historyLogMessage);
			}
		}

		public override ActionOnDataChange UpdateHandler(SourceIdentifier updateSource,
		  DataSet updatedDataSet, ref DataSet customDataSet, ref int historyLogLevel,
		  ref string historyLogMessage)
		{
			if (updateSource == SourceIdentifier.SourceIsPublisher)
			{
				// Build a line item in the audit message to log the Subscriber update.
				StringBuilder AuditMessage = new StringBuilder();
				AuditMessage.Append(String.Format("An existing order was updated at {0}. " +
				  "The SalesOrderID for the order is ", subscriberName));
				AuditMessage.Append(updatedDataSet.Tables[0].Rows[0]["SalesOrderID"].ToString());
				AuditMessage.Append("The order must now be shipped by :");
				AuditMessage.Append(updatedDataSet.Tables[0].Rows[0]["DueDate"].ToString());

				// Set the reference parameter to write the line to the log file.
				historyLogMessage = AuditMessage.ToString();
				// Set the history log level to the default verbose level.
				historyLogLevel = 1;

				// Accept the updated data in the Subscriber's data set and apply it to the Publisher.
				return ActionOnDataChange.AcceptData;
			}
			else
			{
				return base.UpdateHandler(updateSource, updatedDataSet,
				  ref customDataSet, ref historyLogLevel, ref historyLogMessage);
			}
		}

		public override ActionOnDataDelete DeleteHandler(SourceIdentifier deleteSource,
		  DataSet deletedDataSet, ref int historyLogLevel, ref string historyLogMessage)
		{
			if (deleteSource == SourceIdentifier.SourceIsSubscriber)
			{
				// Build a line item in the audit message to log the Subscriber deletes.
				// Note that the rowguid is the only information that is 
				// available in the dataset.
				StringBuilder AuditMessage = new StringBuilder();
				AuditMessage.Append(String.Format("An existing order was deleted at {0}. " +
				  "The rowguid for the order is ", subscriberName));
				AuditMessage.Append(deletedDataSet.Tables[0].Rows[0]["rowguid"].ToString());

				// Set the reference parameter to write the line to the log file.
				historyLogMessage = AuditMessage.ToString();
				// Set the history log level to the default verbose level.
				historyLogLevel = 1;

				// Accept the delete and apply it to the Publisher.
				return ActionOnDataDelete.AcceptDelete;
			}
			else
			{
				return base.DeleteHandler(deleteSource, deletedDataSet,
				  ref historyLogLevel, ref historyLogMessage);
			}
		}
	}
}

Imports System
Imports System.Text
Imports System.Data
Imports System.Data.Common
Imports Microsoft.SqlServer.Replication.BusinessLogicSupport

Namespace Microsoft.Samples.SqlServer.BusinessLogicHandler
    Public Class OrderEntryBusinessLogicHandler
        Inherits BusinessLogicModule

        ' Variables to hold server names.
        Private publisherName As String
        Private subscriberName As String

        ' Implement the Initialize method to get publication 
        ' and subscription information.
        Public Overrides Sub Initialize( _
        ByVal publisher As String, _
        ByVal subscriber As String, _
        ByVal distributor As String, _
        ByVal publisherDB As String, _
        ByVal subscriberDB As String, _
        ByVal articleName As String _
      )
            ' Set the Publisher and Subscriber names.
            publisherName = publisher
            subscriberName = subscriber
        End Sub

        ' Declare what types of row changes, conflicts, or errors to handle.
        Public Overrides ReadOnly Property HandledChangeStates() As ChangeStates
            Get
                ' Handle Subscriber inserts, updates and deletes.
                Return (ChangeStates.SubscriberInserts Or _
                 ChangeStates.SubscriberUpdates Or ChangeStates.SubscriberDeletes)
            End Get
        End Property

        Public Overrides Function InsertHandler(ByVal insertSource As SourceIdentifier, _
        ByVal insertedDataSet As DataSet, ByRef customDataSet As DataSet, _
        ByRef historyLogLevel As Integer, ByRef historyLogMessage As String) _
        As ActionOnDataChange

            If insertSource = SourceIdentifier.SourceIsSubscriber Then
                ' Build a line item in the audit message to log the Subscriber insert.
                Dim AuditMessage As StringBuilder = New StringBuilder()
                AuditMessage.Append(String.Format("A new order was entered at {0}. " + _
                 "The SalesOrderID for the order is :", subscriberName))
                AuditMessage.Append(insertedDataSet.Tables(0).Rows(0)("SalesOrderID").ToString())
                AuditMessage.Append("The order must be shipped by :")
                AuditMessage.Append(insertedDataSet.Tables(0).Rows(0)("DueDate").ToString())

                ' Set the reference parameter to write the line to the log file.
                historyLogMessage = AuditMessage.ToString()

                ' Set the history log level to the default verbose level.
                historyLogLevel = 1

                ' Accept the inserted data in the Subscriber's data set and 
                ' apply it to the Publisher.
                Return ActionOnDataChange.AcceptData
            Else
                Return MyBase.InsertHandler(insertSource, insertedDataSet, customDataSet, _
                 historyLogLevel, historyLogMessage)
            End If
        End Function
        Public Overrides Function UpdateHandler(ByVal updateSource As SourceIdentifier, _
        ByVal updatedDataSet As DataSet, ByRef customDataSet As DataSet, _
        ByRef historyLogLevel As Integer, ByRef historyLogMessage As String) _
        As ActionOnDataChange

            If updateSource = SourceIdentifier.SourceIsPublisher Then
                ' Build a line item in the audit message to log the Subscriber update.
                Dim AuditMessage As StringBuilder = New StringBuilder()
                AuditMessage.Append(String.Format("An existing order was updated at {0}. " + _
                 "The SalesOrderID for the order is ", subscriberName))
                AuditMessage.Append(updatedDataSet.Tables(0).Rows(0)("SalesOrderID").ToString())
                AuditMessage.Append("The order must now be shipped by :")
                AuditMessage.Append(updatedDataSet.Tables(0).Rows(0)("DueDate").ToString())

                ' Set the reference parameter to write the line to the log file.
                historyLogMessage = AuditMessage.ToString()
                ' Set the history log level to the default verbose level.
                historyLogLevel = 1

                ' Accept the updated data in the Subscriber's data set and apply it to the Publisher.
                Return ActionOnDataChange.AcceptData
            Else
                Return MyBase.UpdateHandler(updateSource, updatedDataSet, _
                 customDataSet, historyLogLevel, historyLogMessage)
            End If
        End Function
        Public Overrides Function DeleteHandler(ByVal deleteSource As SourceIdentifier, _
        ByVal deletedDataSet As DataSet, ByRef historyLogLevel As Integer, _
         ByRef historyLogMessage As String) As ActionOnDataDelete
            If deleteSource = SourceIdentifier.SourceIsSubscriber Then
                ' Build a line item in the audit message to log the Subscriber deletes.
                ' Note that the rowguid is the only information that is 
                ' available in the dataset.
                Dim AuditMessage As StringBuilder = New StringBuilder()
                AuditMessage.Append(String.Format("An existing order was deleted at {0}. " + _
                 "The rowguid for the order is ", subscriberName))
                AuditMessage.Append(deletedDataSet.Tables(0).Rows(0)("rowguid").ToString())

                ' Set the reference parameter to write the line to the log file.
                historyLogMessage = AuditMessage.ToString()
                ' Set the history log level to the default verbose level.
                historyLogLevel = 1

                ' Accept the delete and apply it to the Publisher.
                Return ActionOnDataDelete.AcceptDelete
            Else
                Return MyBase.DeleteHandler(deleteSource, deletedDataSet, _
                 historyLogLevel, historyLogMessage)
            End If
        End Function
    End Class
End Namespace
Imports System
Imports System.Text
Imports System.Data
Imports System.Data.Common
Imports Microsoft.SqlServer.Replication.BusinessLogicSupport

Namespace Microsoft.Samples.SqlServer.BusinessLogicHandler
    Public Class OrderEntryBusinessLogicHandler
        Inherits BusinessLogicModule

        ' Variables to hold server names.
        Private publisherName As String
        Private subscriberName As String

        ' Implement the Initialize method to get publication 
        ' and subscription information.
        Public Overrides Sub Initialize( _
        ByVal publisher As String, _
        ByVal subscriber As String, _
        ByVal distributor As String, _
        ByVal publisherDB As String, _
        ByVal subscriberDB As String, _
        ByVal articleName As String _
      )
            ' Set the Publisher and Subscriber names.
            publisherName = publisher
            subscriberName = subscriber
        End Sub

        ' Declare what types of row changes, conflicts, or errors to handle.
        Public Overrides ReadOnly Property HandledChangeStates() As ChangeStates
            Get
                ' Handle Subscriber inserts, updates and deletes.
                Return (ChangeStates.SubscriberInserts Or _
                 ChangeStates.SubscriberUpdates Or ChangeStates.SubscriberDeletes)
            End Get
        End Property

        Public Overrides Function InsertHandler(ByVal insertSource As SourceIdentifier, _
        ByVal insertedDataSet As DataSet, ByRef customDataSet As DataSet, _
        ByRef historyLogLevel As Integer, ByRef historyLogMessage As String) _
        As ActionOnDataChange

            If insertSource = SourceIdentifier.SourceIsSubscriber Then
                ' Build a line item in the audit message to log the Subscriber insert.
                Dim AuditMessage As StringBuilder = New StringBuilder()
                AuditMessage.Append(String.Format("A new order was entered at {0}. " + _
                 "The SalesOrderID for the order is :", subscriberName))
                AuditMessage.Append(insertedDataSet.Tables(0).Rows(0)("SalesOrderID").ToString())
                AuditMessage.Append("The order must be shipped by :")
                AuditMessage.Append(insertedDataSet.Tables(0).Rows(0)("DueDate").ToString())

                ' Set the reference parameter to write the line to the log file.
                historyLogMessage = AuditMessage.ToString()

                ' Set the history log level to the default verbose level.
                historyLogLevel = 1

                ' Accept the inserted data in the Subscriber's data set and 
                ' apply it to the Publisher.
                Return ActionOnDataChange.AcceptData
            Else
                Return MyBase.InsertHandler(insertSource, insertedDataSet, customDataSet, _
                 historyLogLevel, historyLogMessage)
            End If
        End Function
        Public Overrides Function UpdateHandler(ByVal updateSource As SourceIdentifier, _
        ByVal updatedDataSet As DataSet, ByRef customDataSet As DataSet, _
        ByRef historyLogLevel As Integer, ByRef historyLogMessage As String) _
        As ActionOnDataChange

            If updateSource = SourceIdentifier.SourceIsPublisher Then
                ' Build a line item in the audit message to log the Subscriber update.
                Dim AuditMessage As StringBuilder = New StringBuilder()
                AuditMessage.Append(String.Format("An existing order was updated at {0}. " + _
                 "The SalesOrderID for the order is ", subscriberName))
                AuditMessage.Append(updatedDataSet.Tables(0).Rows(0)("SalesOrderID").ToString())
                AuditMessage.Append("The order must now be shipped by :")
                AuditMessage.Append(updatedDataSet.Tables(0).Rows(0)("DueDate").ToString())

                ' Set the reference parameter to write the line to the log file.
                historyLogMessage = AuditMessage.ToString()
                ' Set the history log level to the default verbose level.
                historyLogLevel = 1

                ' Accept the updated data in the Subscriber's data set and apply it to the Publisher.
                Return ActionOnDataChange.AcceptData
            Else
                Return MyBase.UpdateHandler(updateSource, updatedDataSet, _
                 customDataSet, historyLogLevel, historyLogMessage)
            End If
        End Function
        Public Overrides Function DeleteHandler(ByVal deleteSource As SourceIdentifier, _
        ByVal deletedDataSet As DataSet, ByRef historyLogLevel As Integer, _
         ByRef historyLogMessage As String) As ActionOnDataDelete
            If deleteSource = SourceIdentifier.SourceIsSubscriber Then
                ' Build a line item in the audit message to log the Subscriber deletes.
                ' Note that the rowguid is the only information that is 
                ' available in the dataset.
                Dim AuditMessage As StringBuilder = New StringBuilder()
                AuditMessage.Append(String.Format("An existing order was deleted at {0}. " + _
                 "The rowguid for the order is ", subscriberName))
                AuditMessage.Append(deletedDataSet.Tables(0).Rows(0)("rowguid").ToString())

                ' Set the reference parameter to write the line to the log file.
                historyLogMessage = AuditMessage.ToString()
                ' Set the history log level to the default verbose level.
                historyLogLevel = 1

                ' Accept the delete and apply it to the Publisher.
                Return ActionOnDataDelete.AcceptDelete
            Else
                Return MyBase.DeleteHandler(deleteSource, deletedDataSet, _
                 historyLogLevel, historyLogMessage)
            End If
        End Function
    End Class
End Namespace

다음 예에서는 배포자에 비즈니스 논리 처리기 어셈블리를 등록하고 기존 병합 아티클을 변경하여 이 사용자 지정 비즈니스 논리를 사용하도록 합니다.The following example registers a business logic handler assembly at the Distributor and changes an existing merge article to use this custom business logic.

DECLARE @publication AS sysname;
DECLARE @article AS sysname;
DECLARE @friendlyname AS sysname;
DECLARE @assembly AS nvarchar(500);
DECLARE @class AS sysname;
SET @publication = N'AdvWorksCustomers';
SET @article = N'Customers';
SET @friendlyname = N'OrderEntryLogic';
SET @assembly = N'C:\Program Files\Microsoft SQL Server\120\COM\CustomLogic.dll';
SET @class = N'Microsoft.Samples.SqlServer.BusinessLogicHandler.OrderEntryBusinessLogicHandler';

-- Register the business logic handler at the Distributor.
EXEC sys.sp_registercustomresolver 
	@article_resolver = @friendlyname,
	@resolver_clsid = NULL,
	@is_dotnet_assembly = N'true',
	@dotnet_assembly_name = @assembly,
	@dotnet_class_name = @class;

-- Add an article that uses the business logic handler
-- at the Publisher.
EXEC sp_changemergearticle 
	@publication = @publication, 
	@article = @article,
	@property = N'article_resolver', 
	@value = @friendlyname,
	@force_invalidate_snapshot = 0,
	@force_reinit_subscription = 0;
GO
DECLARE @publication AS sysname;
DECLARE @article AS sysname;
DECLARE @friendlyname AS sysname;
DECLARE @assembly AS nvarchar(500);
DECLARE @class AS sysname;
SET @publication = N'AdvWorksCustomers';
SET @article = N'Customers';
SET @friendlyname = N'OrderEntryLogic';
SET @assembly = N'C:\Program Files\Microsoft SQL Server\120\COM\CustomLogic.dll';
SET @class = N'Microsoft.Samples.SqlServer.BusinessLogicHandler.OrderEntryBusinessLogicHandler';

-- Register the business logic handler at the Distributor.
EXEC sys.sp_registercustomresolver 
	@article_resolver = @friendlyname,
	@resolver_clsid = NULL,
	@is_dotnet_assembly = N'true',
	@dotnet_assembly_name = @assembly,
	@dotnet_class_name = @class;

-- Add an article that uses the business logic handler
-- at the Publisher.
EXEC sp_changemergearticle 
	@publication = @publication, 
	@article = @article,
	@property = N'article_resolver', 
	@value = @friendlyname,
	@force_invalidate_snapshot = 0,
	@force_reinit_subscription = 0;
GO

RMO(복제 관리 개체) 사용 Using Replication Management Objects (RMO)

비즈니스 논리 처리기를 만들려면To create a business logic handler

  1. MicrosoftMicrosoft Visual Studio에서 비즈니스 논리 처리기를 구현하는 코드를 포함하는 .NET 어셈블리에 대한 새 프로젝트를 만듭니다.In MicrosoftMicrosoft Visual Studio, create a new project for the .NET assembly that contains the code that implements the business logic handler.

  2. 다음 네임스페이스에 대해 이 프로젝트에 대한 참조를 추가합니다.Add references to the project for the following namespaces.

    어셈블리 참조Assembly Reference 위치Location
    Microsoft.SqlServer.Replication.BusinessLogicSupport C:\Program Files\Microsoft SQL Server\130\C:\Program Files\Microsoft SQL Server\130\COM(기본 설치)COM (default installation)
    System.Data GAC(.NET Framework 구성 요소)GAC (component of the .NET Framework)
    System.Data.Common GAC(.NET Framework 구성 요소)GAC (component of the .NET Framework)
  3. BusinessLogicModule 클래스를 덮어쓰는 클래스를 추가합니다.Add a class that overrides the BusinessLogicModule class.

  4. HandledChangeStates 속성을 구현하여 처리된 변경 유형을 나타냅니다.Implement the HandledChangeStates property to indicate the types of changes that are handled.

  5. BusinessLogicModule 클래스의 다음 메서드 중 하나 이상을 덮어씁니다.Override one or more of the following methods of the BusinessLogicModule class:

    • CommitHandler - 동기화 중에 데이터 변경이 커밋되는 경우 호출됩니다.CommitHandler - invoked when a data change is committed during synchronization.

    • DeleteErrorHandler - DELETE 문이 업로드 또는 다운로드되는 동안 오류가 발생하는 경우 호출됩니다.DeleteErrorHandler - invoked if an error occurs while a DELETE statement is being uploaded or downloaded.

    • DeleteHandler - DELETE 문이 업로드 또는 다운로드될 때 호출됩니다.DeleteHandler - invoked when DELETE statements are being uploaded or downloaded.

    • InsertErrorHandler - INSERT 문이 업로드 또는 다운로드될 때 오류가 발생하는 경우 호출됩니다.InsertErrorHandler - invoked if an error occurs when an INSERT statement is being uploaded or downloaded.

    • InsertHandler - INSERT 문이 업로드 또는 다운로드될 때 호출됩니다.InsertHandler - invoked when INSERT statements are being uploaded or downloaded.

    • UpdateConflictsHandler - 게시자와 구독자에서 UPDATE 문이 충돌하는 경우 호출됩니다.UpdateConflictsHandler - invoked when conflicting UPDATE statements occur at the Publisher and Subscriber.

    • UpdateDeleteConflictHandler - 게시자와 구독자에서 UPDATE 문이 DELETE 문과 충돌하는 경우 호출됩니다.UpdateDeleteConflictHandler - invoked when UPDATE statements conflict with DELETE statements at the Publisher and Subscriber.

    • UpdateErrorHandler - UPDATE 문이 업로드 또는 다운로드될 때 오류가 발생하는 경우 호출됩니다.UpdateErrorHandler - invoked if an error occurs when an UPDATE statement is being uploaded or downloaded.

    • UpdateHandler - UPDATE 문이 업로드 또는 다운로드될 때 호출됩니다.UpdateHandler - invoked when UPDATE statements are being uploaded or downloaded.

    참고

    사용자 지정 비즈니스 논리에 의해 명시적으로 처리되지 않은 모든 아티클 충돌은 아티클에 대한 기본 해결 프로그램에 의해 처리됩니다.Any article conflicts not explicitly handled by your custom business logic are handled by the default resolver for the article.

  6. 프로젝트를 빌드하여 비즈니스 논리 처리기 어셈블리를 만듭니다.Build the project to create the business logic handler assembly.

비즈니스 논리 처리기를 등록하려면To register a business logic handler

  1. ServerConnection 클래스를 사용하여 배포자 연결을 만듭니다.Create a connection to the Distributor by using the ServerConnection class.

  2. ReplicationServer 클래스의 인스턴스를 만듭니다.Create an instance of the ReplicationServer class. 1단계에서 만든 ServerConnection 을 전달합니다.Pass the ServerConnection from step 1.

  3. EnumBusinessLogicHandlers 를 호출하고 반환되는 ArrayList 개체를 검사하여 어셈블리가 아직 비즈니스 논리 처리기로 등록되지 않았는지 확인합니다.Call EnumBusinessLogicHandlers and check the returned ArrayList object to ensure that the assembly has not already been registered as a business logic handler.

  4. BusinessLogicHandler 클래스의 인스턴스를 만듭니다.Create an instance of the BusinessLogicHandler class. 다음 속성을 지정합니다.Specify the following properties:

    • DotNetAssemblyName -.NET 어셈블리의 이름.DotNetAssemblyName - the name of the .NET assembly. 어셈블리가 병합 에이전트 실행 파일과 같은 디렉터리, 병합 에이전트를 동기적으로 시작하는 응용 프로그램과 같은 디렉터리, 또는 GAC에 배포되지 않은 경우 어셈블리 이름에 전체 경로를 포함해야 합니다.If the assembly is not deployed in the same directory as the Merge Agent executable, in the same directory as the application that synchronously starts the Merge Agent, or in the GAC, you must include the full path with the assembly name. 웹 동기화에서 비즈니스 논리 처리기를 사용하는 경우 어셈블리 이름에 전체 경로를 포함해야 합니다.You must include the full path with the assembly name when using a business logic handler with Web synchronization.

    • DotNetClassName - BusinessLogicModule 을 덮어쓰고 비즈니스 논리 처리기를 구현하는 클래스의 정규화된 이름DotNetClassName - the fully-qualified name of the class that overrides BusinessLogicModule and implements the business logic handler.

    • FriendlyName - 비즈니스 논리 처리기에 액세스할 때 사용하는 이름FriendlyName - a friendly name you use when you access the business logic handler.

    • IsDotNetAssembly - @is_dotnet_assembly을 참조하세요.IsDotNetAssembly - a value of true.

비즈니스 논리 처리기를 배포하려면To deploy a business logic handler

  1. 병합 에이전트가 실행되는 서버에서 비즈니스 논리 처리기가 배포자에 등록될 때 지정된 파일 위치에 어셈블리를 배포합니다.Deploy the assembly on the server where the Merge Agent runs in the file location specified when the business logic handler was registered at the Distributor. 끌어오기 구독의 경우 에이전트가 구독자에서 실행되고 밀어넣기 구독의 경우에는 배포자에서 실행됩니다.For a pull subscription the agent runs on the Subscriber, and for a push subscription the agent runs on the Distributor. 웹 동기화를 사용할 경우 에이전트는 웹 서버에서 실행됩니다.When you are using Web synchronization, the agent runs on the Web server. 비즈니스 논리 처리기가 등록될 때 어셈블리 이름에 전체 경로를 포함하지 않은 경우 병합 에이전트 실행 파일과 같은 디렉터리, 병합 에이전트를 동기적으로 시작하는 응용 프로그램과 같은 디렉터리에 어셈블리를 배포합니다.If the full path was not included with the assembly name when the business logic handler was registered, deploy the assembly in the same directory as the Merge Agent executable, in the same directory as the application that synchronously starts the Merge Agent. 동일한 어셈블리를 사용하는 응용 프로그램이 여러 개인 경우 GAC에 어셈블리를 설치할 수 있습니다.You may install the assembly in the GAC if there are multiple applications that use the same assembly.

새 테이블 아티클에서 비즈니스 논리 처리기를 사용하려면To use a business logic handler with a new table article

  1. ServerConnection 클래스를 사용하여 게시자 연결을 만듭니다.Create a connection to the Publisher by using the ServerConnection class.

  2. MergeArticle 클래스의 인스턴스를 만듭니다.Create an instance of the MergeArticle class. 다음 속성을 설정합니다.Set the following properties:

  3. Create 메서드를 호출합니다.Call the Create method. 자세한 내용은 Define an Article을 참조하세요.For more information, see Define an Article.

기존 테이블 아티클에서 비즈니스 논리 처리기를 사용하려면To use a business logic handler with an existing table article

  1. ServerConnection 클래스를 사용하여 게시자 연결을 만듭니다.Create a connection to the Publisher by using the ServerConnection class.

  2. MergeArticle 클래스의 인스턴스를 만듭니다.Create an instance of the MergeArticle class.

  3. Name, PublicationNameDatabaseName 속성을 설정합니다.Set the Name, PublicationName, and DatabaseName properties.

  4. ConnectionContext 속성에 대해 1단계에서 만든 연결을 설정합니다.Set the connection from step 1 for the ConnectionContext property.

  5. LoadProperties 메서드를 호출하여 개체 속성을 가져옵니다.Call the LoadProperties method to get the properties of the object. 이 메서드가 false를 반환하는 경우 3단계에서 아티클 속성이 올바르게 정의되지 않았거나 아티클이 없습니다.If this method returns false, either the article properties in step 3 were defined incorrectly or the article does not exist. 자세한 내용은 View and Modify Article Properties을 참조하세요.For more information, see View and Modify Article Properties.

  6. ArticleResolver에 대한 비즈니스 논리 처리기의 이름을 설정합니다.Set the friendly name of the business logic handler for ArticleResolver. 이 이름은 비즈니스 논리 처리기를 등록할 때 지정된 FriendlyName 속성의 값입니다.This is the value of the FriendlyName property specified when registering the business logic handler.

예(RMO) Examples (RMO)

다음 예는 구독자에서의 삽입, 업데이트, 삭제에 대한 정보를 기록하는 비즈니스 논리 처리기입니다.This example is a business logic handler that logs information about inserts, updates, and deletes at the Subscriber.

using System;
using System.Text;
using System.Data;
using System.Data.Common;
using Microsoft.SqlServer.Replication.BusinessLogicSupport;
using Microsoft.Samples.SqlServer.BusinessLogicHandler;

namespace Microsoft.Samples.SqlServer.BusinessLogicHandler
{
	public class OrderEntryBusinessLogicHandler :
	  Microsoft.SqlServer.Replication.BusinessLogicSupport.BusinessLogicModule
	{
		// Variables to hold server names.
		private string publisherName;
		private string subscriberName;

		public OrderEntryBusinessLogicHandler()
		{
		}

		// Implement the Initialize method to get publication 
		// and subscription information.
		public override void Initialize(
			string publisher,
			string subscriber,
			string distributor,
			string publisherDB,
			string subscriberDB,
			string articleName)
		{
			// Set the Publisher and Subscriber names.
			publisherName = publisher;
			subscriberName = subscriber;
		}

		// Declare what types of row changes, conflicts, or errors to handle.
		override public ChangeStates HandledChangeStates
		{
			get
			{
				// Handle Subscriber inserts, updates and deletes.
				return ChangeStates.SubscriberInserts |
				  ChangeStates.SubscriberUpdates | ChangeStates.SubscriberDeletes;
			}
		}

		public override ActionOnDataChange InsertHandler(SourceIdentifier insertSource,
		  DataSet insertedDataSet, ref DataSet customDataSet, ref int historyLogLevel,
		  ref string historyLogMessage)
		{
			if (insertSource == SourceIdentifier.SourceIsSubscriber)
			{
				// Build a line item in the audit message to log the Subscriber insert.
				StringBuilder AuditMessage = new StringBuilder();
				AuditMessage.Append(String.Format("A new order was entered at {0}. " +
				  "The SalesOrderID for the order is :", subscriberName));
				AuditMessage.Append(insertedDataSet.Tables[0].Rows[0]["SalesOrderID"].ToString());
				AuditMessage.Append("The order must be shipped by :");
				AuditMessage.Append(insertedDataSet.Tables[0].Rows[0]["DueDate"].ToString());

				// Set the reference parameter to write the line to the log file.
				historyLogMessage = AuditMessage.ToString();
				
				// Set the history log level to the default verbose level.
				historyLogLevel = 1;

				// Accept the inserted data in the Subscriber's data set and 
				// apply it to the Publisher.
				return ActionOnDataChange.AcceptData;
			}
			else
			{
				return base.InsertHandler(insertSource, insertedDataSet, ref customDataSet,
				  ref historyLogLevel, ref historyLogMessage);
			}
		}

		public override ActionOnDataChange UpdateHandler(SourceIdentifier updateSource,
		  DataSet updatedDataSet, ref DataSet customDataSet, ref int historyLogLevel,
		  ref string historyLogMessage)
		{
			if (updateSource == SourceIdentifier.SourceIsPublisher)
			{
				// Build a line item in the audit message to log the Subscriber update.
				StringBuilder AuditMessage = new StringBuilder();
				AuditMessage.Append(String.Format("An existing order was updated at {0}. " +
				  "The SalesOrderID for the order is ", subscriberName));
				AuditMessage.Append(updatedDataSet.Tables[0].Rows[0]["SalesOrderID"].ToString());
				AuditMessage.Append("The order must now be shipped by :");
				AuditMessage.Append(updatedDataSet.Tables[0].Rows[0]["DueDate"].ToString());

				// Set the reference parameter to write the line to the log file.
				historyLogMessage = AuditMessage.ToString();
				// Set the history log level to the default verbose level.
				historyLogLevel = 1;

				// Accept the updated data in the Subscriber's data set and apply it to the Publisher.
				return ActionOnDataChange.AcceptData;
			}
			else
			{
				return base.UpdateHandler(updateSource, updatedDataSet,
				  ref customDataSet, ref historyLogLevel, ref historyLogMessage);
			}
		}

		public override ActionOnDataDelete DeleteHandler(SourceIdentifier deleteSource,
		  DataSet deletedDataSet, ref int historyLogLevel, ref string historyLogMessage)
		{
			if (deleteSource == SourceIdentifier.SourceIsSubscriber)
			{
				// Build a line item in the audit message to log the Subscriber deletes.
				// Note that the rowguid is the only information that is 
				// available in the dataset.
				StringBuilder AuditMessage = new StringBuilder();
				AuditMessage.Append(String.Format("An existing order was deleted at {0}. " +
				  "The rowguid for the order is ", subscriberName));
				AuditMessage.Append(deletedDataSet.Tables[0].Rows[0]["rowguid"].ToString());

				// Set the reference parameter to write the line to the log file.
				historyLogMessage = AuditMessage.ToString();
				// Set the history log level to the default verbose level.
				historyLogLevel = 1;

				// Accept the delete and apply it to the Publisher.
				return ActionOnDataDelete.AcceptDelete;
			}
			else
			{
				return base.DeleteHandler(deleteSource, deletedDataSet,
				  ref historyLogLevel, ref historyLogMessage);
			}
		}
	}
}
using System;
using System.Text;
using System.Data;
using System.Data.Common;
using Microsoft.SqlServer.Replication.BusinessLogicSupport;
using Microsoft.Samples.SqlServer.BusinessLogicHandler;

namespace Microsoft.Samples.SqlServer.BusinessLogicHandler
{
	public class OrderEntryBusinessLogicHandler :
	  Microsoft.SqlServer.Replication.BusinessLogicSupport.BusinessLogicModule
	{
		// Variables to hold server names.
		private string publisherName;
		private string subscriberName;

		public OrderEntryBusinessLogicHandler()
		{
		}

		// Implement the Initialize method to get publication 
		// and subscription information.
		public override void Initialize(
			string publisher,
			string subscriber,
			string distributor,
			string publisherDB,
			string subscriberDB,
			string articleName)
		{
			// Set the Publisher and Subscriber names.
			publisherName = publisher;
			subscriberName = subscriber;
		}

		// Declare what types of row changes, conflicts, or errors to handle.
		override public ChangeStates HandledChangeStates
		{
			get
			{
				// Handle Subscriber inserts, updates and deletes.
				return ChangeStates.SubscriberInserts |
				  ChangeStates.SubscriberUpdates | ChangeStates.SubscriberDeletes;
			}
		}

		public override ActionOnDataChange InsertHandler(SourceIdentifier insertSource,
		  DataSet insertedDataSet, ref DataSet customDataSet, ref int historyLogLevel,
		  ref string historyLogMessage)
		{
			if (insertSource == SourceIdentifier.SourceIsSubscriber)
			{
				// Build a line item in the audit message to log the Subscriber insert.
				StringBuilder AuditMessage = new StringBuilder();
				AuditMessage.Append(String.Format("A new order was entered at {0}. " +
				  "The SalesOrderID for the order is :", subscriberName));
				AuditMessage.Append(insertedDataSet.Tables[0].Rows[0]["SalesOrderID"].ToString());
				AuditMessage.Append("The order must be shipped by :");
				AuditMessage.Append(insertedDataSet.Tables[0].Rows[0]["DueDate"].ToString());

				// Set the reference parameter to write the line to the log file.
				historyLogMessage = AuditMessage.ToString();
				
				// Set the history log level to the default verbose level.
				historyLogLevel = 1;

				// Accept the inserted data in the Subscriber's data set and 
				// apply it to the Publisher.
				return ActionOnDataChange.AcceptData;
			}
			else
			{
				return base.InsertHandler(insertSource, insertedDataSet, ref customDataSet,
				  ref historyLogLevel, ref historyLogMessage);
			}
		}

		public override ActionOnDataChange UpdateHandler(SourceIdentifier updateSource,
		  DataSet updatedDataSet, ref DataSet customDataSet, ref int historyLogLevel,
		  ref string historyLogMessage)
		{
			if (updateSource == SourceIdentifier.SourceIsPublisher)
			{
				// Build a line item in the audit message to log the Subscriber update.
				StringBuilder AuditMessage = new StringBuilder();
				AuditMessage.Append(String.Format("An existing order was updated at {0}. " +
				  "The SalesOrderID for the order is ", subscriberName));
				AuditMessage.Append(updatedDataSet.Tables[0].Rows[0]["SalesOrderID"].ToString());
				AuditMessage.Append("The order must now be shipped by :");
				AuditMessage.Append(updatedDataSet.Tables[0].Rows[0]["DueDate"].ToString());

				// Set the reference parameter to write the line to the log file.
				historyLogMessage = AuditMessage.ToString();
				// Set the history log level to the default verbose level.
				historyLogLevel = 1;

				// Accept the updated data in the Subscriber's data set and apply it to the Publisher.
				return ActionOnDataChange.AcceptData;
			}
			else
			{
				return base.UpdateHandler(updateSource, updatedDataSet,
				  ref customDataSet, ref historyLogLevel, ref historyLogMessage);
			}
		}

		public override ActionOnDataDelete DeleteHandler(SourceIdentifier deleteSource,
		  DataSet deletedDataSet, ref int historyLogLevel, ref string historyLogMessage)
		{
			if (deleteSource == SourceIdentifier.SourceIsSubscriber)
			{
				// Build a line item in the audit message to log the Subscriber deletes.
				// Note that the rowguid is the only information that is 
				// available in the dataset.
				StringBuilder AuditMessage = new StringBuilder();
				AuditMessage.Append(String.Format("An existing order was deleted at {0}. " +
				  "The rowguid for the order is ", subscriberName));
				AuditMessage.Append(deletedDataSet.Tables[0].Rows[0]["rowguid"].ToString());

				// Set the reference parameter to write the line to the log file.
				historyLogMessage = AuditMessage.ToString();
				// Set the history log level to the default verbose level.
				historyLogLevel = 1;

				// Accept the delete and apply it to the Publisher.
				return ActionOnDataDelete.AcceptDelete;
			}
			else
			{
				return base.DeleteHandler(deleteSource, deletedDataSet,
				  ref historyLogLevel, ref historyLogMessage);
			}
		}
	}
}

Imports System
Imports System.Text
Imports System.Data
Imports System.Data.Common
Imports Microsoft.SqlServer.Replication.BusinessLogicSupport

Namespace Microsoft.Samples.SqlServer.BusinessLogicHandler
    Public Class OrderEntryBusinessLogicHandler
        Inherits BusinessLogicModule

        ' Variables to hold server names.
        Private publisherName As String
        Private subscriberName As String

        ' Implement the Initialize method to get publication 
        ' and subscription information.
        Public Overrides Sub Initialize( _
        ByVal publisher As String, _
        ByVal subscriber As String, _
        ByVal distributor As String, _
        ByVal publisherDB As String, _
        ByVal subscriberDB As String, _
        ByVal articleName As String _
      )
            ' Set the Publisher and Subscriber names.
            publisherName = publisher
            subscriberName = subscriber
        End Sub

        ' Declare what types of row changes, conflicts, or errors to handle.
        Public Overrides ReadOnly Property HandledChangeStates() As ChangeStates
            Get
                ' Handle Subscriber inserts, updates and deletes.
                Return (ChangeStates.SubscriberInserts Or _
                 ChangeStates.SubscriberUpdates Or ChangeStates.SubscriberDeletes)
            End Get
        End Property

        Public Overrides Function InsertHandler(ByVal insertSource As SourceIdentifier, _
        ByVal insertedDataSet As DataSet, ByRef customDataSet As DataSet, _
        ByRef historyLogLevel As Integer, ByRef historyLogMessage As String) _
        As ActionOnDataChange

            If insertSource = SourceIdentifier.SourceIsSubscriber Then
                ' Build a line item in the audit message to log the Subscriber insert.
                Dim AuditMessage As StringBuilder = New StringBuilder()
                AuditMessage.Append(String.Format("A new order was entered at {0}. " + _
                 "The SalesOrderID for the order is :", subscriberName))
                AuditMessage.Append(insertedDataSet.Tables(0).Rows(0)("SalesOrderID").ToString())
                AuditMessage.Append("The order must be shipped by :")
                AuditMessage.Append(insertedDataSet.Tables(0).Rows(0)("DueDate").ToString())

                ' Set the reference parameter to write the line to the log file.
                historyLogMessage = AuditMessage.ToString()

                ' Set the history log level to the default verbose level.
                historyLogLevel = 1

                ' Accept the inserted data in the Subscriber's data set and 
                ' apply it to the Publisher.
                Return ActionOnDataChange.AcceptData
            Else
                Return MyBase.InsertHandler(insertSource, insertedDataSet, customDataSet, _
                 historyLogLevel, historyLogMessage)
            End If
        End Function
        Public Overrides Function UpdateHandler(ByVal updateSource As SourceIdentifier, _
        ByVal updatedDataSet As DataSet, ByRef customDataSet As DataSet, _
        ByRef historyLogLevel As Integer, ByRef historyLogMessage As String) _
        As ActionOnDataChange

            If updateSource = SourceIdentifier.SourceIsPublisher Then
                ' Build a line item in the audit message to log the Subscriber update.
                Dim AuditMessage As StringBuilder = New StringBuilder()
                AuditMessage.Append(String.Format("An existing order was updated at {0}. " + _
                 "The SalesOrderID for the order is ", subscriberName))
                AuditMessage.Append(updatedDataSet.Tables(0).Rows(0)("SalesOrderID").ToString())
                AuditMessage.Append("The order must now be shipped by :")
                AuditMessage.Append(updatedDataSet.Tables(0).Rows(0)("DueDate").ToString())

                ' Set the reference parameter to write the line to the log file.
                historyLogMessage = AuditMessage.ToString()
                ' Set the history log level to the default verbose level.
                historyLogLevel = 1

                ' Accept the updated data in the Subscriber's data set and apply it to the Publisher.
                Return ActionOnDataChange.AcceptData
            Else
                Return MyBase.UpdateHandler(updateSource, updatedDataSet, _
                 customDataSet, historyLogLevel, historyLogMessage)
            End If
        End Function
        Public Overrides Function DeleteHandler(ByVal deleteSource As SourceIdentifier, _
        ByVal deletedDataSet As DataSet, ByRef historyLogLevel As Integer, _
         ByRef historyLogMessage As String) As ActionOnDataDelete
            If deleteSource = SourceIdentifier.SourceIsSubscriber Then
                ' Build a line item in the audit message to log the Subscriber deletes.
                ' Note that the rowguid is the only information that is 
                ' available in the dataset.
                Dim AuditMessage As StringBuilder = New StringBuilder()
                AuditMessage.Append(String.Format("An existing order was deleted at {0}. " + _
                 "The rowguid for the order is ", subscriberName))
                AuditMessage.Append(deletedDataSet.Tables(0).Rows(0)("rowguid").ToString())

                ' Set the reference parameter to write the line to the log file.
                historyLogMessage = AuditMessage.ToString()
                ' Set the history log level to the default verbose level.
                historyLogLevel = 1

                ' Accept the delete and apply it to the Publisher.
                Return ActionOnDataDelete.AcceptDelete
            Else
                Return MyBase.DeleteHandler(deleteSource, deletedDataSet, _
                 historyLogLevel, historyLogMessage)
            End If
        End Function
    End Class
End Namespace
Imports System
Imports System.Text
Imports System.Data
Imports System.Data.Common
Imports Microsoft.SqlServer.Replication.BusinessLogicSupport

Namespace Microsoft.Samples.SqlServer.BusinessLogicHandler
    Public Class OrderEntryBusinessLogicHandler
        Inherits BusinessLogicModule

        ' Variables to hold server names.
        Private publisherName As String
        Private subscriberName As String

        ' Implement the Initialize method to get publication 
        ' and subscription information.
        Public Overrides Sub Initialize( _
        ByVal publisher As String, _
        ByVal subscriber As String, _
        ByVal distributor As String, _
        ByVal publisherDB As String, _
        ByVal subscriberDB As String, _
        ByVal articleName As String _
      )
            ' Set the Publisher and Subscriber names.
            publisherName = publisher
            subscriberName = subscriber
        End Sub

        ' Declare what types of row changes, conflicts, or errors to handle.
        Public Overrides ReadOnly Property HandledChangeStates() As ChangeStates
            Get
                ' Handle Subscriber inserts, updates and deletes.
                Return (ChangeStates.SubscriberInserts Or _
                 ChangeStates.SubscriberUpdates Or ChangeStates.SubscriberDeletes)
            End Get
        End Property

        Public Overrides Function InsertHandler(ByVal insertSource As SourceIdentifier, _
        ByVal insertedDataSet As DataSet, ByRef customDataSet As DataSet, _
        ByRef historyLogLevel As Integer, ByRef historyLogMessage As String) _
        As ActionOnDataChange

            If insertSource = SourceIdentifier.SourceIsSubscriber Then
                ' Build a line item in the audit message to log the Subscriber insert.
                Dim AuditMessage As StringBuilder = New StringBuilder()
                AuditMessage.Append(String.Format("A new order was entered at {0}. " + _
                 "The SalesOrderID for the order is :", subscriberName))
                AuditMessage.Append(insertedDataSet.Tables(0).Rows(0)("SalesOrderID").ToString())
                AuditMessage.Append("The order must be shipped by :")
                AuditMessage.Append(insertedDataSet.Tables(0).Rows(0)("DueDate").ToString())

                ' Set the reference parameter to write the line to the log file.
                historyLogMessage = AuditMessage.ToString()

                ' Set the history log level to the default verbose level.
                historyLogLevel = 1

                ' Accept the inserted data in the Subscriber's data set and 
                ' apply it to the Publisher.
                Return ActionOnDataChange.AcceptData
            Else
                Return MyBase.InsertHandler(insertSource, insertedDataSet, customDataSet, _
                 historyLogLevel, historyLogMessage)
            End If
        End Function
        Public Overrides Function UpdateHandler(ByVal updateSource As SourceIdentifier, _
        ByVal updatedDataSet As DataSet, ByRef customDataSet As DataSet, _
        ByRef historyLogLevel As Integer, ByRef historyLogMessage As String) _
        As ActionOnDataChange

            If updateSource = SourceIdentifier.SourceIsPublisher Then
                ' Build a line item in the audit message to log the Subscriber update.
                Dim AuditMessage As StringBuilder = New StringBuilder()
                AuditMessage.Append(String.Format("An existing order was updated at {0}. " + _
                 "The SalesOrderID for the order is ", subscriberName))
                AuditMessage.Append(updatedDataSet.Tables(0).Rows(0)("SalesOrderID").ToString())
                AuditMessage.Append("The order must now be shipped by :")
                AuditMessage.Append(updatedDataSet.Tables(0).Rows(0)("DueDate").ToString())

                ' Set the reference parameter to write the line to the log file.
                historyLogMessage = AuditMessage.ToString()
                ' Set the history log level to the default verbose level.
                historyLogLevel = 1

                ' Accept the updated data in the Subscriber's data set and apply it to the Publisher.
                Return ActionOnDataChange.AcceptData
            Else
                Return MyBase.UpdateHandler(updateSource, updatedDataSet, _
                 customDataSet, historyLogLevel, historyLogMessage)
            End If
        End Function
        Public Overrides Function DeleteHandler(ByVal deleteSource As SourceIdentifier, _
        ByVal deletedDataSet As DataSet, ByRef historyLogLevel As Integer, _
         ByRef historyLogMessage As String) As ActionOnDataDelete
            If deleteSource = SourceIdentifier.SourceIsSubscriber Then
                ' Build a line item in the audit message to log the Subscriber deletes.
                ' Note that the rowguid is the only information that is 
                ' available in the dataset.
                Dim AuditMessage As StringBuilder = New StringBuilder()
                AuditMessage.Append(String.Format("An existing order was deleted at {0}. " + _
                 "The rowguid for the order is ", subscriberName))
                AuditMessage.Append(deletedDataSet.Tables(0).Rows(0)("rowguid").ToString())

                ' Set the reference parameter to write the line to the log file.
                historyLogMessage = AuditMessage.ToString()
                ' Set the history log level to the default verbose level.
                historyLogLevel = 1

                ' Accept the delete and apply it to the Publisher.
                Return ActionOnDataDelete.AcceptDelete
            Else
                Return MyBase.DeleteHandler(deleteSource, deletedDataSet, _
                 historyLogLevel, historyLogMessage)
            End If
        End Function
    End Class
End Namespace

다음 예에서는 배포자에 비즈니스 논리 처리기를 등록합니다.This example registers a business logic handler at the Distributor.

// Specify the Distributor name and business logic properties.
string distributorName = publisherInstance;
string assemblyName = @"C:\Program Files\Microsoft SQL Server\110\COM\CustomLogic.dll";
string className = "Microsoft.Samples.SqlServer.BusinessLogicHandler.OrderEntryBusinessLogicHandler";
string friendlyName = "OrderEntryLogic";

ReplicationServer distributor;
BusinessLogicHandler customLogic;

	// Create a connection to the Distributor.
ServerConnection distributorConn = new ServerConnection(distributorName);

try
{
	// Connect to the Distributor.
	distributorConn.Connect();

	// Set the Distributor properties.
	distributor = new ReplicationServer(distributorConn);

	// Set the business logic handler properties.
	customLogic = new BusinessLogicHandler();
	customLogic.DotNetAssemblyName = assemblyName;
	customLogic.DotNetClassName = className;
	customLogic.FriendlyName = friendlyName;
	customLogic.IsDotNetAssembly = true;

	Boolean isRegistered = false;

	// Check if the business logic handler is already registered at the Distributor.
	foreach (BusinessLogicHandler registeredLogic
		in distributor.EnumBusinessLogicHandlers())
	{
		if (registeredLogic == customLogic)
		{
			isRegistered = true;
		}
	}

	// Register the custom logic.
	if (!isRegistered)
	{
		distributor.RegisterBusinessLogicHandler(customLogic);
	}
}
catch (Exception ex)
{
	// Do error handling here.
	throw new ApplicationException(string.Format(
		"The {0} assembly could not be registered.",
		assemblyName), ex);
}
finally
{
	distributorConn.Disconnect();
}
// Specify the Distributor name and business logic properties.
string distributorName = publisherInstance;
string assemblyName = @"C:\Program Files\Microsoft SQL Server\110\COM\CustomLogic.dll";
string className = "Microsoft.Samples.SqlServer.BusinessLogicHandler.OrderEntryBusinessLogicHandler";
string friendlyName = "OrderEntryLogic";

ReplicationServer distributor;
BusinessLogicHandler customLogic;

	// Create a connection to the Distributor.
ServerConnection distributorConn = new ServerConnection(distributorName);

try
{
	// Connect to the Distributor.
	distributorConn.Connect();

	// Set the Distributor properties.
	distributor = new ReplicationServer(distributorConn);

	// Set the business logic handler properties.
	customLogic = new BusinessLogicHandler();
	customLogic.DotNetAssemblyName = assemblyName;
	customLogic.DotNetClassName = className;
	customLogic.FriendlyName = friendlyName;
	customLogic.IsDotNetAssembly = true;

	Boolean isRegistered = false;

	// Check if the business logic handler is already registered at the Distributor.
	foreach (BusinessLogicHandler registeredLogic
		in distributor.EnumBusinessLogicHandlers())
	{
		if (registeredLogic == customLogic)
		{
			isRegistered = true;
		}
	}

	// Register the custom logic.
	if (!isRegistered)
	{
		distributor.RegisterBusinessLogicHandler(customLogic);
	}
}
catch (Exception ex)
{
	// Do error handling here.
	throw new ApplicationException(string.Format(
		"The {0} assembly could not be registered.",
		assemblyName), ex);
}
finally
{
	distributorConn.Disconnect();
}

' Specify the Distributor name and business logic properties.
Dim distributorName As String = publisherInstance
Dim assemblyName As String = "C:\Program Files\Microsoft SQL Server\110\COM\CustomLogic.dll"
Dim className As String = "Microsoft.Samples.SqlServer.BusinessLogicHandler.OrderEntryBusinessLogicHandler"
Dim friendlyName As String = "OrderEntryLogic"

Dim distributor As ReplicationServer
Dim customLogic As BusinessLogicHandler

' Create a connection to the Distributor.
Dim distributorConn As ServerConnection = New ServerConnection(distributorName)

Try
    ' Connect to the Distributor.
    distributorConn.Connect()

    ' Set the Distributor properties.
    distributor = New ReplicationServer(distributorConn)

    ' Set the business logic handler properties.
    customLogic = New BusinessLogicHandler()
    customLogic.DotNetAssemblyName = assemblyName
    customLogic.DotNetClassName = className
    customLogic.FriendlyName = friendlyName
    customLogic.IsDotNetAssembly = True

    Dim isRegistered As Boolean = False

    ' Check if the business logic handler is already registered at the Distributor.
    For Each registeredLogic As BusinessLogicHandler _
    In distributor.EnumBusinessLogicHandlers
        If registeredLogic Is customLogic Then
            isRegistered = True
        End If
    Next

    ' Register the custom logic.
    If Not isRegistered Then
        distributor.RegisterBusinessLogicHandler(customLogic)
    End If
Catch ex As Exception
    ' Do error handling here.
    Throw New ApplicationException(String.Format( _
     "The {0} assembly could not be registered.", _
     assemblyName), ex)
Finally
    distributorConn.Disconnect()
End Try
' Specify the Distributor name and business logic properties.
Dim distributorName As String = publisherInstance
Dim assemblyName As String = "C:\Program Files\Microsoft SQL Server\110\COM\CustomLogic.dll"
Dim className As String = "Microsoft.Samples.SqlServer.BusinessLogicHandler.OrderEntryBusinessLogicHandler"
Dim friendlyName As String = "OrderEntryLogic"

Dim distributor As ReplicationServer
Dim customLogic As BusinessLogicHandler

' Create a connection to the Distributor.
Dim distributorConn As ServerConnection = New ServerConnection(distributorName)

Try
    ' Connect to the Distributor.
    distributorConn.Connect()

    ' Set the Distributor properties.
    distributor = New ReplicationServer(distributorConn)

    ' Set the business logic handler properties.
    customLogic = New BusinessLogicHandler()
    customLogic.DotNetAssemblyName = assemblyName
    customLogic.DotNetClassName = className
    customLogic.FriendlyName = friendlyName
    customLogic.IsDotNetAssembly = True

    Dim isRegistered As Boolean = False

    ' Check if the business logic handler is already registered at the Distributor.
    For Each registeredLogic As BusinessLogicHandler _
    In distributor.EnumBusinessLogicHandlers
        If registeredLogic Is customLogic Then
            isRegistered = True
        End If
    Next

    ' Register the custom logic.
    If Not isRegistered Then
        distributor.RegisterBusinessLogicHandler(customLogic)
    End If
Catch ex As Exception
    ' Do error handling here.
    Throw New ApplicationException(String.Format( _
     "The {0} assembly could not be registered.", _
     assemblyName), ex)
Finally
    distributorConn.Disconnect()
End Try

다음 예에서는 비즈니스 논리 처리기를 사용하도록 기존 아티클을 변경합니다.This example changes an existing article to use the business logic handler.

// Define the Publisher, publication, and article names.
string publisherName = publisherInstance;
string publicationName = "AdvWorksSalesOrdersMerge";
string publicationDbName = "AdventureWorks2012";
string articleName = "SalesOrderHeader";

// Set the friendly name of the business logic handler.
string customLogic = "OrderEntryLogic";

MergeArticle article = new MergeArticle();

// Create a connection to the Publisher.
ServerConnection conn = new ServerConnection(publisherName);

try
{
	// Connect to the Publisher.
	conn.Connect();

	// Set the required properties for the article.
	article.ConnectionContext = conn;
	article.Name = articleName;
	article.DatabaseName = publicationDbName;
	article.PublicationName = publicationName;

	// Load the article properties.
	if (article.LoadProperties())
	{
		article.ArticleResolver = customLogic;
	}
	else
	{
		// Throw an exception of the article does not exist.
		throw new ApplicationException(String.Format(
		"{0} is not published in {1}", articleName, publicationName));
	}
	
}
catch (Exception ex)
{
	// Do error handling here and rollback the transaction.
	throw new ApplicationException(String.Format(
		"The business logic handler {0} could not be associated with " +
		" the {1} article.",customLogic,articleName), ex);
}
finally
{
	conn.Disconnect();
}
// Define the Publisher, publication, and article names.
string publisherName = publisherInstance;
string publicationName = "AdvWorksSalesOrdersMerge";
string publicationDbName = "AdventureWorks2012";
string articleName = "SalesOrderHeader";

// Set the friendly name of the business logic handler.
string customLogic = "OrderEntryLogic";

MergeArticle article = new MergeArticle();

// Create a connection to the Publisher.
ServerConnection conn = new ServerConnection(publisherName);

try
{
	// Connect to the Publisher.
	conn.Connect();

	// Set the required properties for the article.
	article.ConnectionContext = conn;
	article.Name = articleName;
	article.DatabaseName = publicationDbName;
	article.PublicationName = publicationName;

	// Load the article properties.
	if (article.LoadProperties())
	{
		article.ArticleResolver = customLogic;
	}
	else
	{
		// Throw an exception of the article does not exist.
		throw new ApplicationException(String.Format(
		"{0} is not published in {1}", articleName, publicationName));
	}
	
}
catch (Exception ex)
{
	// Do error handling here and rollback the transaction.
	throw new ApplicationException(String.Format(
		"The business logic handler {0} could not be associated with " +
		" the {1} article.",customLogic,articleName), ex);
}
finally
{
	conn.Disconnect();
}

' Define the Publisher, publication, and article names.
Dim publisherName As String = publisherInstance
Dim publicationName As String = "AdvWorksSalesOrdersMerge"
Dim publicationDbName As String = "AdventureWorks2012"
Dim articleName As String = "SalesOrderHeader"

' Set the friendly name of the business logic handler.
Dim customLogic As String = "OrderEntryLogic"

Dim article As MergeArticle = New MergeArticle()

' Create a connection to the Publisher.
Dim conn As ServerConnection = New ServerConnection(publisherName)

Try
    ' Connect to the Publisher.
    conn.Connect()

    ' Set the required properties for the article.
    article.ConnectionContext = conn
    article.Name = articleName
    article.DatabaseName = publicationDbName
    article.PublicationName = publicationName

    ' Load the article properties.
    If article.LoadProperties() Then
        article.ArticleResolver = customLogic
    Else
        ' Throw an exception of the article does not exist.
        Throw New ApplicationException(String.Format( _
         "{0} is not published in {1}", articleName, publicationName))
    End If

Catch ex As Exception
    ' Do error handling here and rollback the transaction.
    Throw New ApplicationException(String.Format( _
     "The business logic handler {0} could not be associated with " + _
     " the {1} article.", customLogic, articleName), ex)
Finally
    conn.Disconnect()
End Try
' Define the Publisher, publication, and article names.
Dim publisherName As String = publisherInstance
Dim publicationName As String = "AdvWorksSalesOrdersMerge"
Dim publicationDbName As String = "AdventureWorks2012"
Dim articleName As String = "SalesOrderHeader"

' Set the friendly name of the business logic handler.
Dim customLogic As String = "OrderEntryLogic"

Dim article As MergeArticle = New MergeArticle()

' Create a connection to the Publisher.
Dim conn As ServerConnection = New ServerConnection(publisherName)

Try
    ' Connect to the Publisher.
    conn.Connect()

    ' Set the required properties for the article.
    article.ConnectionContext = conn
    article.Name = articleName
    article.DatabaseName = publicationDbName
    article.PublicationName = publicationName

    ' Load the article properties.
    If article.LoadProperties() Then
        article.ArticleResolver = customLogic
    Else
        ' Throw an exception of the article does not exist.
        Throw New ApplicationException(String.Format( _
         "{0} is not published in {1}", articleName, publicationName))
    End If

Catch ex As Exception
    ' Do error handling here and rollback the transaction.
    Throw New ApplicationException(String.Format( _
     "The business logic handler {0} could not be associated with " + _
     " the {1} article.", customLogic, articleName), ex)
Finally
    conn.Disconnect()
End Try

참고 항목See Also

병합 아티클용 사용자 지정 충돌 해결 프로그램 구현 Implement a Custom Conflict Resolver for a Merge Article
비즈니스 논리 처리기 디버깅(복제 프로그래밍) Debug a Business Logic Handler (Replication Programming)
복제 보안을 위한 최선의 구현 방법 Replication Security Best Practices
복제 관리 개체 개념Replication Management Objects Concepts