Implémenter un gestionnaire de logique métier pour un article de fusion

S'applique à : SQL Server

Cette rubrique décrit comment implémenter un gestionnaire de logique métier pour un article de fusion dans SQL Server à l’aide de la programmation de réplication ou d’objets RMO (Replication Management Objects).

L'espace de noms Microsoft.SqlServer.Replication.BusinessLogicSupport implémente une interface qui vous permet d'écrire une logique métier complexe afin de gérer les événements qui se produisent au cours du processus de synchronisation de la réplication de fusion. Les méthodes dans le gestionnaire de logique métier peuvent être appelées par le processus de réplication pour chaque ligne modifiée qui est répliquée pendant la synchronisation.

La procédure globale permettant d'implémenter un gestionnaire de logique métier est la suivante :

  1. Créez l'assembly du gestionnaire de logique métier.

  2. Inscrivez l'assembly sur le serveur de distribution.

  3. Déployez l'assembly sur le serveur où l'Agent de fusion s'exécute. Pour un abonnement par extraction, l'agent s'exécute sur l'Abonné, alors que pour un abonnement par émission de données, il s'exécute sur le serveur de distribution. Lorsque vous utilisez la synchronisation Web, l'agent s'exécute sur le serveur Web.

  4. Créez un article qui utilise le gestionnaire de logique métier ou modifiez un article existant pour utiliser le gestionnaire de logique métier.

Le gestionnaire de logique métier que vous spécifiez est exécuté pour chaque ligne faisant l'objet d'une synchronisation. Une logique complexe et des appels à d'autres applications ou services réseau peuvent avoir une incidence sur les performances. Pour plus d’informations sur les gestionnaires de logique métier, consultez Exécuter une logique pendant la synchronisation de fusion.

Dans cette rubrique

Utilisation de la programmation de la réplication

Pour créer et déployer un gestionnaire de logique métier

  1. Dans Microsoft Visual Studio, créez un projet pour l'assembly .NET contenant le code qui implémente le gestionnaire de logique métier.

  2. Ajoutez des références au projet pour les espaces de noms ci-dessous.

    Référence d'assembly Emplacement
    Microsoft.SqlServer.Replication.BusinessLogicSupport C:\Program Files\Microsoft SQL Server\nnn\COM (installation par défaut)
    System.Data GAC (composant du .NET Framework)
    System.Data.Common GAC (composant du .NET Framework)
  3. Ajoutez une classe qui remplace la classe BusinessLogicModule .

  4. Implémentez la propriété HandledChangeStates pour indiquer les types de modifications qui sont gérés.

  5. Remplacez une ou plusieurs des méthodes suivantes de la classe BusinessLogicModule :

    • CommitHandler – méthode appelée lorsqu'une modification de données est validée pendant la synchronisation ;

    • DeleteErrorHandler – méthode appelée lorsqu'une erreur se produit quand une instruction DELETE est téléchargée vers ou à partir du serveur ;

    • DeleteHandler – méthode appelée lorsque des instructions DELETE sont téléchargées vers ou à partir du serveur ;

    • InsertErrorHandler – méthode appelée lorsqu'une erreur se produit quand une instruction INSERT est téléchargée vers ou à partir du serveur ;

    • InsertHandler – méthode appelée lorsque des instructions INSERT sont téléchargées vers ou à partir du serveur ;

    • UpdateConflictsHandler – méthode appelée lorsque des instructions UPDATE en conflit se produisent sur le serveur de publication et l'Abonné ;

    • UpdateDeleteConflictHandler – méthode appelée lorsque des instructions UPDATE entrent en conflit avec des instructions DELETE sur le serveur de publication et l'Abonné ;

    • UpdateErrorHandler – méthode appelée lorsqu'une erreur se produit quand une instruction UPDATE est téléchargée vers ou à partir du serveur ;

    • UpdateHandler – méthode appelée lorsque des instructions UPDATE sont téléchargées vers ou à partir du serveur.

  6. Générez le projet pour créer l'assembly du gestionnaire de logique métier.

  7. Déployez l'assembly dans le répertoire contenant le fichier exécutable de l'Agent de fusion (replmerg.exe), qui est pour une installation par défaut C:\Program Files\Microsoft SQL Server\nnn\COM, ou installez-le dans le .NET Global Assembly Cache (GAC). Vous devez installer l'assembly dans le GAC seulement si des applications autres que l'Agent de fusion requièrent l'accès à l'assembly. L'assembly peut être installé dans le GAC à l'aide de l'outil Global Assembly Cache (Gacutil.exe) fourni dans le Kit de développement .NET Framework SDK.

    Remarque

    Un gestionnaire de logique métier doit être déployé sur chaque serveur sur lequel l'Agent de fusion s'exécute, ce qui inclut le serveur IIS qui héberge replisapi.dll lors de l'utilisation de la synchronisation Web.

