Export (0) Print
Expand All
Creating Audit Tables, Invoking COM Objects, and More
Exception-handling Techniques
Exploring SQL Server Triggers: Part 2
Deliver User-Friendly Reports from Your Application with SQL Server Reporting Services
SQL Server: Display Your Data Your Way with Custom Renderers for Reporting Services
Updating Data in Linked Servers, Information Schema Views, and More
XML Features in SQL Server 2000
Expand Minimize

Creating Merge Replication Custom Conflict Resolvers Using Visual Basic

SQL Server 2000
 

Andrea Fox
Microsoft Corporation

April 2001

Summary: This article describes how to create a Visual Basic application for merge replication that will handle both conflict and non-conflict changes between the affected tables. The article documents a .DLL that, when referenced from the Visual Basic project, contains many methods that enable the developer to gather information from the Publisher and Subscriber sites to successfully resolve conflicting data and place the correct data at both locations. (39 printed pages)

Note   This article contains information from the SQL Server 2000 Resource Kit. You can purchase the kit at the Microsoft Press Web site.

Contents

Introduction
Using the Microsoft SQL Replication Conflict Resolver Library
Registering a Custom Conflict Resolver
Conclusion
Appendix: How to Run the Samples

Introduction

Microsoft® SQL Server™ 2000 includes several merge replication conflict resolution options, such as a merge text conflict resolver, an averaging conflict resolver, several DATETIME conflict resolvers, and other commonly used conflict resolvers. In most merge replication conflicts, one of these built-in solutions will resolve the conflicts that may occur as part of merge replication. However, when a unique business need requires customized logic to determine the final merged data at Publisher and Subscriber sites, you can write an application that resolves the conflict according to specified business rules.

When writing custom conflict resolver applications, you will need to:

  • Define the conditions you want to handle with the application logic.
  • Determine the type of change condition that is currently being handled.
  • Initialize the source and destination information.
  • Gather column information as to which column has changed if any special resolver tracker has been implemented, such as column tracking.
  • Determine what phase the merge process is in: upload or download.
  • Determine what data should be the winning data in a change.
  • Propagate the data to the matching table on the other server and overwrite the losing data.

To help you accomplish these steps, this article includes sections on using the Microsoft SQL Replication Conflict Resolver Library, learning to register a custom conflict resolver, and examining some merge replication custom conflict resolver samples.

The merge replication conflict resolvers discussed in this article are written in Microsoft Visual Basic®. An intermediate-level understanding of Visual Basic, as well as a beginning understanding of Component Object Model (COM) and Interface Definition Language (IDL), is assumed.

Using the Microsoft SQL Replication Conflict Resolver Library

To write a merge replication custom conflict resolver, use the Microsoft SQL Replication Conflict Resolver Library. It is a DLL file that is installed as part of SQL Server 2000. This library of methods enables your application to respond as changes are applied during synchronization.

The Microsoft SQL Replication Conflict Resolver Library contains methods available to retrieve data and commit data between servers. To use the library in your merge replication custom conflict resolver applications, you need to:

  • Include the Microsoft SQL Replication Conflict Resolver Library in a Visual Basic application.
  • Implement the IVBCustomResolver interface.
  • Understand the methods in the IReplRowChange interface.
  • Understand the methods in the IConnectionInfo interface.
  • Understand the enumerated constants in the library.

The Microsoft SQL Replication Conflict Resolver Library file is replrec.dll. It provides methods to return information from SQL Server to the application, including information about changes, details on how the rows are affected and which columns differ, plus Publisher and Subscriber connection information.

The replrec.dll file also contains methods that perform actions, such as evaluating a change to a table, determining winning data and placing it into a table, or removing data from a table.

The following diagram shows a COM representation of the Microsoft SQL Replication Conflict Resolver Library object and its three interfaces: IVBCustomResolver, IReplRowChange, and IConnectionInfo.

Aa902635.custom_resolver_figure_1(en-us,SQL.80).gif

Figure 1. A COM representation of the Microsoft SQL Replication Conflict Resolver Library object and its three interfaces

Adding the Microsoft SQL Replication Conflict Resolver Library to Visual Basic

To include the Microsoft SQL Replication Conflict Resolver Library in a Visual Basic application, your application must be an ActiveX® DLL project type, and you must add the SQL Merge Conflict Resolver component to the project.

If you install SQL Server 2000 to the default location, the SQL Merge Conflict Resolver component will be in C:\Program Files\Microsoft SQL Server\80\COM\. To add the component to your project, click the Project | References menu. In the References dialog box, click Microsoft SQL Replication Conflict Resolver Library.

ComponentReferenceLibrary
SQL Merge Conflict ResolverMicrosoft SQL Replication Conflict Resolver LibraryReplrec.dll

To get detailed information about the component after it has been added to the project in Visual Basic, inspect its type library in the Object Browser. In the Library menu, select the SQLResolver library. In the Classes pane, the SQL Resolver shows the three interfaces, IVBCustomResolver, IConnectionInfo, and IReplRowChange, as well as several predefined constants.

IVBCustomResolver Interface

The IVBCustomResolver interface, used in a new class module by using the Implements keyword, contains two methods that must be coded by the developer in the application: the GetHandledStates method and the Reconcile method.

GetHandledStates Method

Method Syntax

GetHandledStates(ResolverBm As Long)

This method enables you to list the conditions that the resolver will handle. The REPOLE_CHANGE_TYPE enumerations listed become a logical OR of the change types the resolver supports. The GetHandledStates method is invoked once when the table is loaded during the merge process. Then, for every change that needs to be propagated, the custom resolver is invoked if the change is a state that is handled by the resolver.

Although this process is commonly referred to as a conflict resolver, the changes that can be handled include non-conflict changes, such as new rows inserted at the Subscriber. All the available conflict and non-conflict changes that can be handled are listed in the REPOLE_CHANGE_TYPE enumeration. For a complete list of change types available, see the Constants topic later in this article.

Reconcile Method

Method Syntax

Sub Reconcile(pRowChange As IReplRowChange, dwFlags As Long, pvReserved As IReplRowChange)

Use the Reconcile method to define the business logic that determines what data is applied to the losing table.

The important parameter for the Reconcile method is a referenc e to an IReplRowChange object, shown as the variable pRowChange in the method syntax. IReplRowChange is defined in an include file, sqlres.h. Through the supporting methods available in IReplRowChange, you can determine the columns in conflict, examine the conflicting data, and update the appropriate table with the winning data based on the criteria established in your Reconcile method code.

For best results when using the Reconcile method, it is important to understand the different stages of the merge process. The first stage that occurs is the upload of changes from the Subscriber to the Publisher. The second stage is a download of changes from the Publisher to the Subscriber. During the upload, the Subscriber is considered as the source of the change, and the Publisher is the destination. During the download, the Publisher is considered the source and the Subscriber is the destination. This means the variables that designate source and destination change depending on the stage of the merge process.

When a custom resolver is a Visual Basic application, conflicts are resolved immediately after the application executes the appropriate resolution action. If the user wants to review the changes before implementation, and then either accept the resolution or resubmit the changes, he or she can do so. The option is available to write the losing row to a conflict table named conflict_<PublicationName>_<ArticleName>_usertablename, and to apply the winning row to the appropriate table. The custom resolver logs a special message to the conflict table that provides context information, which the user can evaluate before making a final decision about accepting the changes.

During coding in the Reconcile method, you will usually use some of the enumerations that are available as constants defined in the SQL Merge Conflict Resolver library component. It is recommended you use symbols for these enumerations whenever possible, instead of using hard-coded constants.

IReplRowChange and IConnectionInfo Interfaces

The IReplRowChange and IConnectionInfo interfaces provide methods that can be called during use of your merge replication custom conflict resolver application. The methods provide three functions:

  • Getting table, column, and connection information for the Publisher and Subscriber
  • Getting conflict information
  • Performing resolution actions

Here you will find a description of what each method does, as well as the Visual Basic signature and the IDL semantics for each method. The IDL semantics are provided as a quick reference for parameter direction.

IReplRowChange Interface and Methods

The IReplRowChange interface contains methods that deliver core replication functionality, such as the ability to query column values and to copy rows from source database to destination database. The methods available in this interface are called from the custom resolver application to return data used in decision-making logic, or to gather information about the database and tables and about the column in conflict.

