次の方法で共有


マージ アーティクルのビジネス ロジック ハンドラーを実装する

このトピックでは、レプリケーション プログラミングまたはレプリケーション管理オブジェクト (RMO) を使用して、SQL Server 2014 でマージ アーティクルのビジネス ロジック ハンドラーを実装する方法について説明します。

Microsoft.SqlServer.Replication.BusinessLogicSupport名前空間には、マージ レプリケーション同期プロセス中に発生するイベントを処理する複雑なビジネス ロジックを記述できるインターフェイスが実装されています。 ビジネス ロジック ハンドラーのメソッドは、同期中にレプリケートされる変更された行ごとに、レプリケーション プロセスによって呼び出すことができます。

ビジネス ロジック ハンドラーを実装するための一般的なプロセスは次のとおりです。

  1. ビジネス ロジック ハンドラー アセンブリを作成します。

  2. ディストリビューターでアセンブリを登録します。

  3. マージ エージェントが実行されているサーバーにアセンブリを配置します。 プル サブスクリプションの場合、エージェントはサブスクライバーで実行され、プッシュ サブスクリプションの場合はエージェントがディストリビューターで実行されます。 Web 同期を使用している場合、エージェントは Web サーバー上で実行されます。

  4. ビジネス ロジック ハンドラーを使用するアーティクルを作成するか、ビジネス ロジック ハンドラーを使用するように既存のアーティクルを変更します。

指定したビジネス ロジック ハンドラーは、同期されるすべての行に対して実行されます。 複雑なロジックや他のアプリケーションまたはネットワーク サービスの呼び出しは、パフォーマンスに影響を与える可能性があります。 ビジネス ロジック ハンドラーの詳細については、「 マージ同期中にビジネス ロジックを実行する」を参照してください。

このトピックについて

レプリケーション プログラミングの使用

ビジネス ロジック ハンドラーを作成してデプロイするには

  1. Microsoft Visual Studio で、ビジネス ロジック ハンドラーを実装するコードを含む .NET アセンブリの新しいプロジェクトを作成します。

  2. 次の名前空間のプロジェクトへの参照を追加します。

    アセンブリ リファレンス ロケーション
    Microsoft.SqlServer.Replication.BusinessLogicSupport C:\Program Files\Microsoft SQL Server\120\COM (既定のインストール)
    System.Data GAC (.NET Framework のコンポーネント)
    System.Data.Common GAC (.NET Framework のコンポーネント)
  3. BusinessLogicModule クラスをオーバーライドするクラスを追加します。

  4. HandledChangeStates プロパティを実装して、処理される変更の種類を示します。

  5. BusinessLogicModule クラスの次の 1 つ以上のメソッドをオーバーライドします。

    • CommitHandler - 同期中にデータ変更がコミットされたときに呼び出されます。

    • DeleteErrorHandler - DELETE ステートメントがアップロードまたはダウンロードされるときにエラーが発生したときに呼び出されます。

    • DeleteHandler - DELETE ステートメントがアップロードまたはダウンロードされるときに呼び出されます。

    • InsertErrorHandler - INSERT ステートメントがアップロードまたはダウンロードされるときにエラーが発生したときに呼び出されます。

    • InsertHandler - INSERT ステートメントがアップロードまたはダウンロードされるときに呼び出されます。

    • UpdateConflictsHandler - パブリッシャーとサブスクライバーで競合する UPDATE ステートメントが発生したときに呼び出されます。

    • UpdateDeleteConflictHandler - UPDATE ステートメントがパブリッシャーおよびサブスクライバーの DELETE ステートメントと競合する場合に呼び出されます。

    • UpdateErrorHandler - UPDATE ステートメントがアップロードまたはダウンロードされるときにエラーが発生したときに呼び出されます。

    • UpdateHandler - UPDATE ステートメントがアップロードまたはダウンロードされるときに呼び出されます。

  6. プロジェクトをビルドして、ビジネス ロジック ハンドラー アセンブリを作成します。

  7. マージ エージェント実行可能ファイル (replmerg.exe) を含むディレクトリにアセンブリを展開します。既定のインストールでは C:\Program Files\Microsoft SQL Server\120\COM、または .NET グローバル アセンブリ キャッシュ (GAC) にインストールします。 マージ エージェント以外のアプリケーションでアセンブリへのアクセスが必要な場合にのみ、アセンブリを GAC にインストールする必要があります。 アセンブリは、.NET Framework SDK で提供されているグローバル アセンブリ キャッシュ ツール (Gacutil.exe) を使用して GAC にインストールできます。

    ビジネス ロジック ハンドラーは、マージ エージェントを実行するすべてのサーバーに展開する必要があります。これには、Web 同期を使用するときに replisapi.dll をホストする IIS サーバーが含まれます。