Pour inscrire un gestionnaire de logique métier

  1. Sur le serveur de publication, exécutez sp_enumcustomresolvers (Transact-SQL) pour vérifier que l’assembly n’a pas été inscrit en tant que gestionnaire de logique métier.

  2. Sur le serveur de distribution, exécutez sp_registercustomresolver (Transact-SQL), en spécifiant un nom convivial pour le gestionnaire de logique métier pour article_resolver, une valeur true pour @is_dotnet_assembly, le nom de l’assembly pour @dotnet_assembly_name et le nom complet de la classe qui remplace BusinessLogicModule pour @dotnet_class_name.

    Remarque

    Si l'assembly n'est pas déployé dans le même répertoire que l'exécutable de l'Agent de fusion, dans le même répertoire que l'application qui démarre de façon synchrone l'Agent de fusion ni dans le GAC (Global Assembly Cache), vous devez spécifier le chemin d'accès complet avec le nom de l'assembly pour @dotnet_assembly_name. Lorsque vous utilisez la synchronisation Web, vous devez spécifier l'emplacement de l'assembly sur le serveur Web.

Pour utiliser un gestionnaire de logique métier avec un nouvel article de table

  1. Exécutez sp_addmergearticle (Transact-SQL) pour définir un article, en spécifiant le nom convivial du gestionnaire de logique métier pour @article_resolver. Pour plus d’informations, consultez définir un Article.

Pour utiliser un gestionnaire de logique métier avec un article de table existant

  1. Exécutez sp_changemergearticle (Transact-SQL) en spécifiant @publication, @article, en affectant la valeur article_resolver à property et en affectant le nom convivial du gestionnaire de logique métier à @value.

Exemples (programmation de la réplication)

Cet exemple illustre un gestionnaire de logique métier qui crée un journal d'audit.

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

L'exemple ci-dessous inscrit un assembly du gestionnaire de logique métier sur le serveur de distribution et modifie un article de fusion existant afin d'utiliser cette logique métier personnalisée.

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

Utilisation d'objets RMO (Replication Management Objects)

Pour créer un gestionnaire de logique métier

  1. Dans Microsoft Visual Studio, créez un projet pour l'assembly .NET contenant le code qui implémente le gestionnaire de logique métier.

  2. Ajoutez des références au projet pour les espaces de noms ci-dessous.

    Référence d'assembly Emplacement
    Microsoft.SqlServer.Replication.BusinessLogicSupport C:\Program Files\Microsoft SQL Server\nnn\COM (installation par défaut)
    System.Data GAC (composant du .NET Framework)
    System.Data.Common GAC (composant du .NET Framework)
  3. Ajoutez une classe qui remplace la classe BusinessLogicModule .

  4. Implémentez la propriété HandledChangeStates pour indiquer les types de modifications qui sont gérés.

  5. Remplacez une ou plusieurs des méthodes suivantes de la classe BusinessLogicModule :

    • CommitHandler – méthode appelée lorsqu'une modification de données est validée pendant la synchronisation ;

    • DeleteErrorHandler – méthode appelée si une erreur se produit pendant qu'une instruction DELETE est téléchargée vers ou à partir du serveur ;

    • DeleteHandler – méthode appelée lorsque des instructions DELETE sont téléchargées vers ou à partir du serveur ;

    • InsertErrorHandler – méthode appelée si une erreur se produit pendant qu'une instruction INSERT est téléchargée vers ou à partir du serveur ;

    • InsertHandler – méthode appelée lorsque des instructions INSERT sont téléchargées vers ou à partir du serveur ;

    • UpdateConflictsHandler – méthode appelée lorsque des instructions UPDATE en conflit se produisent sur le serveur de publication et l'Abonné ;

    • UpdateDeleteConflictHandler – méthode appelée lorsque des instructions UPDATE entrent en conflit avec des instructions DELETE sur le serveur de publication et l'Abonné ;

    • UpdateErrorHandler – méthode appelée si une erreur se produit pendant qu'une instruction UPDATE est téléchargée vers ou à partir du serveur ;

    • UpdateHandler – méthode appelée lorsque des instructions UPDATE sont téléchargées vers ou à partir du serveur.

    Remarque

    Tous les conflits d'un article qui ne sont pas gérés explicitement par votre logique métier personnalisée sont gérés par le programme de résolution par défaut pour l'article.

  6. Générez le projet pour créer l'assembly du gestionnaire de logique métier.

