Extending the replication synchronization process
RasterSyncExtension\RasterSyncWorkspaceExtension.cs
// Copyright 2012 ESRI
// 
// All rights reserved under the copyright laws of the United States
// and applicable international laws, treaties, and conventions.
// 
// You may freely redistribute and use this sample code, with or
// without modification, provided you include the original copyright
// notice and use restrictions.
// 
// See the use restrictions.
// 

using System;
using System.Runtime.InteropServices;
using ESRI.ArcGIS.esriSystem;
using ESRI.ArcGIS.Geodatabase;
using ESRI.ArcGIS.GeoDatabaseDistributed;

namespace RasterSyncExtension
{
  /// <summary>
  /// An example of a workspace extension that extends the synchronization process. This
  /// implementation synchronizes a specific raster catalog in addition to the out-of-the-box
  /// synchronization process.
  /// </summary>
  [Guid("97CD2883-37CB-4f76-BD0F-945279C783DC")]
  [ClassInterface(ClassInterfaceType.None)]
  [ProgId("RasterSyncExtension.RasterSyncWorkspaceExtension")]
  [ComVisible(true)]
  public class RasterSyncWorkspaceExtension : IWorkspaceExtensionControl, IWorkspaceExtension2, IWorkspaceReplicaSyncEvents
  {
    #region Private Variables
    /// <summary>
    /// Provides a weak reference to the extension's workspace.
    /// </summary>
    private IWorkspaceHelper workspaceHelper = null;

    /// <summary>
    /// The name of the raster catalog to synchronize.
    /// </summary>
    private const String rasterCatalogName = "ras_cat";

    /// <summary>
    /// The name of the replica that requires raster synchronization.
    /// </summary>
    private const String rasterReplicaName = "myreplicaras";

    /// <summary>
    /// The name of the integer field in the raster catalogs that stores generation numbers.
    /// </summary>
    private const String genFieldName = "gen";
    #endregion

    #region IWorkspaceExtensionControl Members
    /// <summary>
    /// Initializes the workspace extension.
    /// </summary>
    /// <param name="workspaceHelper">Provides a weak reference to the workspace.</param>
    public void Init(IWorkspaceHelper workspaceHelper)
    {
      this.workspaceHelper = workspaceHelper;
    }

    /// <summary>
    /// Called to shutdown the extension.
    /// </summary>
    public void Shutdown()
    {
      workspaceHelper = null;
    }
    #endregion

    #region IWorkspaceExtension2 Members
    /// <summary>
    /// The name of the extension.
    /// </summary>
    public string Name
    {
      get { return "RasterSyncWorkspaceExtension"; }
    }

    /// <summary>
    /// The extension's GUID.
    /// </summary>
    public UID GUID
    {
      get
      {
        UID uid = new UIDClass();
        uid.Value = "{97CD2883-37CB-4f76-BD0F-945279C783DC}";
        return uid;
      }
    }

    /// <summary>
    /// An enumerator of private dataset names used by the extension.
    /// Not used in this implementation.
    /// </summary>
    /// <param name="datasetType">The dataset type.</param>
    /// <returns>An enumerator of strings.</returns>
    public IEnumBSTR get_PrivateDatasetNames(esriDatasetType datasetType)
    {
      return null;
    }

    /// <summary>
    /// An enumerator of data dictionary names used by the extension.
    /// Not used in this implementation.
    /// </summary>
    public IEnumBSTR DataDictionaryTableNames
    {
      get { return null; }
    }

    /// <summary>
    /// Indicates whether the extension owns a dataset type.
    /// </summary>
    /// <param name="datasetType">The type of dataset to check.</param>
    /// <returns>False; this extension owns no dataset types.</returns>
    public Boolean OwnsDatasetType(esriDatasetType datasetType)
    {
      return false;
    }

    /// <summary>
    /// Returns a reference to the extension's workspace.
    /// </summary>
    public IWorkspace Workspace
    {
      get { return workspaceHelper.Workspace; }
    }
    #endregion

    #region IWorkspaceReplicaSyncEvents Members
    /// <summary>
    /// Occurs in the replica geodatabase after data changes have been exported
    /// from that replica geodatabase to a delta database.
    /// Not used in this implementation.
    /// </summary>
    public void AfterExportingDataChanges(IReplica sourceReplica, object dataChangesSource, object deltaFile)
    {
      // Not used in this implementation.
    }

