マージ記事用のビジネスロジックハンドラーを実装してください

適用対象:SQL Server

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

Microsoft.SqlServer.Replication.BusinessLogicSupport名前空間は、マージレプリケーション同期プロセス中に発生するイベントを処理する複雑なビジネスロジックを書くためのインターフェースを実装しています。 レプリケーションプロセスは、同期時にレプリケートする各変更行に対してビジネスロジックハンドラー内のメソッドを呼び出します。

ビジネス ロジック ハンドラーを実装するための一般的な手順を次に示します。

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

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

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

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

指定するビジネス ロジック ハンドラーは、同期されるすべての行に対して実行されます。 複雑なロジックや他のアプリケーションやネットワークサービスへの呼び出しはパフォーマンスに影響を与えることがあります。 ビジネス ロジック ハンドラーについて詳しくは、「Execute Business Logic During Merge Synchronization」(マージ同期中のビジネス ロジックの実行) をご覧ください。

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

ビジネス ロジック ハンドラーを作成および配置するには

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

  2. 次の名前空間の参照をプロジェクトに追加してください。

    アセンブリ参照 場所
    Microsoft.SqlServer.Replication.BusinessLogicSupport < drive>:\Program Files\Microsoft SQL Server\nnn\COM (既定のインストール)
    System.Data GAC(.NETフレームワークのコンポーネント)
    System.Data.Common GAC(.NETフレームワークのコンポーネント)
  3. BusinessLogicModule クラスをオーバーライドするクラスを追加します。

  4. ハンドラーが管理する変更の種類を示す HandledChangeStates プロパティを実装します。

  5. BusinessLogicModule クラスのメソッドのうち、次のいずれかまたは複数のメソッドをオーバーライドします。

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

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

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

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

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

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

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

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

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

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

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

    マージ エージェントが動作するすべてのサーバーにビジネスロジックハンドラを展開しなければなりません。 この要件には、ウェブ同期を使用する際に replisapi.dll をホストするIISサーバーも含まれます。

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

  1. Publisherで、アセンブリがすでにビジネスロジックハンドラーとして登録されていないことを確認するために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. sp_changemergearticle(Transact-SQL)を実行し、@propertyには@publication@articlearticle_resolver@valueにはビジネスロジックハンドラーのフレンドリーな名前を指定します。

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

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

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 < drive>:\Program Files\Microsoft SQL Server\nnn\COM (既定のインストール)
    System.Data GAC(.NETフレームワークのコンポーネント)
    System.Data.Common GAC(.NETフレームワークのコンポーネント)
  3. BusinessLogicModule クラスをオーバーライドするクラスを追加します。

  4. ハンドラーが管理する変更の種類を示す HandledChangeStates プロパティを実装します。

  5. BusinessLogicModule クラスのメソッドのうち、次のいずれかまたは複数のメソッドをオーバーライドします。

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

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

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

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

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

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

    • UpdateDeleteConflictHandler- UPDATE ステートメントがPublisherおよびサブスクライバーの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 クラスのインスタンスを作成します。 次のプロパティを設定します。

    • Nameにアーティクル名を指定します。

    • PublicationName にパブリケーション名を指定します。

    • DatabaseNameにパブリケーション データベース名を指定します。

    • FriendlyNameにビジネス ロジック ハンドラーの表示名 ( ArticleResolver) を指定します。

  3. Create メソッドを呼び出します。 詳しくは、「 アーティクルを定義」をご覧ください。

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

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

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

  3. NamePublicationNameDatabaseName の各プロパティを設定します。

  4. 手順 1. で作成した接続を ConnectionContext プロパティに設定します。

  5. LoadProperties メソッドを呼び出して、オブジェクトのプロパティを取得します。 このメソッドが falseを返すなら、ステップ3で記事のプロパティを正しく定義しているか、記事自体が存在しない可能性があります。 詳しくは、「 View and Modify Article Properties」をご覧ください。

  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 = "AdventureWorks2022";
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 = "AdventureWorks2022"
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