Many of the methods use a ColumnID parameter. The ColumnID contains a number that represents the position of the column in the table, ranging from 1 to <number of columns>. This number is used as a parameter to methods instead of the column name.

If the table is vertically partitioned, a column position in the table at the Subscriber does not necessarily match what the column position value would be in the publishing table at the Publisher. For example, if a table contains five columns, but column 3 is not published, ColumnID=4 corresponds to the last column of the table. The following sections give the Visual Basic and IDL signatures of the methods.

Summary of Methods in IReplRowChange

MethodDescription
CopyColumnFromSource Sets the destination column to contain the same value as the respective source column.
CopyRowFromSource Sets the destination row to contain the same data as is contained in the source row.
DeleteRow Deletes the destination row.
DoDummyUpdate Updates source or destination row meta data. This allows a subsequent merge process to pick up a change as a new change.
ForceRememberChangeCalled before invoking IReplRowChange operations that alter the destination row.
GetChangeType Returns information regarding the type of change that occurred.
GetColumnAttributesReturns a bitmap indicating if column is an identity column and/or updatable.
GetColumnDatatype Returns the column type.
GetColumnName Returns the column name.
GetColumnStatus Returns the column status.
GetDestinationColumnValue Returns the value of the column from the destination table.
GetDestinationConnectionInfo Returns connection information about the destination.
GetDestinationOwnerNameReturns the owner name of the destination table.
GetErrorInfo Returns a detailed error code and description if the resolver has been invoked to handle an error situation.
GetExtendedError Returns additional information about the error, such as whether the error was a duplicate key or a unique index violation.
GetNumColumns Returns the number of columns in the base table.
GetPriorityWinner Returns a value indicating which one has the higher priority, the source or the destination.
GetResolverProcedureName Returns the resolver-specific information (for example, a column name).
GetRowGuidColName Returns the name of the column in the base table that is the uniqueidentifier column used in merge replication.
GetRowIdentifier Returns the uniqueidentifier for the row in conflict.
GetSourceColumnValue Returns the value of the column from the source table.
GetSourceConnectionInfo Returns connection information about the source.
GetTableNameReturns the name of the table in conflict.
GetTableOwnerName Returns the owner name of the table.
InsertRow Inserts the row at the destination.
LogConflict Indicates whether the conflict should be logged, where, and what message to include.
LogError Logs the error and an optional description.
UpdateRow Updates the destination row.

Methods in the IReplRowChange Interface

CopyColumnFromSource(
   ColumnId As Long)

Invoke this method if the corresponding destination column should be set to the same value contained in the source column referenced by the input parameter ColumnID.

IDL Semantics

   HRESULT CopyColumnFromSource (INTEGERTYPE ColumnId)

CopyRowFromSource()

Invoke this method if the source is the conflict winner. The row at the destination will then be set to the same values as the source row.

IDL Semantics

   HRESULT CopyRowFromSource ()

DeleteRow()

Invoke this method to delete the row at the destination. This method does not automatically delete the row at the source. It is used if a delete at the source conflicts with an update at the destination, and the delete is the winner.

IDL Semantics

   HRESULT DeleteRow ()

DoDummyUpdate(
   fUpLineage As REPOLE_BOOL, 
   fAtPublisher As REPOLE_BOOL)

Updates the row meta data at either the source or the destination. Invoke this method at one of the two nodes to make sure the resulting row is propagated back to the originating node. The parameter fAtPublisher is used to determine whether the meta data to be updated is at the Publisher or the Subscriber. Set to TRUE to update at the Publisher. Set to FALSE to update at the Subscriber.

Consider, for example, the scenario of a merge replication conflict in which the destination is the conflict winner. Calling DoDummyUpdate on the destination (having fAtPublisher=TRUE in the upload phase, FALSE otherwise) ensures the losing source row gets the values of the winning destination row as soon as it becomes the destination itself. Thus, if the Subscriber loses during upload, the DoDummyUpdate method guarantees the Publisher's row will be downloaded to the Subscriber during the next phase.

Calling the method on the source can be used in error situations, when a resend should be forced. You might need to resend, for example, when you are using a Subscriber-always-wins resolver, but the upload of the Subscriber change has failed because the Publisher inserted a row with the same primary key. The download of the inserted row from the Publisher will fail due to the same reason, leading to the deletion of the inserted row at the Publisher. Therefore, the subsequent upload of the row that originated at the Subscriber will succeed when resending is forced.

Set fUpLineage to TRUE to keep version information about the changed row. If you are unsure what value to use for the parameter, use TRUE.

IDL Semantics

   HRESULT DoDummyUpdate (
                        [in] BOOLTYPE fUpLineage, 
                        [in] BOOLTYPE fAtPublisher)

ForceRememberChange()

Call this method before invoking IReplRowChange operations that alter the destination row, such as CopyRowFromSource(), CopyColumnFromSource(), or DeleteRow(), if the conflict resolver allows an update/insert/delete at a Subscriber to win over an update/insert/delete at either the Publisher or another Subscriber that has newly merged with the Publisher.

IDL Semantics

   HRESULT ForceRememberChange()

GetChangeType(
   pChangeType As REPOLE_CHANGE_TYPE)