    /// <summary>
    /// Occurs in the master geodatabase after data changes in either a replica
    /// geodatabase or delta database are transferred to the master geodatabase.
    /// </summary>
    /// <param name="targetReplica">The target replica.</param>
    /// <param name="dataChangesSource">A collection of changes made to the master geodatabase.</param>
    /// <param name="oidMappingTable">Not used in this implementation.</param>
    /// <param name="changesTable">Not used in this implemented.</param>
    public void AfterSynchronizingDataChanges(IReplica targetReplica, object dataChangesSource, ITable oidMappingTable, ITable changesTable)
    {
      // Make sure that the correct replica is being synchronized.
      String replicaName = targetReplica.Name;
      String unqualifiedReplicaName = replicaName.Substring(replicaName.LastIndexOf('.') + 1);
      if (!unqualifiedReplicaName.Equals(rasterReplicaName))
      {
        return;
      }

      // Get the rasters to pull if connected synchronization is occurring.
      IDataChanges3 dataChanges3 = dataChangesSource as IDataChanges3;
      if (dataChanges3 != null)
      {
        // Get the source's replicas.
        IName sourceWorkspaceName = (IName)dataChanges3.ParentWorkspaceName;
        IWorkspace sourceWorkspace = (IWorkspace)sourceWorkspaceName.Open();
        IWorkspaceReplicas sourceWorkspaceReplicas = (IWorkspaceReplicas)sourceWorkspace;

        // Get the replica generation numbers.
        int genBegin = 0;
        int genEnd = 0;
        int targetGen = 0;
        dataChanges3.GenerationNumbers(out genBegin, out genEnd, out targetGen);
        IQueryFilter queryFilter = new QueryFilterClass();
        queryFilter.WhereClause = String.Format("{0} > {1} or {0} is NULL", genFieldName, genBegin);

        // Open a cursor to get the rasters to copy form the source.
        IRasterWorkspaceEx sourceRasterWorkspaceEx = (IRasterWorkspaceEx)sourceWorkspace;
        IRasterCatalog sourceRasterCatalog = sourceRasterWorkspaceEx.OpenRasterCatalog(rasterCatalogName);
        IFeatureClass sourceFeatureClass = (IFeatureClass)sourceRasterCatalog;
        int sourceGenFieldIndex = sourceFeatureClass.FindField(genFieldName);
        IFeatureCursor sourceCursor = sourceFeatureClass.Search(queryFilter, true);

        // Open the target raster catalog.
        IRasterWorkspaceEx targetRasterWorkspaceEx = (IRasterWorkspaceEx)workspaceHelper.Workspace;
        IRasterCatalog targetRasterCatalog = targetRasterWorkspaceEx.OpenRasterCatalog(rasterCatalogName);
        IFeatureClass targetFeatureClass = (IFeatureClass)targetRasterCatalog;
        int targetGenFieldIndex = targetFeatureClass.FindField(genFieldName);
        IFeatureCursor targetCursor = targetFeatureClass.Insert(true);

        // Copy the rasters from the source to the target.
        IFeature sourceFeature = null;
        while ((sourceFeature = sourceCursor.NextFeature()) != null)
        {
          // Copy the raster and set the target gen to -1 (received).
          IFeatureBuffer featureBuffer = targetFeatureClass.CreateFeatureBuffer();
          featureBuffer.set_Value(targetRasterCatalog.RasterFieldIndex, sourceFeature.get_Value(sourceRasterCatalog.RasterFieldIndex));
          featureBuffer.set_Value(targetGenFieldIndex, -1);
          targetCursor.InsertFeature(featureBuffer);

          // Set the source row value to the current generation.
          if (sourceFeature.get_Value(sourceGenFieldIndex) == DBNull.Value)
          {
            sourceFeature.set_Value(sourceGenFieldIndex, genEnd);
          }
          sourceFeature.Store();
        }
        Marshal.FinalReleaseComObject(sourceCursor);
        Marshal.FinalReleaseComObject(targetCursor);
      }
    }

    /// <summary>
    /// Occurs in the replica geodatabase before data changes are exported from that replica geodatabase to a delta database.
    /// Not used in this implementation.
    /// </summary>
    public void BeforeExportingDataChanges(IReplica sourceReplica, object dataChangesSource, object deltaFile)
    {
      // Not used in this implementation.
    }

    /// <summary>
    /// Occurs in the master geodatabase before data changes in either a replica geodatabase or delta
    /// database are transferred to the master geodatabase.
    /// Not used in this implementation.
    /// </summary>
    public void BeforeSynchronizingDataChanges(IReplica targetReplica, object dataChangesSource)
    {
      // Not used in this implementation.
    }
    #endregion
  }
}