ビジネス ロジック ハンドラーを登録するには

  1. パブリッシャーで 、sp_enumcustomresolvers (Transact-SQL) を実行して、アセンブリがまだビジネス ロジック ハンドラーとして登録されていないことを確認します。

  2. ディストリビューターで、sp_registercustomresolver (Transact-SQL) を実行し、@article_resolverのビジネス ロジック ハンドラーのフレンドリ名、@is_dotnet_assemblyの値true、@dotnet_assembly_nameのアセンブリの名前、および@dotnet_class_nameBusinessLogicModuleをオーバーライドするクラスの完全修飾名を指定します。

    マージ エージェント実行可能ファイルと同じディレクトリ、マージ エージェントを同期的に起動するアプリケーションと同じディレクトリ、またはグローバル アセンブリ キャッシュ (GAC) にアセンブリが配置されていない場合は、 @dotnet_assembly_nameのアセンブリ名で完全パスを指定する必要があります。 Web 同期を使用する場合は、Web サーバーでのアセンブリの場所を指定する必要があります。

新しいテーブルアーティクルでビジネス ロジック ハンドラーを使用するには

  1. sp_addmergearticle (Transact-SQL) を実行してアーティクルを定義し、@article_resolverのビジネス ロジック ハンドラーのフレンドリ名を指定します。 詳細については、 アーティクルの定義を参照してください。

既存のテーブルアーティクルでビジネス ロジック ハンドラーを使用するには

  1. @publication、@article@propertyのarticle_resolverの値、および@valueのビジネス ロジック ハンドラーのフレンドリ名を指定して、sp_changemergearticle (Transact-SQL) を実行します。

例 (レプリケーション プログラミング)

この例では、監査ログを作成するビジネス ロジック ハンドラーを示します。

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

次の例では、ビジネス ロジック ハンドラー アセンブリをディストリビューターに登録し、このカスタム ビジネス ロジックを使用するように既存のマージ アーティクルを変更します。

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) の使用

ビジネス ロジック ハンドラーを作成するには

  1. Microsoft Visual Studio で、ビジネス ロジック ハンドラーを実装するコードを含む .NET アセンブリの新しいプロジェクトを作成します。

  2. 次の名前空間のプロジェクトへの参照を追加します。

    アセンブリ リファレンス ロケーション
    Microsoft.SqlServer.Replication.BusinessLogicSupport C:\Program Files\Microsoft SQL Server\120\COM (既定のインストール)
    System.Data GAC (.NET Framework のコンポーネント)
    System.Data.Common GAC (.NET Framework のコンポーネント)
  3. BusinessLogicModule クラスをオーバーライドするクラスを追加します。

  4. HandledChangeStates プロパティを実装して、処理される変更の種類を示します。

  5. BusinessLogicModule クラスの次の 1 つ以上のメソッドをオーバーライドします。

    • CommitHandler - 同期中にデータ変更がコミットされたときに呼び出されます。

    • DeleteErrorHandler - DELETE ステートメントのアップロードまたはダウンロード中にエラーが発生した場合に呼び出されます。

    • DeleteHandler - DELETE ステートメントがアップロードまたはダウンロードされるときに呼び出されます。

    • InsertErrorHandler - INSERT ステートメントがアップロードまたはダウンロードされるときにエラーが発生した場合に呼び出されます。

    • InsertHandler - INSERT ステートメントがアップロードまたはダウンロードされるときに呼び出されます。

    • UpdateConflictsHandler - パブリッシャーとサブスクライバーで競合する UPDATE ステートメントが発生したときに呼び出されます。

    • UpdateDeleteConflictHandler - UPDATE ステートメントがパブリッシャーおよびサブスクライバーの DELETE ステートメントと競合する場合に呼び出されます。

    • UpdateErrorHandler - UPDATE ステートメントがアップロードまたはダウンロードされるときにエラーが発生した場合に呼び出されます。

    • UpdateHandler - UPDATE ステートメントがアップロードまたはダウンロードされるときに呼び出されます。

    カスタム ビジネス ロジックによって明示的に処理されないアーティクルの競合は、アーティクルの既定の競合回避モジュールによって処理されます。

  6. プロジェクトをビルドして、ビジネス ロジック ハンドラー アセンブリを作成します。