Pour inscrire un gestionnaire de logique métier

  1. Créez une connexion au serveur de distribution en utilisant la classe ServerConnection .

  2. Créez une instance de la classe ReplicationServer. Passez l'objet ServerConnection créé à l'étape 1.

  3. Appelez EnumBusinessLogicHandlers et vérifiez l'objet ArrayList retourné pour vous assurer que l'assembly n'a pas déjà été inscrit en tant que gestionnaire de logique métier.

  4. Créez une instance de la classe BusinessLogicHandler. Spécifiez les propriétés suivantes :

    • DotNetAssemblyName – nom de l'assembly .NET. Si l'assembly n'est pas déployé dans le même répertoire que l'exécutable de l'Agent de fusion, dans le même répertoire que l'application qui démarre de façon synchrone l'Agent de fusion ou dans le GAC, vous devez inclure le chemin d'accès complet avec le nom de l'assembly. Vous devez inclure le chemin d'accès complet avec le nom de l'assembly lorsque vous utilisez un gestionnaire de logique métier avec la synchronisation Web ;

    • DotNetClassName – nom complet de la classe qui remplace BusinessLogicModule et implémente le gestionnaire de logique métier ;

    • FriendlyName – nom convivial que vous utilisez lorsque vous accédez au gestionnaire de logique métier ;

    • IsDotNetAssembly – valeur true.

Pour déployer un gestionnaire de logique métier

  1. Déployez l'assembly sur le serveur où l'Agent de fusion s'exécute, dans l'emplacement de fichier qui a été spécifié quand le gestionnaire de logique métier a été inscrit sur le serveur de distribution. Pour un abonnement par extraction, l'agent s'exécute sur l'Abonné, alors que pour un abonnement par émission de données, il s'exécute sur le serveur de distribution. Lorsque vous utilisez la synchronisation Web, l'agent s'exécute sur le serveur Web. Si le chemin d'accès complet n'a pas été inclus avec le nom de l'assembly lorsque le gestionnaire de logique métier a été inscrit, déployez l'assembly dans le même répertoire que l'exécutable de l'Agent de fusion, dans le même répertoire que l'application qui démarre de façon synchrone l'Agent de fusion. Vous pouvez installer l'assembly dans le GAC si plusieurs applications utilisent le même assembly.

Pour utiliser un gestionnaire de logique métier avec un nouvel article de table

  1. Créez une connexion au serveur de publication en utilisant la classe ServerConnection .

  2. Créez une instance de la classe MergeArticle. Définissez les propriétés suivantes :

  3. Appelez la méthode Create . Pour plus d’informations, consultez définir un Article.

Pour utiliser un gestionnaire de logique métier avec un article de table existant

  1. Créez une connexion au serveur de publication en utilisant la classe ServerConnection .

  2. Créez une instance de la classe MergeArticle.

  3. Définissez les propriétés Name, PublicationNameet DatabaseName .

  4. Définissez la connexion créée à l'étape 1 pour la propriété ConnectionContext .

  5. Appelez la méthode LoadProperties pour obtenir les propriétés de l'objet. Si cette méthode retourne false, soit les propriétés de l'article ont été définies de manière incorrecte à l'étape 3, soit l'article n'existe pas. Pour plus d'informations, voir View and Modify Article Properties.

  6. Définissez le nom convivial du gestionnaire de logique métier pour ArticleResolver. Il s'agit de la valeur de la propriété FriendlyName spécifiée lors de l'inscription du gestionnaire de logique métier.

Exemples (RMO)

Cet exemple illustre un gestionnaire de logique métier qui enregistre les informations sur les insertions, les mises à jour et les suppressions sur l'Abonné.

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

Cet exemple illustre l'inscription d'un gestionnaire de logique métier sur le serveur de distribution.

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

Cet exemple illustre la modification d'un article existant pour utiliser le gestionnaire de logique métier.

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