Quantcast
Channel: SQL Server Replication forum
Viewing all articles
Browse latest Browse all 4054

What did I do wrong in duplicating this BLH example

$
0
0

Question: What did I do wrong in the below 6 steps?

I unsuccessfully attempted to duplicate the example at:

http://blogs.msdn.com/b/sql_pfe_blog/archive/2011/04/08/merge-replication-conflict-detection-vs-conflict-resolution-part-2-custom-business-logic-handler.aspx

(Note: I removed all references to my computer from the images.)

(1)  Created SamplePubDB with 2 tables:SEE reply images for steps

(2)  Steps to create publication:SEE reply images for actual steps

(3) Create the Subscription: SEE reply images for actual steps

(4) With Visual Studio 2010, I built file Sample_t1MergeConflictHandler_Lib.dll (fromSampleMergeHandler.zip)and placed it at this location on my PC: c:\temp\Sample_t1MergeConflictHandler_Lib.dll. The only change that I made to the source code (see below) was to make the line numbers match what is shown on the Internet site (I concatenated 2 lines to make 1 in a few places).

I did nothing else to the Visual Studio C# code. I did nothing to the compilation settings after installing Visual Studio. I added the reference as directed and built it. The output from the Build on Visual Studio 2010:

------ Rebuild All started: Project: Sample_t1MergeConflictHandler_Lib, Configuration: Debug Any CPU ------
  Sample_t1MergeConflictHandler_Lib -> C:\Users\...\documents\visual studio 2010\Projects\Sample_t1MergeConflictHandler_Lib\Sample_t1MergeConflictHandler_Lib\bin\Debug\Sample_t1MergeConflictHandler_Lib.dll
========== Rebuild All: 1 succeeded, 0 failed, 0 skipped ==========

The produced dll is only 8 kilobytes in size!

using System;
 using System.Collections.Generic;
 using System.Linq;
 using System.Text;
//Need to include replication objects as well as System.Data 
//for direct connections to SQL
 using Microsoft.SqlServer.Replication.BusinessLogicSupport;
 using System.Data;
 using System.Data.SqlClient;
 using System.Data.Common;