GetChangeType( returns what type of change occurred. The value returned is one of the enumerated constants from REPOLE_CHANGE_TYPE.

IDL Semantics

   HRESULT _stdcall GetChangeType([out] REPOLE_CHANGE_TYPE* pChangeType)

GetColumnAttributes(
   ColumnId As Long, 
   PlColumnAttributes As REPOLE_COLUMN_ATTRIBUTES)

GetColumnAttributes( Returns a bitmap of the enumerated constants from REPOLE_COLUMN_ATTRIBUTES into plColumnAttributes, for the column being referenced by the input parameter ColumnID. The bitmap indicates whether the referenced column is an identity column and/or an updatable column. It can be both. Check the results with *plColumnAttributes & REPOLEColumnAttribute_Updatable, or *plColumnAttributes & REPOLEColumnAttribute_Identity, respectively.

IDL Semantics

    HRESULT _stdcall GetColumnAttributes(
                   long ColumnId, 
                   [out] REPOLE_COLUMN_ATTRIBUTES* plColumnAttributes)

GetColumnDatatype(
   ColumnId As Long, 
   plDataType As REPOLE_SQL_DATATYPE)

GetColumnDatatype( returns a value in plDataType that indicates what type of column is being referenced by the input ColumnID parameter. The type of column returned is one of the enumerated constants from REPOLE_SQL_DATATYPE.

IDL Semantics

   HRESULT _stdcall GetColumnDatatype(
                        long ColumnId, 
                        [out] REPOLE_SQL_DATATYPE* plDataType)

GetColumnName(
   ColumnId As Long, 
   pColumnName As String, 
   cbColumnName As Long)

Returns the name of the column being referenced by the input parameter ColumnID, into pColumnName. The input parameter cbColumnName gives a buffer size for pColumnName. HRESULT returns an error if the buffer is too small.

IDL Semantics

   HRESULT _stdcall GetColumnName(
                        long ColumnId, 
                        [out] LPWSTR pColumnName, 
                        long cbColumnName)

GetColumnStatus(
   ColumnId As Long, 
   pColStatus As REPOLE_COLSTATUS_TYPE)

Returns a value into pColStatus that indicates the status of the column being referenced by the input ColumnID parameter. The status of the column returned is one of the enumerated constants from REPOLE_COLSTATUS_TYPE.

IDL Semantics

   HRESULT _stdcall GetColumnStatus(
                        long ColumnId, 
                        [out] REPOLE_COLSTATUS_TYPE* pColStatus)

GetDestinationColumnValue(
   ColumnId As Long, 
   pvBuffer, 
   cbBufferMax As Long, 
   pcbBufferActual As Long)

GetDestinationColumnValue( returns the value of the column, referenced by the ColumnID parameter, into pvBuffer. The column value returned is the value of the column at the destination table. The output parameter pcbBufferActual returns the number of bytes that the column value uses. The input parameter cbBufferMax indicates the size of pvBuffer. HRESULT returns an error if the buffer is too small.

IDL Semantics

   HRESULT _stdcall GetDestinationColumnValue(
                        long ColumnId, 
                        [out] VARIANT* pvBuffer, 
                        [in] long cbBufferMax, 
                        [out] long* pcbBufferActual)

GetDestinationConnectionInfo(
   ppDestinationConnectionInfo As IConnectionInfo, 
   pfIsPublisher As REPOLE_BOOL)

GetDestinationConnectionInfo( returns a pointer to the object that contains all the connection information about the destination in the ppDestinationConnectionInfo parameter. This variable can then be used to make all the calls in the IConnectionInfo object. For example, this is the line of code that initializes the IConnectionInfo variable from the AdditiveResolver sample:

Call rrc.GetDestinationConnectionInfo(DestConnectionInfo, DestIsPublisher)

Once initialized, the DestConnectionInfo variable can be used as the qualifier to all the methods available in the IConnectionInfo object by using:

Call DestConnectionInfo.GetServerName(stSubscriber, Len(stSubscriber))

The GetDestinationConnectionInfo method also returns a Boolean into pfIsPublisher that indicates whether or not the Publisher is the destination. TRUE indicates that the system is in the upload phase and the Publisher is the destination, not the source. FALSE indicates that the system is in the download phase and the Publisher is the source, not the destination.

IDL Semantics

   HRESULT _stdcall GetDestinationConnectionInfo(
                   [out] IConnectionInfo** ppDestinationConnectionInfo, 
                   [out] REPOLE_BOOL* pfIsPublisher)

GetDestinationOwnerName(
   pDestOwnerName As String, 
   cbDestName As Long)

GetDestinationOwnerName( returns the name of the owner of the destination table into pDestOwnerName. The input parameter cbDestName indicates the size of pDestOwnerName. HRESULT returns an error if the buffer is too small.

IDL Semantics

   HRESULT _stdcall GetDestinationOwnerName(
                        [out] LPWSTR pDestOwnerName, 
                        long cbDestName)

GetErrorInfo(
   pErrCode As Long, 
   pErrText As String)

GetErrorInfo( returns the error code into pErrCode and the string description of the error into pErrText.

IDL Semantics

   HRESULT _stdcall GetErrorInfo(
                        [out] long* pErrCode, 
                        LPWSTR pErrText)

GetExtendedError(
   pExtError As REPOLE_EXTENDEDERROR_TYPE)

GetExtendedError( returns additional information about the error, such as whether the error was a duplicate key or a unique index violation. The value returned is one of the enumerated constants from REPOLE_EXTENDEDERROR_TYPE.

IDL Semantics

   HRESULT GetExtendedError ([out] REPOLE_EXTENDEDERROR_TYPE* pExtError)

GetNumColumns(
   pdwColumnCount As Long)

GetNumColumns( finds the number of columns in the base table. If there is a vertical partition, the number of columns in the table may not match the number of columns in the underlying table. For example, if a table contains five columns, but column 3 is not published, ColumnID=4 corresponds to the last column of the table.

IDL Semantics

   HRESULT _stdcall GetNumColumns([out] long* pdwColumnCount)

GetPriorityWinner(
   pPriorityWinner As REPOLE_PRIORITY_TYPE)

GetPriorityWinner( returns a value indicating if the source or destination has the higher priority, using one of the enumerated constants from REPOLE_PRIORITY_TYPE. This method always returns the source or the destination as the winner. With equal priorities, the default winner is the Publisher. The value REPOLE_Priority_Equal is not used.

IDL Semantics

   HRESULT _stdcall GetPriorityWinner(
                        [out] REPOLE_PRIORITY_TYPE*
                        pPriorityWinner)

GetResolverProcedureName(
   pResolverProcedureName As String, 
   cbResolverProcedureName As Long)

GetResolverProcedureName( retrieves resolver-specific information into pResolverProcedureName. For example, if the merge conflict process is focused on a particular column that has been specified in the Resolver tab of the table properties, this method returns that column name. cbResolverProcedureName indicates the size of the buffer. HRESULT returns an error if the buffer is too small.

IDL Semantics

   HRESULT _stdcall GetResolverProcedureName(
                        [out] LPWSTR pResolverProcedureName, 
                        long cbResolverProcedureName)

GetRowGuidColName(
   pRowGuidColName As String, 
   cbRowGuidColName As Long)

GetRowGuidColName( retrieves the name of the column in the base table, which is the uniqueidentifier column used in merge replication. The input parameter cbRowGuidColName indicates the size of the buffer. HRESULT returns an error if the buffer is too small.

IDL Semantics

   HRESULT _stdcall GetRowGuidColName(
                        [out] LPWSTR pRowGuidColName, 
                        long cbRowGuidColName)

GetRowIdentifier(
   pRowGuid As REPLGUID)

GetRowIdentifier( returns the row uniqueidentifier, which is used to uniquely identify the row.

IDL Semantics

   HRESULT _stdcall GetRowIdentifier([out] REPLGUID* pRowGuid)

GetSourceColumnValue(
   ColumnId As Long, 
   pvBuffer, 
   cbBufferMax As Long, 
   pcbBufferActual As Long)

GetSourceColumnValue( returns the value of the column, referenced by the ColumnID parameter, into pvBuffer. The column value returned is the value of the column at the source table. The parameter pcbBufferActual returns the number of bytes the column value uses. The input parameter cbBufferMax indicates the size of pvBuffer. HRESULT returns an error if the buffer is too small.

IDL Semantics

   HRESULT _stdcall GetSourceColumnValue(
                        long ColumnId, 
                        [out] VARIANT* pvBuffer, 
                        [in] long cbBufferMax, 
                        [out] long* pcbBufferActual)

GetSourceConnectionInfo(
   ppSourceConnectionInfo As IConnectionInfo, 
   pfIsPublisher As REPOLE_BOOL)

GetSourceConnectionInfo( returns a pointer to the object that contains all the connection information about the source in the ppSourceConnectionInfo parameter. This variable can then be used to make all the calls in the IConnectionInfo object. For example, this is the line of code that initializes the IConnectionInfo variable from the AdditiveResolver sample:

Call rrc.GetSourceConnectionInfo(SrcConnectionInfo, SrcIsPublisher)

After it has been initialized, the SrcConnectionInfo can be used as the class identifier to call all the methods available in the IConnectionInfo object by using:

Call SrcConnectionInfo.GetServerName(stPublisher, en(stPublisher))

The GetSourceConnectionInfo method also returns a Boolean that indicates whether or not the Publisher is the source. TRUE indicates that the system is in the download phase and the Publisher is the source, not the destination. FALSE indicates that the system is in the upload phase and the Publisher is the destination, not the source.

IDL Semantics

   HRESULT _stdcall GetSourceConnectionInfo(
                        [out] IConnectionInfo** ppSourceConnectionInfo, 
                        [out] REPOLE_BOOL* pfIsPublisher)

GetTableName(
   pTableName As String, 
   cbTableName As Long)

GetTableName( returns the name of the table, against which the conflict is occurring, into pTableName. The parameter cbTableName is an input parameter that gives a buffer size for pTableName. HRESULT returns an error if the buffer is too small.

IDL Semantics

   HRESULT _stdcall GetTableName(
                        [out] LPWSTR pTableName, 
                        long cbTableName)

GetTableOwnerName(
   pOwnerName As String,
   cbOwnerName As Long)

GetTableOwnerName( returns the name of the table owner into pOwnerName. The input parameter cbOwnerName gives a buffer size for pOwnerName. HRESULT returns an error if the buffer is too small.

IDL Semantics

   HRESULT _stdcall GetTableOwnerName(
                        [out] LPWSTR pOwnerName, 
                        long cbOwnerName)

InsertRow()

InsertRow() inserts the row at the destination.

IDL Semantics

   HRESULT InsertRow ()

LogConflict(
   bLogSourceConflict As REPOLE_BOOL, 
   ConflictType As REPOLE_CONFLICT_TYPE, 
   bOnlyLogIfUpdater As REPOLE_BOOL,
   [pszConflictMessage As String], 
   [bLogConflictOnUpload As REPOLE_BOOL])

LogConflict( takes input parameters that indicate whether the conflict should be logged, specifications on what message to log, and where the log is kept.

The first parameter, bLogSourceConflict, is a Boolean indicating whether or not to log the conflict at the source.

If bOnlyLogIfUpdater is set to TRUE, a conflict is logged only if the database that has the losing version actually created that version. Thus, if a Publisher propagates a change to multiple Subscribers and then receives a change that causes a conflict and makes the version at all of those Subscribers lose, a conflict will not be logged for each of those Subscribers, because they would not have made the losing change. They merely would have received it from the Publisher, due to a change from some other Subscriber.

If bOnlyLogIfUpdater is FALSE, the losing version is logged regardless of whether the replica made the losing update.

The input parameter ConflictType is the message to log. The optional input parameter pszConflictMessage can be used as additional text to the conflict message being logged. This allows the conflict message to be customized with additional information.

The optional input parameter bLogConflictOnUpload does not need to be used. It is a default parameter that is not used by a custom resolver.

IDL Semantics

   HRESULT _stdcall LogConflict(
      [in] REPOLE_BOOL bLogSourceConflict, 
      [in] REPOLE_CONFLICT_TYPE ConflictType, 
      [in] REPOLE_BOOL bOnlyLogIfUpdater, 
      [in, optional, defaultvalue("")] BSTR pszConflictMessage, 
      [in, optional, defaultvalue(0)] REPOLE_BOOL bLogConflictOnUpload)

LogError(
   ChangeType As REPOLE_CHANGE_TYPE, 
   [pszErrorMessage As String])

LogError( logs an error, such as a duplicate primary key error. It logs the input parameter ChangeType and the text in the optional input parameter pszErrorMessage string as the description.

IDL Semantics

   HRESULT LogError ([in] REPOLE_CHANGE_TYPE ChangeType, 
         [in, optional, defaultvalue("")] BSTR pszErrorMessage)

UpdateRow()

UpdateRow() updates the row at the destination.

IDL Semantics

   HRESULT UpdateRow ()

IConnectionInfo Interface and Methods

The methods available in IConnectionInfo are called from the merge replication custom conflict resolver application to return information about the database and tables. The IConnectionInfo interface is also used when a resolver needs to access a stored procedure. A limitation of the IConnectionInfo interface is that only the GET methods are accessible to user-implemented resolvers. Although the Object Browser shows many SET methods, only the GET methods are documented.

Many of the methods use a ColumnID parameter. The ColumnID contains a number that represents the position of the column in the table, ranging from 1 to <number of columns>. This number is used as a parameter to methods instead of the column name.

If the table is vertically partitioned, a column position in the table at the Subscriber does not necessarily match what the column position value would be in the underlying table at the Publisher. For example, if a table contains five columns, but column 3 is not published, ColumnID=4 corresponds to the last column of the table.

Qualification of Method Calls

The methods in IConnectionInfo return information about the databases and tables involved in the merge process. The methods can return information about the source or the destination. To specify which one you want information about, qualify the procedure call with a variable. For example, this code returns information about the destination:

Call DestConnectionInfo.GetServerName(stPublisher, Len(stPublisher))

This code returns information about the source:

Call SrcConnectionInfo.GetServerName(stPublisher, Len(stPublisher))

Although the same method is called (GetServerName), the distinction regarding what it returns is specified in the qualifier.

To initialize the qualifiers, use the GetDestinationConnectionInfo in the IReplRowChange interface to initialize the DestConnectionInfo variable. Use the GetSourceConnectionInfo method in the IReplRowChange interface to initialize the SrcConnectionInfo variable.

Summary of Methods in IConnectionInfo

MethodDescription
GetApplicationNameReturns the name of the application using the connection.
GetBcpBatchSizeReturns a value that indicates the setting of the batch size of commit.
GetCatalogReturns the name of the catalog.
GetCodePageReturns the current CodePage setting for the server.
GetComparisonStyleReturns a value that indicates the SQL collation.
GetConnectNameReturns the connection name.
GetDatabaseReturns the database name.
GetDatabaseStatusReturns the database status.
GetDatasourceReturns the data source name.
GetDatasourcePathReturns the path to the .mdb file.
GetDataSourceTypeReturns the type of data source.
GetDBCreatedThisSessionReturns a value that indicates whether or not the database has been created in this session.
GetHostNameReturns the host name being used in the IConnectionInfo object.
GetInternetAddressReturns the Internet address.
GetInternetNetworkReturns the NetLibrary to use when connecting.
GetLCIDReturns the preferred locale ID value.
GetLoginReturns into Login the login currently in use if using SQL Server Authentication. If using Windows Authentication, the value returned to the Login parameter is an empty string.
GetLoginTimeoutReturns the number of seconds the system will wait before returning from a failed login attempt.
GetMajorVersionReturns the product major version number.
GetMinorVersionReturns the product minor version number.
GetPacketSizeReturns the packet size, in bytes.
GetPasswordReturns the current IConnectionInfo object password.
GetProviderNameReturns the OLE DB provider name.
GetProviderStringReturns the OLE DB property provider string.
GetQueryTimeoutReturns the number of seconds of the time-out value for queries against a server.
GetSecurityModeReturns the security mode being employed to connect to SQL Server by the Windows user or group of users.
GetServerNameReturns the server name.
GetServerRoleReturns a string that indicates whether the server role is as a Subscriber, Distributor, or Publisher.
GetUseInprocLoaderReturns a value indicating whether or not the agent is using inproc loader (BULK INSERT statement) to load the data from bcp files into tables.

Methods in the IConnectionInfo Interface

GetApplicationName(
   ApplicationName As String, 
   cbApplicationName As Long)

GetApplicationName( returns the name of the application that uses this connection into ApplicationName (for example, "Merge Agent"). The input parameter cbApplicationName indicates the buffer size. HRESULT returns an error if the buffer is too small.

IDL Semantics

   HRESULT _stdcall GetApplicationName(
                        [out] LPWSTR ApplicationName, 
                        long cbApplicationName)

GetBcpBatchSize(
   plBcpBatchSize As Long)

GetApplicationName( returns into plBcpBatchSize a long data type that indicates the setting of the batch size of commit.

IDL Semantics

   HRESULT _stdcall GetBcpBatchSize(
                        [out] long* plBcpBatchSize)

GetCatalog(
   Catalog As String, 
   cbCatalog As Long)

GetCatalog( returns into Catalog the name of the catalog. The input parameter cbCatalog indicates the buffer size. HRESULT returns an error if the buffer is too small.

IDL Semantics

   HRESULT _stdcall GetCatalog(
                        [out] LPWSTR Catalog, 
                        long cbCatalog)

GetCodePage(
   plCodePage As Long)

GetCodePage( returns into plCodePage the current CodePage setting for the server. For more information on CodePage, see "Collations" in SQL Server Books Online (included with the SQL Server 2000 software package).

IDL Semantics

   HRESULT _stdcall GetCodePage(
                        [out] long* plCodePage)

GetComparisonStyle(
   plComparisonStyle As Long)

GetComparisonStyle( returns into plComparisonStyle an integer that indicates the SQL collation. For more information on collation, see "SQL Server Collation Fundamentals" in SQL Server Books Online (included with the SQL Server 2000 software package).

IDL Semantics

   HRESULT _stdcall GetComparisonStyle(
                        [out] long* plComparisonStyle)

GetConnectName(
   ConnectName As String, 
   cbConnectName As Long)

GetConnectName( returns the connection name into ConnectName. The input parameter cbConnectName indicates the buffer size. HRESULT returns an error if the buffer is too small.

IDL Semantics

   HRESULT _stdcall GetConnectName(
                        [out] LPWSTR ConnectName, 
                        long cbConnectName)

GetDatabase(
   Database As String, 
   cbDatabase As Long)

GetDatabase( returns into Database the name of the database. The input parameter cbDatabase indicates the buffer size. HRESULT returns an error if the buffer is too small.

IDL Semantics

   HRESULT _stdcall GetDatabase(
                        [out] LPWSTR Database, 
                        long cbDatabase)

GetDatabaseStatus(
   plDatabaseStatus As Long)

GetDatabaseStatus( returns a value into plDatabaseStatus that is one of the enumerated constants from REPOLE_DBAddoption.

IDL Semantics

   HRESULT _stdcall GetDatabaseStatus(
                        [out] long* plDatabaseStatus)

GetDatasource(
   Datasource As String, 
   cbDatasource As Long)

GetDatasource( returns into Datasource the name of the data source. The input parameter, cbDatasource, indicates the buffer size. HRESULT returns an error if the buffer is too small.

IDL Semantics

   HRESULT _stdcall GetDatasource(
                        [out] LPWSTR Datasource, 
                        long cbDatasource)

GetDatasourcePath(
   DatasourcePath As String, 
   cbDatasourcePath As Long)

If the data source is a Jet database, this method returns the path to the .mdb file into DatasourcePath. This is the same data stored in the Datasource_path column, which you can see by running the stored procedure sysmergesubscriptions. The input parameter, cbDatasourcePath, indicates the buffer size. HRESULT returns an error if the buffer is too small.

IDL Semantics

   HRESULT _stdcall GetDatasourcePath(
                        [out] LPWSTR DatasourcePath, 
                        long cbDatasourcePath)

GetDataSourceType(
   pRetValue As REPOLE_DATASOURCE_TYPE)

GetDataSourceType( returns the type of data source into pRetValue. The value returned is one of the enumerated constants from REPOLE_DATASOURCE_TYPE.

IDL Semantics

   HRESULT _stdcall GetDataSourceType(
                        [out] REPOLE_DATASOURCE_TYPE* pRetValue)

GetDBCreatedThisSession(
   pbDBCreatedThisSession As Long)

GetDBCreatedThisSession( returns into pbDBCreatedThisSession a value that indicates whether the database has been created in this session. A value of 0 indicates FALSE, or not created this session, while a value of 1 indicates TRUE, the database was created this session.

IDL Semantics

   HRESULT _stdcall GetDBCreatedThisSession(
                        [out] long* pbDBCreatedThisSession)

GetHostName(
   HostName As String, 
   cbHostName As Long)

GetHostName( returns the host name being used in the IConnectionInfo object into HostName. The input parameter cbHostName indicates the buffer size. HRESULT returns an error if the buffer is too small.

IDL Semantics

   HRESULT _stdcall GetHostName(
                        [out] LPWSTR HostName, 
                        long cbHostName)

GetInternetAddress(
   InternetAddress As String, 
   cbInternetAddress As Long)

GetInternetAddress( returns the Internet address as a string into InternetAddress. The input parameter cbInternetAddress indicates the buffer size. HRESULT returns an error if the buffer is too small.

IDL Semantics

   HRESULT _stdcall GetInternetAddress(
                        [out] LPWSTR InternetAddress, 
                        long cbInternetAddress)

GetInternetNetwork(
   InternetNetwork As String, 
   cbInternetNetwork As Long)

GetInternetNetwork( returns into InternetNetwork the NetLibrary to use when connecting. The NetLibrary string does not contain the .dll extension. This option is useful when configuring the Merge Agent to connect over the Internet. The input parameter cbInternetNetwork indicates the buffer size. HRESULT returns an error if the buffer is too small.

IDL Semantics

   HRESULT _stdcall GetInternetNetwork(
                        [out] LPWSTR InternetNetwork, 
                        long cbInternetNetwork)

GetLCID(
   plLCID As Long)

GetLCID( returns a value into plLCID that indicates the preferred locale ID.

IDL Semantics

   HRESULT _stdcall GetLCID(
                        [out] long* plLCID)

GetLogin(
   Login As String, 
   cbLogin As Long)

GetLogin( returns into Login the login currently in use if you are using SQL Server Authentication. If you are using Windows Authentication, the value returned to the Login parameter is an empty string. The input parameter cbLogin indicates the buffer size. HRESULT returns an error if the buffer is too small.

IDL Semantics

   HRESULT _stdcall GetLogin(
                        [out] LPWSTR Login, 
                        long cbLogin)

GetLoginTimeout(
   plLoginTimeout As Long)

GetLoginTimeout( returns the number of seconds the system will wait before returning from a failed login attempt.

IDL Semantics

   HRESULT _stdcall GetLoginTimeout(
                        [out] long* plLoginTimeout)

GetMajorVersion(
   plMajorVersion As Long)

GetMajorVersion( returns the major version number of the product into plMajorVersion. The value returned is one of the enumerated constants from REPOLE_SERVER_VERSION.

IDL Semantics

   HRESULT _stdcall GetMajorVersion(
                        long* plMajorVersion)

GetMinorVersion(
   plMinorVersion As Long)

GetMinorVersion( returns the minor version number of the product into plMinorVersion. The value returned is one of the enumerated constants from REPOLE_SERVER_MINOR_VERSION.

IDL Semantics

    HRESULT _stdcall GetMinorVersion(
                        long* plMinorVersion)

GetPacketSize(
   pusPacketSize As Long)

GetPacketSize( returns the packet size, in bytes, into pusPacketSize. The default is 4096 bytes. For more information, see "network packet size Option" in SQL Server Books Online (included with the SQL Server 2000 software package).

IDL Semantics

   HRESULT _stdcall GetPacketSize(
                        [out] unsigned long* pusPacketSize)

GetPassword(
   Password As String, 
   cbPassword As Long)

GetPassword( returns the password currently being used in the IConnectionInfo object into Password. The input parameter cbPassword indicates the buffer size. HRESULT returns an error if the buffer is too small.

IDL Semantics

   HRESULT _stdcall GetPassword(
                        [out] LPWSTR Password, 
                        long cbPassword)

GetProviderName(
   ProviderName As String, 
   cbProviderName As Long)

GetProviderName( returns the OLE DB provider name into ProviderName. The input parameter cbProviderName indicates the buffer size. HRESULT returns an error if the buffer is too small.

IDL Semantics

   HRESULT _stdcall GetProviderName(
                        [out] LPWSTR ProviderName, 
                        long cbProviderName)

GetProviderString(
   ProviderString As String, 
   cbProviderString As Long)

GetProviderString( returns the OLE DB provider string into ProviderString. The ProviderString property specifies the OLE DB provider-specific connection data required to implement a connection to the referenced OLE DB data source. The input parameter cbProviderString indicates the buffer size. HRESULT returns an error if the buffer is too small.

IDL Semantics

   HRESULT _stdcall GetProviderString(
                        [out] LPWSTR ProviderString, 
                        long cbProviderString)

GetQueryTimeout(
   plQueryTimeout As Long)

GetQueryTimeout( returns the number of seconds of the time-out value for queries against a server into plQUeryTimeout.

IDL Semantics

   HRESULT _stdcall GetQueryTimeout(
                        [out] long* plQueryTimeout)

GetSecurityMode(
   pRetValue As REPOLE_SECURITY_TYPE)

GetSecurityMode( returns into pRetValue the security mode being employed to connect to SQL Server by the Windows user or group of users. The value returned is one of the enumerated constants from REPOLE_SECURITY_TYPE.

IDL Semantics

   HRESULT _stdcall GetSecurityMode(
                        [out] REPOLE_SECURITY_TYPE* pRetValue)

GetServerName(
   ServerName As String, 
   cbServerName As Long)

GetServerName( returns into ServerName the name of the server. The input parameter, cbServerName, indicates the buffer size. HRESULT returns an error if the buffer is too small.

IDL Semantics

   HRESULT _stdcall GetServerName(
                        [out] LPWSTR ServerName, 
                        long cbServerName)

GetServerRole(
   ServerRole As String, 
   cbServerRole As Long)

GetServerRole( returns a string into ServerRole that indicates which server role is being used, Subscriber, Distributor, or Publisher. The input parameter cbServerRole indicates the buffer size. HRESULT returns an error if the buffer is too small.

IDL Semantics

   HRESULT _stdcall GetServerRole(
                        [out] LPWSTR ServerRole, 
                        long cbServerRole)

GetUseInprocLoader(
   pbUseInprocLoader As Long)

GetUseInprocLoader( returns a value into pbUseInprocLoader indicating whether or not the agent is using inproc loader (BULK INSERT statement) to load the data from bcp files into tables. It returns FALSE if the agent is using ODBC BCP to load data from bcp files into the tables.

IDL Semantics

   HRESULT _stdcall GetUseInprocLoader(
                        [out] long* pbUseInprocLoader)

Constants

This section provides details on the enumerated data types that are defined in the SQL Replication Conflict Resolver Library. These data types are also used as parameters and return values to the methods in the SQL Merge Conflict Resolver component.

REPLGUID

The REPLGUID constants provide a class identifier that is a properly formatted GUID.

ConstantDescription
Data1Long
Data2Short
Data3Short
Data4Unsigned char(8)

REPOLE_BOOL

The REPOLE_BOOL constants specify values that indicate true or false.

ConstantValueDescription
REPOLEBool_FALSE0Value is false.
REPOLEBool_TRUE1Value is true.

REPOLE_CHANGE_TYPE

The REPOLE_CHANGE_TYPE constants specify the type of database operation (update, insert, delete), whether or not there is a conflict, and whether or not column tracking is active.

ConstantValueDescription
REPOLEChange_SubscriberInsert0x00000001Subscriber is inserting new row in upload phase.
REPOLEChange_PublisherInsert0x00010000Publisher is inserting new row in download phase.
REPOLEChange_SubscriberDelete
_NoConflict
0x00000002Subscriber is deleting a row. No conflict detected.
REPOLEChange_PublisherDelete_
NoConflict
0x00020000Publisher is deleting a row. No conflict detected.
REPOLEChange_SubscriberSyste
mDelete
0x00000004Subscriber is deleting the row due to reasons like primary key violations.
REPOLEChange_PublisherSystem
Delete
0x00040000Publisher is deleting the row due to reasons like primary key violations.
REPOLEChange_SubscriberDelete
_Conflict
0x00000008Subscriber is deleting a row. Conflict detected.
REPOLEChange_PublisherDelete_
Conflict
0x00080000Publisher is deleting a row. Conflict detected.
REPOLEChange_SubscriberRemov
eFromPartial
0x00000010Not used.
REPOLEChange_PublisherRemove
FromPartial
0x00100000The publisher tells the subscriber to delete the row, because it is no longer in the partition.
REPOLEChange_SubscriberUpdate
_NoConflict
0x00000020Subscriber is updating a row. No conflict detected.
REPOLEChange_PublisherUpdate_
NoConflict
0x00200000Publisher is updating a row. No conflict detected.
REPOLEChange_SubscriberUpdate
_ConflictWithDelete
0x00000040Subscriber is updating a row and has a conflict with a delete coming from the Publisher.
REPOLEChange_PublisherUpdate_
ConflictWithDelete
0x00400000Publisher is updating a row and has a conflict with a delete coming from the Subscriber.
REPOLEChange_SubscriberUpdate
_ConflictColTrack
0x00000080Subscriber is updating a specific column, column tracking is in effect, and the update has a conflict with a change coming from the Publisher.
REPOLEChange_PublisherUpdate_
ConflictColTrack
0x00800000Publisher is updating a specific column, column tracking is in effect, and the update has a conflict with a change coming from the Subscriber.
REPOLEChange_SubscriberUpdate
_ConflictNoColTrack
0x00000100Subscriber is updating a specific column, column tracking is not in effect, and the update has a conflict with a change coming from the Publisher.
REPOLEChange_PublisherUpdate_
ConflictNoColTrack
0x01000000Publisher is updating a specific column, column tracking is not in effect, and the update has a conflict with a change coming from the Subscriber.
REPOLEChange_UploadInsertFaile
d
0x00000200A row was inserted at the Subscriber, but the subsequent insertion at the Publisher failed.
REPOLEChange_DownloadInsertF
ailed
0x02000000A row was inserted at the Publisher, but the subsequent insertion at the Subscriber failed.
REPOLEChange_UploadDeleteFail
ed
0x00000400A row was deleted at the Subscriber, but the subsequent deletion at the Publisher failed.
REPOLEChange_DownloadDelete
Failed
0x04000000A row was deleted at the Publisher, but the subsequent deletion at the Subscriber failed.
REPOLEChange_UploadUpdate
Failed
0x00000800A row was updated at the Subscriber, but the subsequent update at the Publisher failed.
REPOLEChange_DownloadUpdate
Failed
0x08000000A row was updated at the Publisher, but the subsequent update at the Subscriber failed.
REPOLEUpdateConflicts The combination of:
(REPOLEChange_SubscriberUpdate_
ConflictColTrack |
REPOLEChange_PublisherUpdate_
ConflictColTrack |
REPOLEChange_SubscriberUpdate_
ConflictNoColTrack |
REPOLEChange_PublisherUpdate_
ConflictNoColTrack)
REPOLEAllConflicts  The combination of:
(REPOLEChange_SubscriberDelete_Conflict |
REPOLEChange_PublisherDelete_Conflict |
REPOLEChange_SubscriberUpdate_
ConflictWithDelete |
REPOLEChange_PublisherUpdate_
ConflictWithDelete |
REPOLEUpdateConflicts)
REPOLEAllErrors  The combination of:
(REPOLEChange_UploadInsertFailed |
REPOLEChange_DownloadInsertFailed |
REPOLEChange_UploadDeleteFailed |
REPOLEChange_DownloadDeleteFailed |
REPOLEChange_UploadUpdateFailed |
REPOLEChange_DownloadUpdateFailed)
REPOLEAllNonConflicts  The combination of:
(REPOLEChange_SubscriberInsert |
REPOLEChange_PublisherInsert |
REPOLEChange_SubscriberDelete_
NoConflict |
REPOLEChange_PublisherDelete_
NoConflict|
REPOLEChange_SubscriberSystemDelete |
REPOLEChange_PublisherSystemDelete |
REPOLEChange_SubscriberRemoveFrom
Partial |
REPOLEChange_SubscriberUpdate_
NoConflict |
REPOLEChange_PublisherUpdate_
NoConflict)
REPOLEAllChanges  The combination of:
(REPOLEAllConflicts | REPOLEAllErrors |
REPOLEAllNonConflicts)

REPOLE_COLSTATUS_TYPE

The REPOLE_COLSTATUS_TYPE constants specify the status of an individual column.

ConstantValueDescription
REPOLEColumn_NotUpdated0x0001No updates to column, or data values match.
REPOLEColumn_UpdatedNoConflict0x0002Column updated at source, no column-level conflict.
REPOLEColumn_UpdatedWithConflict0x0003Column updated at source, conflicts with change at destination.
REPOLEColumn_DifferNoTrack0x0004No column tracking information; data values are different.

REPOLE_COLUMN_ATTRIBUTES

The REPOLE_COLUMN_ATTRIBUTES constants specify whether the column is updatable, an identity column, or both.

ConstantValueDescription
REPOLEColumnAttribute_Updatable0x0001This column is updatable.
REPOLEColumnAttribute_Identity0x0002This column is an identity column.

Remarks

The values returned from methods that use this constant return a bitmap of the values, and because a column can be both updatable and an identity column, check the results with *plColumnAttributes & REPOLEColumnAttribute_Updatable, or *plColumnAttributes & REPOLEColumnAttribute_Identity, respectively.

REPOLE_CONFLICT_TYPE

The REPOLE_CONFLICT_TYPE constants specify a value that describes the type of conflict that occurred and if the failure occurred during the upload or download phase.

ConstantValueDescription
REPOLEConflict_Min1For internal use only.
REPOLEConflict_UpdateConflict1 The two replicas made conflicting updates to the same row.
REPOLEConflict_ColumnUpdateConflict2The two replicas made conflicting updates to the same column of the same row.
REPOLEConflict_UpdateDeleteWinsConflict3An update conflicted with a delete, and the delete won.
REPOLEConflict_UpdateWinsDelete
Conflict
4An update conflicted with a delete, and the update won.
REPOLEConflict_UploadInsertFailed5Corresponds to the respective REPOLE_CHANGE_TYPE.
REPOLEConflict_DownloadInsertFailed6Corresponds to the respective REPOLE_CHANGE_TYPE.
REPOLEConflict_UploadDeleteFailed7Corresponds to the respective REPOLE_CHANGE_TYPE.
REPOLEConflict_DownloadDeleteFailed8Corresponds to the respective REPOLE_CHANGE_TYPE.
REPOLEConflict_UploadUpdateFailed9Corresponds to the respective REPOLE_CHANGE_TYPE.
REPOLEConflict_DownloadUpdateFailed10Corresponds to the respective REPOLE_CHANGE_TYPE.
REPOLEConflict_ResolutionDone11For internal use only.
REPOLEConflict_Max11For internal use only.

REPOLE_DATASOURCE_TYPE

Because SQL Server 2000 offers the ability to replicate data to any heterogeneous data source that provides a 32-bit ODBC or OLE DB driver on either Microsoft Windows® 2000, Microsoft Windows NT® Server 4.0, or Windows 98 operating systems, the REPOLE_DATASOURCE_TYPE constants provide data source type values for the application. For more information, see "DATASOURCE_TYPE" in SQL Server Books Online (included with the SQL Server 2000 software package).

ConstantValue Description
REPOLEDataSource_Native0x0000Microsoft SQL Server Subscriber data source.
REPOLEDataSource_Jet0x0002Microsoft Jet 4.0 database.

The object browser shows more constants for this enumeration. They are not valid, however, in a Visual Basic merge replication conflict resolver application, and, therefore, are not listed here.

REPOLE_DBADDOPTION

The REPOLE_DBADDOPTION constants specify if the Subscriber database exists, if it must be created or attached, or if the subscription must be attached.

ConstantValueDescription
REPOLEExisting_Database0x0000Uses an existing Subscriber database.
REPOLECreate_Database0x0001Creates the Subscriber database (SQL Server Subscribers only).
REPOLEAttach_Database0x0002Attaches a Subscriber database file, typically an .mdf (SQL Server Subscribers only).
REPOLEAttach_Subscription0x0003Attaches a subscription file, typically an .msf (Microsoft Subscription File).

REPOLE_EXTENDEDERROR_TYPE

The REPOLE_EXTENDEDERROR_TYPE constants return additional error information.

ConstantValueDescription
REPOLEExtErrorNoneOrNotSpecified 0x00000000All errors not specifically addressed.
REPOLEExtErrorDupKey 0x00000001SQL Server error 2627: Violation of %ls constraint '%.*ls'. Cannot insert duplicate key in object '%.*ls'.
REPOLEExtErrorDupUniqueIndex 0x00000002SQL Server error 2601: Cannot insert duplicate key row in object '%.*ls' with unique index '%.*ls'.

REPOLE_PRIORITY_TYPE

The REPOLE_PRIORITY_TYPE constants return the assigned priority. For more information, see "Subscriber Types and Conflicts" in SQL Server Books Online (included with the SQL Server 2000 software package).

ConstantValueDescription
REPOLEPriority_Source0x0001The source has the higher priority.
REPOLEPriority_Destination0x0002The destination has the higher priority.
REPOLEPriority_Equal0x0003The source and destination have equal priority.
Note   If priorities are equal, you will receive REPOLEPriority_Source during download or REPOLEPriority_Destination during upload, instead of REPOLEPriority_Equal.

REPOLE_SECURITY_TYPE

These constants specify what kind of security is used when connecting to SQL Server.

ConstantValueDescription
REPOLESecurity_Min0For internal use only.
REPOLESecurity_Normal0Specifies SQL Server Authentication mode.
REPOLESecurity_Integrated1Specifies Windows Authentication mode.
REPOLESecurity_Mixed2Specifies Windows Authentication or SQL Server Authentication. SQL Server Authentication is provided for backward compatibility.
REPOLESecurity_Max2For internal use only.
REPOLESecurity_Invalid-1Indicates that the security mode is not specified. Security mode is retrieved from the Publisher.

Remarks

The terminology used in several of these constants contains some references for backward compatibility to SQL Server version 6.5. The terms, Windows Authentication and Mixed Mode, replace integrated security and mixed security, respectively. Standard security has no equivalent. For more information, see "Authentication Modes" in SQL Server Books Online (included with the SQL Server 2000 software package).

REPOLE_SERVER_VERSION

This is an enumeration that describes which version of SQL Server is running.

ConstantValueDescription
REPOLEVersion_Invalid -1Unknown or not set.
REPOLEVersion_70RTM10SQL Server 7.0.
REPOLEVersion_70SP120SQL Server 7.0 with Service Pack 1.
REPOLEVersion_70SP230SQL Server 7.0 with Service Pack 2.
REPOLEVersion_70SP335SQL Server 7.0 with Service Pack 3.
REPOLEVersion_8040SQL Server 2000.

REPOLE_SERVER_MINOR_VERSION

This is an enumeration that indicates the minor version number that is running.

ConstantValueDescription
REPOLEMinorVersion_Invalid -1Unknown or not set.
REPOLEMinorVersion_80Beta210SQL Server 2000, Beta 2.
REPOLEMinorVersion_80EAP620SQL Server 2000, Early Adopter Program 6.
REPOLEMinorVersion_80RTM30SQL Server 2000 Retail.

REPOLE_SQL_DATATYPE

This is an enumeration of the ODBC data types. For more information on the default ODBC data types and their descriptions, see the topic, SQL Data Types, in the Microsoft Open Database Connectivity (ODBC) section of the Microsoft Data Access Components (MDAC) SDK (available on the MSDN® Library CD). Additionally, the ODBC SDK can also be downloaded from the Universal Data Access Web site, and is available in the Microsoft ODBC 3.0 Software Development Kit and Programmer's Reference from Microsoft Press®.

ConstantValue
REPOLEType_SQL_UNKNOWN_TYPE0
REPOLEType_SQL_CHAR1
REPOLEType_SQL_NUMERIC2
REPOLEType_SQL_DECIMAL3
REPOLEType_SQL_INTEGER4
REPOLEType_SQL_SMALLINT5
REPOLEType_SQL_FLOAT6
REPOLEType_SQL_REAL7
REPOLEType_SQL_DOUBLE8
REPOLEType_SQL_DATETIME9
REPOLEType_SQL_DATE9
REPOLEType_SQL_INTERVAL10
REPOLEType_SQL_TIME10
REPOLEType_SQL_TIMESTAMP11
REPOLEType_SQL_VARCHAR12
REPOLEType_SQL_LONGVARCHAR-1
REPOLEType_SQL_BINARY-2
REPOLEType_SQL_VARBINARY-3
REPOLEType_SQL_LONGVARBINARY-4
REPOLEType_SQL_BIGINT-5
REPOLEType_SQL_TINYINT-6
REPOLEType_SQL_BIT-7
REPOLEType_SQL_GUID-11

Remarks

For any of the datetime and interval data types, this field returns the verbose data type: SQL_DATETIME or SQL_INTERVAL. The developer is responsible for knowing the concise data type, as there is no REPOLE_SQL_DATATYPE enumeration that maps directly to the individual ODBC data types.

Registering a Custom Conflict Resolver

To be recognized by SQL Server, the merge replication custom conflict resolver must be registered.

How to Register a Merge Replication Custom Conflict Resolver

In Visual Basic, go to the File menu and click Make <yourproject>.DLL. This will compile and register the application on the current computer. If the .DLL needs to be registered on a different computer, run REGSVR32 <yourproject>.DLL from the command prompt. The .DLL must be registered on the computer that is invoking the merge agent. If you are using this application with a push subscription, the resolver must be registered at the Distributor that is invoking the merge agent. Similarly, for pull subscriptions, the resolver must be registered at the Subscriber that is invoking the merge agent. To ensure the resolver is registered properly, run sp_enumcustomresolvers on the computer on which it is registered. If the application name is returned in the result set, it is registered properly.

Registering Custom Resolvers on a Cluster

When using custom conflict resolvers with merge replication on a failover cluster, register the custom resolver on all nodes of the failover cluster. This ensures the custom resolver will be able to load the resolver properly after a failover. Merge replication conflict resolvers that come with SQL Server are automatically registered.

Merge Replication Custom Conflict Resolver Samples

The Merge Replication Custom Conflict Resolver (SampleResolver) sample is available on the SQL Server 2000 Resource Kit CD-ROM in the folder, \ToolsAndSamples\SampleResolver. The samples demonstrate the implementation of the SQL Replication Conflict Resolver Library in merge replication custom conflict resolver applications. The included samples are:

  • Additive Resolver
  • Minimum Resolver

The samples contain an SQL Script that will register the custom conflict resolvers.

Additive Resolver

The Additive Resolver is designed to handle several types of UPDATE conflicts. The Additive Resolver chooses the sum of the column values from the Publisher and Subscriber as the winning column value in an UPDATE conflict.

When a custom application is used to resolve conflicts on a particular column, the user is required to enter the name of the column when creating the publication using the Create Publication Wizard, or using the @resolver_info parameter of the sp_addmergearticle system stored procedure. For more information, see "Specifying a Custom Resolver" in SQL Server Books Online (included with the SQL Server 2000 software package).

The IVBCustomResolver_Reconcile Method

The IVBCustomResolver_Reconcile method is where the conflict logic is coded. It is necessary to gather all the data needed (the order in which the data is gathered is not critical) before invoking the resolve code. The resolve code is in the IVBCustomResolver_ComputeAdditiveValues method in the AdditiveResolver sample.

To gather the necessary data, the first task in the Reconcile method is to determine what kind of change occurred. This can be found by calling the method GetChangeType and passing to it a variable of type SQLResolver.REPOLE_CHANGE_TYPE. The value returned will be one of the REPOLE_CHANGE_TYPE variables.

The next task, which you will see in the sample, is to retrieve the data connection information by calling the GetSourceConnectionInfo and GetDestinationConnectionInfo methods. These two methods take, as their parameters, a variable declared as IConnectionInfo, as well as a variable defined as a Boolean (REPOLE_BOOL). The application then uses the GetRowIdentifier method to get the GUID of the row in the table. It then determines whether the merge process is being done in the upload phase or the download phase.

When a custom application is used to resolve conflicts on a particular column, the code retrieves the column name the user has entered by calling the GetResolverProcedureName method of IReplRowChange. This function gets a column name (not a procedure name). When that application has the column name, it needs to convert the name to a value that indicates the position of the column in the table. The application loops through all the columns in the table, comparing the retrieved column name to each column name in the table. When it finds a match, the application calls the IVBCustomResolver_ComputeAdditiveValues method, which determines the new column value.

The IVBCustomResolver_ComputeAdditiveValues Method

The application first determines the status of the column in question by calling GetColumnStatus. This gives the following status options:

REPOLEColumn_DifferNoTrack

REPOLEColumn_NotUpdated

REPOLEColumn_UpdatedNoConflict

REPOLEColumn_UpdatedWithConflict

The application can then decide how to proceed based on the state of the column. In the sample, if the column has been updated at both source and destination, the value is retrieved from both computers using calls to GetSourceColumnValue and GetDestinationColumnValue. The application then adds them together.

To put the new value back into the table column, the application uses the SetColumn function. The SetColumn function does not commit the value. To commit the value to the table, the UpdateRow function must be called and changes to the entire row are committed.

For columns that have been updated without changes, the application copies the column data by using CopyColumnFromSource. Again, to commit the value to the table, call the UpdateRow function.

MinimumResolver

The MiminumResolver handles several types of conflicts. When an UPDATE conflict occurs, it determines the minimum value between the source and destination column values, and sets the minimum value as the winning value. If the column values match, then the winning data is based on priorities. For more information on assigning priorities in conflicts, see "Subscriber Types and Conflicts" in SQL Server Books Online (included with the SQL Server 2000 software package).

The IVBCustomResolver_Reconcile Method

The IVBCustomResolver_Reconcile method is where the conflict logic is coded. It is necessary to gather all the data needed (the order in which the data is gathered is not critical) before invoking the resolve code. The resolve code in this sample is in the IVBCustomResolver_ComputeMinimumValue method.

To gather the necessary data, the first task in the Reconcile method is to determine what kind of change occurred. The type of change can be found by calling the method GetChangeType and passing to it a variable of type SQLResolver.REPOLE_CHANGE_TYPE. The value returned will be one of the REPOLE_CHANGE_TYPE variables.

The next task, which you will see in the sample, is to retrieve the data connection information. This is done by calling the GetSourceConnectionInfo and GetDestinationConnectionInfo methods. These two methods take as their parameters a variable declared as IconnectionInfo and a variable defined as a Boolean (REPOLE_BOOL). The application then uses the GetRowIdentifier method to get the GUID of the row in the table. It then determines whether the merge process is being done in the upload phase or the download phase.

Because this application is designed to resolve conflicts in a particular column, the code retrieves the column name the user has entered by calling the GetResolverProcedureName method of IReplRowChange. This function gets a procedure name (not a column name). Now that the application has the column name, it must convert the name to a value that indicates the position of the column in the table. The application loops through all the columns in the table, comparing the retrieved column name to each column name in the table. When it finds a match, the application calls the IVBCustomResolver_ComputeMinimumValue method, which determines the new column value.

The IVBCustomResolver_ComputeMinimumValue Method

The application first retrieves the values of the column from the source and the destination. Then IF/ELSE logic is used to determine which value is less than the other, or if they are equal. If they are equal, the application calls GetPriorityWinner to determine the winner.

To put the new value back into the table column, the application uses the SetColumn function. The SetColumn function does not commit the value. To commit the value to the table, the UpdateRow function must be called and changes to the entire row are committed.

The application copies the column data by using CopyColumnFromSource, and uses the UpdateRow method to commit the value to the table.

Conclusion

This article covered creating a VB application that uses the Microsoft SQL Replication Conflict Resolver Library. It documented the methods in the replrec.dll, described which events are called, when the events are called, and how to code the events to cause the correct resolution of data to occur in the affected tables.

Appendix: How to Run the Samples

  1. In SQL Query Analyzer, open \ToolsAndSamples\SampleResolver\vbresolver_1.sql. This script registers both sample resolvers, and creates a publication and subscription. The script:
    • Registers the Sample Additive Resolver using the CLSID of SampleCustomResolver.AdditiveResolver component.
    • Registers the Sample Minimum Values Resolver using the CLSID of SampleCustomResolver.MinumumResolver component.
    • Creates the SampleNorthwind publication.
    • Creates the Orders article and sets the Sample Minimum Values Resolver as the resolver, with the resolver column OrderDate as the resolver_info property.
    • Creates the Products article and sets the Sample Additive Resolver as the resolver, with the resolver column UnitsOnOrder as the resolver_info property.
    • Creates subscription database SampleNorthwindSubscription, and sets up an anonymous subscription to SampleNorthwind that can be synchronized using the Windows Synchronization Manager.
    • Runs the snapshot agent for the publication.
  2. When the script in vbresolver_1.sql is finished, merge the data using Windows Synchronization Manager. From the Windows Start menu, click Start, select Programs, select Accessories, and then click Synchronize.
  3. After the Subscription has been synchronized using the Merge Agent, use SQL Query Analyzer to open the file \ToolsAndSamples\SampleResolver\vbresolver_2.sql. When the script in vbresolver_2.sql is run, it will create an update conflict, which will then demonstrate the custom resolvers.

The information contained in this article represents the current view of Microsoft Corporation on the issues discussed as of the date of publication. Because Microsoft must respond to changing market conditions, it should not be interpreted to be a commitment on the part of Microsoft, and Microsoft cannot guarantee the accuracy of any information presented after the date of publication.

This article is for informational purposes only. MICROSOFT MAKES NO WARRANTIES, EXPRESS OR IMPLIED, AS TO THE INFORMATION IN THIS ARTICLE.

Complying with all applicable copyright laws is the responsibility of the user. Without limiting the rights under copyright, no part of this article may be reproduced, stored in or introduced into a retrieval system, or transmitted in any form or by any means (electronic, mechanical, photocopying, recording, or otherwise), or for any purpose, without the express written permission of Microsoft Corporation.

Microsoft may have patents, patent applications, trademarks, copyrights, or other intellectual property rights covering subject matter in this article. Except as expressly provided in any written license agreement from Microsoft, the furnishing of this article does not give you any license to these patents, trademarks, copyrights, or other intellectual property.

The example companies, organizations, products, people and events depicted herein are fictitious. No association with any real company, organization, product, person or event is intended or should be inferred.

© 2001 Microsoft Corporation. All rights reserved.

Microsoft, ActiveX, Microsoft Press, MSDN, Visual Basic, Windows, and Windows NT are either registered trademarks or trademarks of Microsoft Corporation in the United States and/or other countries.

The names of actual companies and products mentioned herein may be the trademarks of their respective owners.

Was this page helpful?
(1500 characters remaining)
Thank you for your feedback
Show:
© 2014 Microsoft