Hello,
I've the following Problem with my Own SQL Replication Plugin.
Its like this Problem shown here: http://social.msdn.microsoft.com/Forums/en-US/sqlreplication/thread/03f5207b-7330-4ce5-9525-faf427d86080/
The Shown Error Messages in the Replication Agent:
Attempted to read or write protected memory. This is often an indication that other memory is corrupt. (Source: MSSQL_REPL, Error number: MSSQL_REPL-2147199411)
"The Merge Agent encountered an error when executing code in the 'InsertErrorHandler' method implemented in the business logic handler 'C:\Program Files (x86)\replication\CustomSqlReplication.dll'. Ensure that the overridden 'InsertErrorHandler' method has
been properly implemented in the business logic handler. (Source: MSSQL_REPL, Error number: MSSQL_REPL-2147199411)
Get help: http://help/MSSQL_REPL-2147199411"
If Some extended logs help, then just tell me where to find them and I can post them too.
The Way I'm creating the Plugin and registering I've written an german Blog post about it here:
http://squadwuschel.wordpress.com/2012/09/18/eigenes-c-plugin-businesslogicmodule-fr-sql-server-replikation-erstellen/
perhaps this helps.
I am Running a Merge Replication with Push Clients and the Replication Mode is "continuous" on an SQL Server 2008R2
In the following you see my C# Code for the .NET 2.0 Dll
I am Deleting doubled DB Entries from a table with a composite primary Key, to Prevent Insert Problems from the SQL Server from the Replication. Because there is no Insert possible if the same composite primary Key is allready in use.
public class SqlReplicationModule : BusinessLogicModule { #region Properties public override ChangeStates HandledChangeStates { get { return ChangeStates.SubscriberInsertErrors | ChangeStates.PublisherInsertErrors; } } public SqlReplicationConnection SqlReplicationConnection { get; set; } #endregion #region Public functions public override void Initialize(string publisher, string subscriber, string distributor, string publisherDb, string subscriberDb, string articleName) { SqlReplicationConnection = new SqlReplicationConnection(publisher, subscriber, distributor, publisherDb, subscriberDb, articleName); } #endregion #region Override Events public override ActionOnDataError InsertErrorHandler(SourceIdentifier insertSource, DataSet insertedDataSet, ref ErrorLogType errorLogType, ref string customErrorMessage, int errorCode, string errorMessage, ref int historyLogLevel, ref string historyLogMessage) { try { if (SqlReplicationConnection.ArticleName.ToLower() == "table1") { Debugging.WriteLog("ReplicationTable: " + SqlReplicationConnection.ArticleName); ReplicationModuelPrinterStatusSetpoints replication = new ReplicationModuelPrinterStatusSetpoints(SqlReplicationConnection, insertedDataSet); if (replication.ErrorCorrection()) { return ActionOnDataError.AcceptCustomErrorBehavior; } } } catch (Exception exception) { Debugging.WriteLog("InsertErrorHandler", exception); } return base.InsertErrorHandler(insertSource, insertedDataSet, ref errorLogType, ref customErrorMessage, errorCode, errorMessage, ref historyLogLevel, ref historyLogMessage); } #endregion } public class ReplicationModuelPrinterStatusSetpoints { #region Properties private const string TableName = "table1"; public SqlReplicationConnection Sql { get; set; } public DataSet InsertedDataSet { get; set; } #endregion #region Konstruktor public ReplicationModuelPrinterStatusSetpoints(SqlReplicationConnection sqlReplicationConnection, DataSet insertedDataSet) { Sql = sqlReplicationConnection; InsertedDataSet = insertedDataSet; } #endregion #region Public Functions /// <summary> public bool ErrorCorrection() { Debugging.WriteLog("ErrorCorrection - ReplicationModuelPrinterStatusSetpoints"); try { if (InsertedDataSet.Tables.Count > 0 && InsertedDataSet.Tables[0].Rows.Count > 0) { string statusId = InsertedDataSet.Tables[0].Rows[0]["status_id"].ToString(); string id = InsertedDataSet.Tables[0].Rows[0]["id"].ToString(); string sqlString = string.Format("Select * from {0} where status_id = {1} and id = {2}", TableName, statusId, id); DataTable tablePublisher = Sql.GetDataTable(sqlString, SqlReplicationConnection.RepType.Publisher); if (tablePublisher != null && tablePublisher.Rows.Count > 0) { DataTable tableSubScriber = Sql.GetDataTable(sqlString, SqlReplicationConnection.RepType.Subscriber); if (tableSubScriber != null && tableSubScriber.Rows.Count > 0) { return RemoveDuplicateDataRow(tableSubScriber, tablePublisher, id, printerId); } } } } catch (Exception exception) { Debugging.WriteLog("ErrorCorrection:", exception); } return false; } #endregion #region Private functions private bool RemoveDuplicateDataRow(DataTable subscriber, DataTable publisher, string statusId, string id) { Debugging.WriteLog("RemoveDuplicateDataRow - ReplicationModuelPrinterStatusSetpoints"); try { DateTime datePublisher = DateTime.Now; DateTime.TryParse(publisher.Rows[0]["created"].ToString(), out datePublisher); DateTime dateSubscriber = DateTime.Now; DateTime.TryParse(subscriber.Rows[0]["created"].ToString(), out dateSubscriber); string sqlDelete = string.Format("Delete from {0} where status_id = {1} and id = {2}", TableName, statusId, id); if (datePublisher > dateSubscriber) { return Sql.ExecuteSql(sqlDelete, SqlReplicationConnection.RepType.Subscriber); } else { return Sql.ExecuteSql(sqlDelete, SqlReplicationConnection.RepType.Publisher); } } catch (Exception exception) { Debugging.WriteLog("RemoveDuplicateDataRow:", exception); } return false; } #endregion } public class SqlReplicationConnection { #region Properties public enum RepType { Subscriber, Publisher } public string Publisher { get; set; } public string Subscriber { get; set; } public string Distributor { get; set; } public string PublisherDb { get; set; } public string SubscriberDb { get; set; } public string ArticleName { get; set; } #endregion #region Konstruktor public SqlReplicationConnection(string publisher, string subscriber, string distributor, string publisherDb, string subscriberDb, string articleName) { Publisher = publisher; Subscriber = subscriber; Distributor = distributor; PublisherDb = publisherDb; SubscriberDb = subscriberDb; ArticleName = articleName; } #endregion #region Public Functions public string PublisherConnectionString() { return string.Format("Data Source='{0}';Initial Catalog='{1}';Integrated Security=SSPI", Publisher, PublisherDb); } public string SubScriberConnectionString() { return string.Format("Data Source='{0}';Initial Catalog='{1}';Integrated Security=SSPI", Subscriber, SubscriberDb); } public DataTable GetDataTable(string sqlString, RepType repType) { DataTable dataTable = new DataTable(); using (SqlConnection sqlCon = new SqlConnection(GetConnectionstring(repType))) { try { SqlCommand sqlCommand = new SqlCommand(sqlString); SqlDataAdapter dataAdapter = new SqlDataAdapter(sqlCommand); sqlCommand.Connection = sqlCon; sqlCon.Open(); dataAdapter.Fill(dataTable); Debugging.WriteLog("SQL-GetDataTable: " + sqlString); } catch (Exception exception) { dataTable = null; Debugging.WriteLog("GetDataTable - Error: ", exception); } finally { sqlCon.Close(); } } return dataTable; } public string GetString(string sqlString, RepType repType) { string str = string.Empty; using (SqlConnection sqlCon = new SqlConnection(GetConnectionstring(repType))) { try { SqlCommand sqlCommand = new SqlCommand(sqlString); sqlCommand.Connection = sqlCon; sqlCon.Open(); str = Convert.ToString(sqlCommand.ExecuteScalar()); Debugging.WriteLog("SQL-GetString: " + sqlString); } catch (Exception exception) { str = string.Empty; Debugging.WriteLog("GetString - Error: ", exception); } finally { sqlCon.Close(); } } return str; } public Boolean ExecuteSql(string sqlString, RepType repType) { SqlConnection sqlConnection = null; SqlCommand sqlCommand = null; try { sqlConnection = new SqlConnection(GetConnectionstring(repType)); sqlCommand = new SqlCommand(sqlString, sqlConnection); sqlConnection.Open(); sqlCommand.ExecuteNonQuery(); Debugging.WriteLog("SQL-ExecuteSql: " + sqlString); return true; } catch (Exception exception) { Debugging.WriteLog("SQL-ExecuteSql: ", exception); return false; } finally { if (sqlConnection != null) { sqlConnection.Close(); } } } #endregion #region Private Functions private string GetConnectionstring(RepType repType) { string connectionString = PublisherConnectionString(); if (repType == RepType.Subscriber) { connectionString = SubScriberConnectionString(); } return connectionString; } #endregion }
The SQL Code to Register the Dll
In the DLL Direcotory is also the Microsoft.SqlServer.Replication.BusinessLogicSupport.dll
DECLARE @publication AS sysname; DECLARE @article AS sysname; DECLARE @friendlyname AS sysname; DECLARE @assembly AS nvarchar(500); DECLARE @class AS sysname; SET @publication = N'ReplicationName'; SET @article = N'table1'; SET @friendlyname = N'ReplicationModule'; SET @assembly = N'C:\ProgramData\replication\CustomSqlReplication.dll'; SET @class = N'CustomSqlReplication.SqlReplicationModule'; 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
The Logging from the C# Code shows me that Rows get Deleted, and so it "works". The Problem also only appears at the moment on our Testing system and not on the live system.
thx much Squadwuschel