namespace Sample_t1MergeConflictHandler_Lib
 {
    public class C_t1ConflictHandler :
  Microsoft.SqlServer.Replication.BusinessLogicSupport.BusinessLogicModule
     {
         private const string PUBSVR_ESC = @"mypubservername\kilimanjaroInstance";
         private const string SUBSVR_ESC = @"mysubservername\kilimanjaroInstance";
         private const string PUBDB = "SamplePubDB";
         private const string SUBDB = "SampleSubDB";
        // Declare what types of row changes, conflicts, or errors to handle.
         override public ChangeStates HandledChangeStates
         {
             get
             {
                 // Handle Subscriber inserts, updates and deletes.
                 return ChangeStates.UpdateConflicts;
             }
         }
        //User provided function used to gather the row version and each of its column versions at the publisher
         //and subscriber
         //row and column versions are acquired by a call to sp_showrowreplicainfo via a direct connection to
         //the publisher + subscriber
         public void Populate_ConflictRow_VersionInfo(string rowguid, ref DataSet PubReplicaDataSet,
             ref DataSet SubReplicaDataSet, ref int pVer, ref int sVer)
         {
             //publisher connection
             SqlConnection PubConn = new SqlConnection("Data Source=" + PUBSVR_ESC + ";Initial Catalog=" + PUBDB + ";"+ "Integrated Security=SSPI");
             //subscriber connection
             SqlConnection SubConn = new SqlConnection("Data Source=" + SUBSVR_ESC + ";Initial Catalog=" + SUBDB + ";"+ "Integrated Security=SSPI");
             //adapter used for both publisher and subscriber connections
             SqlDataAdapter tempReplicaDataAdapter = new SqlDataAdapter();
            ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
             // Collect Publisher row + column versions which are supplied via sp_showrowreplicainfo
             tempReplicaDataAdapter.SelectCommand = new SqlCommand("exec sp_showrowreplicainfo @tablename='t1', @rowguid = '" + rowguid + "', @show = 'BOTH'",
                 PubConn);
             tempReplicaDataAdapter.Fill(PubReplicaDataSet);
             // Tables[0] = Contains Publisher row versions
             // Tables[1] = Contains Publisher columns versions
             PubConn.Close();
             //filter row versions to get version for the publisher
             DataRow pVerRow = PubReplicaDataSet.Tables[0].Select("server_name='" + PUBSVR_ESC + "' and db_name='"+ PUBDB + "' and current_state='y'")[0];
             // Extract current publisher row version into the pVer variable
             pVer = Int32.Parse(pVerRow["version"].ToString());
            ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
             // Collect Subscriber row + column versions which are supplied via sp_showrowreplicainfo
             tempReplicaDataAdapter.SelectCommand = new SqlCommand("exec sp_showrowreplicainfo @tablename='t1', @rowguid = '" + rowguid + "', @show = 'BOTH'",
                 SubConn);
             tempReplicaDataAdapter.Fill(SubReplicaDataSet);
             // Tables[0] = Contains Subscriber row versions
             // Tables[1] = Contains Subscriber columns versions
             SubConn.Close();
             //filter row versions to get version for the subscriber //note filter which is getting only columns modified at the subscriber
             DataRow sVerRow = SubReplicaDataSet.Tables[0].Select("server_name='" + SUBSVR_ESC + "' and db_name='" + SUBDB + "' and current_state='y'")[0];
            // Extract current subscriber row version into the sVer variable
             sVer = Int32.Parse(sVerRow["version"].ToString());
         }



        //Provides logic to handle an update conflict between the publisher and a subscriber
         //This first calls Populate_ConflictRow_VersionInfo to collect the row,column versions
         //It then builds the final column values (into the customDataSet object) for the row,
         //based on the following logic:
         //Loop through each column
         //if the column was changed at the publisher and not the subscriber 
        //- the final row will include the publisher's column value
         //if the column was changed at the subscriber and not the publisher
         //- the final row will include the subscriber's column value
         //if the column was changed at both publisher and subscriber, the logic below chooses the publisher column value
         //The final row values are placed in  customDataSet
         //Both the publisher and subscriber will be sent the row built into customDataSet
         public override ActionOnUpdateConflict UpdateConflictsHandler(
             DataSet publisherDataSet, DataSet subscriberDataSet, ref DataSet customDataSet,
             ref ConflictLogType conflictLogType, ref string customConflictMessage,
             ref int historyLogLevel, ref string historyLogMessage)
         {
             //Initially load customDataSet with the values from the publisher row
             //Ultimately, customDataSet will contain the "merged" row to be sent to pub/sub
             customDataSet = publisherDataSet.Copy();
            // Tables[0] = Contains the first (and only) conflict row 
            DataRow rowCustom    = customDataSet.Tables[0].Rows[0];
             DataRow rowPublisher = publisherDataSet.Tables[0].Rows[0];
             DataRow rowSubscriber = subscriberDataSet.Tables[0].Rows[0];
            //Obtain rowguid for the conflict row
             String rowguidVal = rowPublisher["rowguid"].ToString();
            // Populate replica datasets to gather row,column versions at the publisher/subscriber
             //Replica datasets contain sp_showrowreplicainfo output,
             //which contains information about last row version and column version for each column
             // Tables[0] =  row versions
             // Tables[1] =  columns versions. This table has one row for each column in the table.
             DataSet PubReplica = new DataSet();
             DataSet SubReplica = new DataSet();
             int pubVersion = -1;
             int subVersion = -1;
             Populate_ConflictRow_VersionInfo(rowguidVal, ref PubReplica, ref SubReplica, ref pubVersion, ref subVersion);
            //Loop through ALL columns in the publisher row - the row for which the conflict occurred
             //and decide which column value should "win", the final row will contain all changed values
             //and if the same column was changed at publisher/subscriber it will choose the publisher change
             for (int i = 0; i < rowPublisher.ItemArray.Length; i++)
             {
                     // Initially set currCol to publisher column value
                     DataColumn currCol = rowPublisher.Table.Columns[i];
                    //Filter to extract column version info for current column
                     //There is one row in the PubReplica.Tables[1] dataset for each modified column in the user table
                     DataRow[] pubColumnReplicaInfoRows =
                         PubReplica.Tables[1].Select("version=" + pubVersion + " and db_name='"+ PUBDB + "' and colname = '" + currCol.ColumnName + "'");
                     //If pubColumnReplicaInfo is null it means this column was not updated at the publisher
                     DataRow pubColumnReplicaInfo = null;
                     if (pubColumnReplicaInfoRows.Length > 0)
                         pubColumnReplicaInfo = pubColumnReplicaInfoRows[0];
                    //Extract subscriber column version for current column
                     //There is one row in the SubReplica.Tables[1] dataset for each modified column in the user table
                     DataRow[] subColumnReplicaInfoRows =
                         SubReplica.Tables[1].Select("version=" + subVersion + " and db_name='"+ SUBDB + "' and colname = '" + currCol.ColumnName + "'");
                     //If subColumnReplicaInfo is null it means this column was not updated at the subscriber
                     DataRow subColumnReplicaInfo = null;
                     if (subColumnReplicaInfoRows.Length > 0)
                         subColumnReplicaInfo = subColumnReplicaInfoRows[0];
                    //If col was not changed at the publisher or subscriber (both are null).
                     //keep values in rowCustom (which has publisher data copy)
                     if (pubColumnReplicaInfo == null && subColumnReplicaInfo == null)
                         continue;
                     //if col was changed at sub(sub!=null) but not at pub(pub=null),keep sub change
                     else if ( subColumnReplicaInfo != null && pubColumnReplicaInfo == null)
                         rowCustom[i] = rowSubscriber[i];
                     //if col was changed at pub(pub!=null) but not at sub,keep pub change
                     else if (subColumnReplicaInfo == null && pubColumnReplicaInfo != null)
                         rowCustom[i] = rowPublisher[i];
                     //If col was changed on both pub and sub, keep publisher version, publisher wins
                     //you can add a timestamp if you want to ensure you keep the first change
                     else if (subColumnReplicaInfo != null && pubColumnReplicaInfo != null)
                         rowCustom[i] = rowPublisher[i];
             }
            //if choose ConflictLogNone option
             //indication of conflict having occurred would only be in msmerge_history
             //conflictLogType = ConflictLogType.ConflictLogNone;
            //This will mark subscriber row as the  loser, setting this in order to show conflicts during sync status
             //otherwise sync status will not indicate a conflict occurred
             //note this affects the winning row output in dbo.MSmerge_conflict_SamplePub1_t1 as well
             //in this table. the winning row will show as the entire publisher row
             //, which is not what this custom handler actually returns
             //as the winning row
             conflictLogType = ConflictLogType.ConflictLogSubscriber;
            customConflictMessage = string.Format("T1 custom handler - winning row may contain changes from both pub,sub rows.", rowguidVal);
             historyLogMessage = string.Format("A conflict was detected and handled by the custom handler for table T1, row = {0} ", rowguidVal);
             historyLogLevel = 1;
             return ActionOnUpdateConflict.AcceptCustomConflictData;
         }
    }
}