ビジネス ロジック ハンドラーを登録するには

  1. ServerConnection クラスを使用してディストリビューターへの接続を作成します。

  2. ReplicationServer クラスのインスタンスを作成します。 手順 1 の ServerConnection を渡します。

  3. EnumBusinessLogicHandlersを呼び出し、返されたArrayList オブジェクトを調べて、アセンブリがまだビジネス ロジック ハンドラーとして登録されていないことを確認します。

  4. BusinessLogicHandler クラスのインスタンスを作成します。 次のプロパティを指定します。

    • DotNetAssemblyName - .NET アセンブリの名前。 マージ エージェント実行可能ファイルと同じディレクトリ、マージ エージェントを同期的に起動するアプリケーションと同じディレクトリ、または GAC 内にアセンブリが配置されていない場合は、アセンブリ名を持つ完全なパスを含める必要があります。 Web 同期でビジネス ロジック ハンドラーを使用する場合は、アセンブリ名に完全なパスを含める必要があります。

    • DotNetClassName - BusinessLogicModule をオーバーライドし、ビジネス ロジック ハンドラーを実装するクラスの完全修飾名。

    • FriendlyName - ビジネス ロジック ハンドラーにアクセスするときに使用するフレンドリ名。

    • IsDotNetAssembly - trueの値。

ビジネス ロジック ハンドラーをデプロイするには

  1. ビジネス ロジック ハンドラーがディストリビューターに登録されたときに指定されたファイルの場所にマージ エージェントが実行されているサーバーにアセンブリを配置します。 プル サブスクリプションの場合、エージェントはサブスクライバーで実行され、プッシュ サブスクリプションの場合はエージェントがディストリビューターで実行されます。 Web 同期を使用している場合、エージェントは Web サーバー上で実行されます。 ビジネス ロジック ハンドラーの登録時にアセンブリ名に完全なパスが含まれていない場合は、マージ エージェントを同期的に起動するアプリケーションと同じディレクトリにあるマージ エージェント実行可能ファイルと同じディレクトリにアセンブリを配置します。 同じアセンブリを使用する複数のアプリケーションがある場合は、GAC にアセンブリをインストールできます。

新しいテーブルアーティクルでビジネス ロジック ハンドラーを使用するには

  1. ServerConnection クラスを使用してパブリッシャーへの接続を作成します。

  2. MergeArticle クラスのインスタンスを作成します。 次のようにプロパティを設定します。

  3. Create メソッドを呼び出します。 詳細については、 アーティクルの定義を参照してください。

既存のテーブルアーティクルでビジネス ロジック ハンドラーを使用するには

  1. ServerConnection クラスを使用してパブリッシャーへの接続を作成します。

  2. MergeArticle クラスのインスタンスを作成します。

  3. NamePublicationName、およびDatabaseNameのプロパティを設定します。

  4. ConnectionContext プロパティの手順 1 の接続を設定します。

  5. LoadProperties メソッドを呼び出して、オブジェクトのプロパティを取得します。 このメソッドが falseを返す場合、手順 3 のアーティクル プロパティが正しく定義されていないか、アーティクルが存在しません。 詳細については、「アーティクル のプロパティの表示と変更」を参照してください。

  6. ArticleResolverのビジネス ロジック ハンドラーのフレンドリ名を設定します。 これは、ビジネス ロジック ハンドラーの登録時に指定された FriendlyName プロパティの値です。

例 (RMO)

この例は、サブスクライバーでの挿入、更新、削除に関する情報をログに記録するビジネス ロジック ハンドラーです。

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

この例では、ディストリビューターにビジネス ロジック ハンドラーを登録します。

// 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

この例では、ビジネス ロジック ハンドラーを使用するように既存のアーティクルを変更します。

// 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

こちらもご覧ください

マージ アーティクルのカスタム競合回避モジュールを実装する
ビジネス ロジック ハンドラーのデバッグ (レプリケーション プログラミング)
レプリケーション のセキュリティに関するベスト プラクティス
レプリケーション管理オブジェクトの概念