マージ アーティクルのビジネス ロジック ハンドラを実装する方法 (レプリケーション プログラミング)
Microsoft.SqlServer.Replication.BusinessLogicSupport 名前空間は、マージ レプリケーションの同期処理中に発生するイベントを処理する、複雑なビジネス ロジックを記述するためのインターフェイスを実装します。ビジネス ロジック ハンドラのメソッドは、同期中にレプリケートされる変更行ごとに、レプリケーション処理から呼び出すことができます。
ビジネス ロジック ハンドラを実装するための一般的な手順を次に示します。
- ビジネス ロジック ハンドラのアセンブリを作成します。
- ディストリビュータにアセンブリを登録します。
- マージ エージェントが実行されるサーバーにアセンブリを配置します。プル サブスクリプションの場合、エージェントはサブスクライバ上で実行されます。プッシュ サブスクリプションの場合、エージェントはディストリビュータ上で実行されます。Web 同期を使用した場合、エージェントは Web サーバー上で実行されます。
- ビジネス ロジック ハンドラを使用するアーティクルを作成するか、またはビジネス ロジック ハンドラを使用する既存のアーティクルを変更します。
指定するビジネス ロジック ハンドラは、同期されるすべての行に対して実行されます。その他のアプリケーションまたはネットワーク サービスに対する複雑なロジックおよび呼び出しは、パフォーマンスに影響を与える可能性があります。ビジネス ロジック ハンドラの詳細については、「マージ同期中のビジネス ロジックの実行」を参照してください。
ビジネス ロジック ハンドラを作成および配置するには
Microsoft Visual Studio で、.NET アセンブリの新しいプロジェクトを作成し、ビジネス ロジック ハンドラの実装コードを記述します。
次の名前空間の参照をプロジェクトに追加します。
アセンブリ参照 場所 Microsoft.SqlServer.Replication.BusinessLogicSupport
C:\Program Files\Microsoft SQL Server\90\COM (既定のインストール場所)
GAC (.NET Framework のコンポーネント)
GAC (.NET Framework のコンポーネント)
BusinessLogicModule クラスをオーバーライドするクラスを追加します。
処理対象の変更の種類を指定する HandledChangeStates プロパティを実装します。
BusinessLogicModule クラスのメソッドのうち、次のいずれかまたは複数のメソッドをオーバーライドします。
- CommitHandler - 同期中、データ変更がコミットされたときに呼び出されます。
- DeleteErrorHandler - DELETE ステートメントをアップロードまたはダウンロードするときにエラーが発生すると呼び出されます。
- DeleteHandler - DELETE ステートメントをアップロードまたはダウンロードするときに呼び出されます。
- InsertErrorHandler - INSERT ステートメントをアップロードまたはダウンロードするときにエラーが発生すると呼び出されます。
- InsertHandler - INSERT ステートメントをアップロードまたはダウンロードするときに呼び出されます。
- UpdateConflictsHandler - パブリッシャおよびサブスクライバで、UPDATE ステートメントの競合が発生した場合に呼び出されます。
- UpdateDeleteConflictHandler - パブリッシャおよびサブスクライバで、UPDATE ステートメントと DELETE ステートメントが競合した場合に呼び出されます。
- UpdateErrorHandler - UPDATE ステートメントをアップロードまたはダウンロードするときにエラーが発生すると呼び出されます。
- UpdateHandler - UPDATE ステートメントをアップロードまたはダウンロードするときに呼び出されます。
プロジェクトをビルドして、ビジネス ロジック ハンドラ アセンブリを作成します。
アセンブリを、マージ エージェント実行可能ファイル (replmerg.exe) を含むディレクトリ (既定のインストールの場合は C:\Program Files\Microsoft SQL Server\90\COM) に配置するか、.NET グローバル アセンブリ キャッシュ (GAC) にインストールします。マージ エージェント以外のアプリケーションがアセンブリへのアクセスを必要とする場合は、アセンブリを GAC にのみインストールしてください。アセンブリを GAC にインストールするには、.NET Framework SDK のグローバル アセンブリ キャッシュ ツール (Gacutil.exe) を使用します。
メモ : ビジネス ロジック ハンドラは、Web 同期を使用するときに replisapi.dll をホストする IIS サーバーなど、マージ エージェントを実行する各サーバーに配置する必要があります。
ビジネス ロジック ハンドラを登録するには
パブリッシャで、sp_enumcustomresolvers (Transact-SQL) を実行して、アセンブリがまだビジネス ロジック ハンドラとして登録されていないことを確認します。
ディストリビュータで、sp_registercustomresolver (Transact-SQL) を実行します。@article_resolver にビジネス ロジック ハンドラの表示名を、@is_dotnet_assembly に true を、@dotnet_assembly_name にアセンブリの名前を、@dotnet_class_name に BusinessLogicModule をオーバーライドするクラスの完全修飾名を指定します。
メモ : マージ エージェント実行可能ファイルがあるディレクトリ、マージ エージェントを同期的に起動するアプリケーションがあるディレクトリ、およびグローバル アセンブリ キャッシュ (GAC) の、いずれとも異なる場所にビジネス ロジック ハンドラ アセンブリが配置されている場合は、@dotnet_assembly_name にアセンブリ名を含む完全なパスを指定する必要があります。Web 同期を使用する場合は、Web サーバーでアセンブリの位置を指定する必要があります。
新しいテーブル アーティクルでビジネス ロジック ハンドラを使用するには
- @article_resolver にビジネス ロジック ハンドラの表示名を指定して、アーティクルを定義する sp_addmergearticle (Transact-SQL) を実行します。詳細については、「アーティクルを定義する方法 (レプリケーション Transact-SQL プログラミング)」を参照してください。
既存のテーブル アーティクルでビジネス ロジック ハンドラを使用するには
- @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\90\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
参照
処理手順
ビジネス ロジック ハンドラをデバッグする方法 (レプリケーション プログラミング)
マージ アーティクル用の COM ベース カスタム競合回避モジュールを実装する方法 (レプリケーションのプログラミング)
マージ アーティクルに対してストアド プロシージャ ベースのカスタム競合回避モジュールを実装する方法 (レプリケーション Transact-SQL プログラミング)