(5)   To register the resolver at the distributor, I highlighted the server with the publication, and opened a new query. I executed the contents of the (SampleMergeHandler.zip) InstallBusinnessLogicHandler file (see T-SQL below) .

use distribution
 DECLARE @publication AS sysname;
 DECLARE @article AS sysname;
 DECLARE @friendlyname AS sysname;
 DECLARE @assembly AS nvarchar(500);
 DECLARE @class AS sysname;
 SET @publication = N'SamplePub1';
 SET @article = N't1';
 SET @friendlyname = N't1ConflictHandler';
 SET @assembly = N'c:\temp\Sample_t1MergeConflictHandler_Lib.dll';
 SET @class = N'Sample_t1MergeConflictHandler_Lib.C_t1ConflictHandler';
 exec sp_unregistercustomresolver @article_resolver = @friendlyname
 exec sp_registercustomresolver @article_resolver = @friendlyname
     , @resolver_clsid = NULL
     , @is_dotnet_assembly = 'true'
     , @dotnet_assembly_name =  @assembly
     , @dotnet_class_name =  @class

use SamplePubDB
 EXEC sp_changemergearticle 
 @publication = @publication, 
 @article = @article,
  @property = N'article_resolver', 
 @value = @friendlyname,
  @force_invalidate_snapshot = 0,
  @force_reinit_subscription = 0;
 EXEC sp_changemergearticle 
 @publication = @publication, 
 @article = @article,
  @property = N'verify_resolver_signature', 
 @value = 0,
  @force_invalidate_snapshot = 0,
  @force_reinit_subscription = 0;
 go
 


(6)  I reproduced the conflict (see T-SQL below) as outlined in the file ReproduceConflict (both servers changed col c3 on row 1), and waited for conflict resolution, but nothing on either databases changed. When I right clicked on the publication and choose view conflicts, I received a message saying no conflicts occurred.



--On Subscriber (Client)
use SampleSubDB
go

update t1 set c3=333,c4=444 where c1=1
go

update t2 set c3=333,c4=444 where c1=1
go

--On Publication Server
use SamplePubDB
go

update t1 set c2=2,c3=3 where c1=1
go

update t2 set c2=2,c3=3 where c1=1
go









Viewing all articles
Browse latest Browse all 4054

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>