Developer forum

Forum » Dynamicweb 10 » Integration Provider issues. FileProviders are hardcoded and not extendable

Integration Provider issues. FileProviders are hardcoded and not extendable

John Collins
Reply

I have a need to have similar functionality to the FileProviders where the source and destination are exactly the same. This works great for FileProviders where there is a lock on the destination table, then when you select the source table the destination table is automatically populated. Also, the mapping is autopopulated with the same fields.
The class : public static class ProviderExtensions

has a hardcoded function :

    private static bool IsFileProvider(Type type)
    {
        var fileProviders = new List<string>() { "XmlProvider", "ExcelProvider", "CsvProvider", "JsonProvider" };
        return fileProviders.Any(name => string.Equals(type.Name, name, StringComparison.OrdinalIgnoreCase));
    }

 

Which I can't extend in anyway,


Replies

 
John Collins
Reply

I'm trying to create a StagingTableProvider which can take a source and automatically create staging tables.

Here is a suggestion for a change which would make this more flexible:

  Interface Flexibility for Custom Transformations

  Enhanced Interface Design

  public interface ISameAsSourceProvider
  {
      bool SupportsSameAsSource { get; }

      // This method gives you complete control over destination schema creation
      Schema CreateDestinationSchemaFromSource(Schema sourceSchema, string? requestedDestinationTableName = null);

      // Optional: Transform individual table names
      string TransformTableName(string sourceTableName) => sourceTableName;

      // Optional: Transform column definitions
      Column TransformColumn(Column sourceColumn) => sourceColumn;
  }

  Implementation Examples

  Example 1: Change Table Names
  public class StagingTableProvider : BaseProvider, IDestination, ISameAsSourceProvider
  {
      public bool SupportsSameAsSource => true;

      public Schema CreateDestinationSchemaFromSource(Schema sourceSchema, string? requestedDestinationTableName = null)
      {
          var destinationSchema = new Schema();

          foreach (var sourceTable in sourceSchema.GetTables())
          {
              // Transform table name with prefix
              var destinationTableName = $"Staging_{sourceTable.Name}";

              var destinationTable = destinationSchema.AddNewTable(destinationTableName, sourceTable.SqlSchema);

              // Copy and potentially transform columns
              foreach (var sourceColumn in sourceTable.Columns)
              {
                  var transformedColumn = TransformColumn(sourceColumn);
                  destinationTable.AddNewColumn(transformedColumn.Name, transformedColumn.Type, transformedColumn.IsPrimaryKey, transformedColumn.IsAutoIncrement);
              }
          }

          return destinationSchema;
      }

      public string TransformTableName(string sourceTableName)
      {
          return $"Staging_{sourceTableName}";
      }

      public Column TransformColumn(Column sourceColumn)
      {
          // Example: Add audit columns
          var column = sourceColumn.Clone();

          // Could modify column types, names, constraints, etc.
          if (sourceColumn.Name == "CreatedDate")
          {
              column.Name = "StagingCreatedDate";
          }

          return column;
      }
  }

  Example 2: Add Staging-Specific Columns
  public Schema CreateDestinationSchemaFromSource(Schema sourceSchema, string? requestedDestinationTableName = null)
  {
      var destinationSchema = new Schema();

      foreach (var sourceTable in sourceSchema.GetTables())
      {
          var destinationTable = destinationSchema.AddNewTable(sourceTable.Name, sourceTable.SqlSchema);

          // Copy all source columns
          foreach (var sourceColumn in sourceTable.Columns)
          {
              destinationTable.AddNewColumn(sourceColumn.Name, sourceColumn.Type, sourceColumn.IsPrimaryKey, sourceColumn.IsAutoIncrement);
          }

          // Add staging-specific audit columns
          destinationTable.AddNewColumn("StagingLoadDate", typeof(DateTime), false, false);
          destinationTable.AddNewColumn("StagingBatchId", typeof(string), false, false);
          destinationTable.AddNewColumn("StagingSourceFile", typeof(string), false, false);
          destinationTable.AddNewColumn("StagingProcessedFlag", typeof(bool), false, false);
      }

      return destinationSchema;
  }

  Example 3: Data Type Transformations
  public Column TransformColumn(Column sourceColumn)
  {
      var column = sourceColumn.Clone();

      // Example transformations for staging scenarios
      switch (sourceColumn.Type.Name.ToLower())
      {
          case "string":
              // Make all strings nullable in staging for data quality issues
              column.IsNullable = true;
              break;

          case "datetime":
              // Convert all dates to string in staging for problematic data
              column.Type = typeof(string);
              column.Length = 50;
              break;

          case "decimal":
              // Convert decimals to strings to avoid precision issues during staging
              column.Type = typeof(string);
              column.Length = 50;
              break;
      }

      return column;
  }

  Integration with DynamicWeb UI

  The UI would need minor updates to support this:

  TableMappingSaveCommand.cs
  // Instead of just using source table name, call the interface
  if (mapping.Job.Destination is ISameAsSourceProvider sameAsSourceProvider)
  {
      var customSchema = sameAsSourceProvider.CreateDestinationSchemaFromSource(
          JobSchemaService.GetSchema(mapping.Job.Source),
          destinationTableName
      );

      // Use the custom schema instead of direct copy
      JobHelper.AddCustomSchemaToDestinationMapping(mapping, customSchema);
  }

  Benefits of This Approach

  1. Complete Control: Transform table names, column names, data types
  2. Staging-Specific Logic: Add audit columns, change nullability, etc.
  3. Data Quality: Convert problematic types to strings for staging
  4. Flexibility: Different providers can implement different transformation logic
  5. Backward Compatibility: Existing file providers just return sourceSchema.Clone()

  Advanced Use Cases You Could Implement

  - Prefix/Suffix Table Names: Staging_Products, Products_Temp
  - Add Metadata Columns: Load dates, batch IDs, source tracking
  - Data Type Safety: Convert all to strings for staging, then transform later
  - Schema Versioning: Add version columns for change tracking
  - Partitioning Support: Add date partition columns
  - Error Handling Columns: Add columns for data quality flags

 

Some more changes needed for the UI to work similar:

  1. Create New Function in ProviderExtensions.cs

  // ADD this new function alongside the existing IsFileProvider
  private static bool SupportsSameAsSourceBehavior(Type type)
  {
      // Check if implements our new interface
      if (typeof(ISameAsSourceProvider).IsAssignableFrom(type))
          return true;

      // Legacy support: existing file providers also support this behavior
      return IsFileProvider(type);
  }

  // ADD new extension method
  public static bool SupportsSameAsSourceBehavior(this IDestination destination)
  {
      Ensure.NotNull(destination);
      return SupportsSameAsSourceBehavior(destination.GetType());
  }

  2. Update Usage Sites to Use New Function

  Instead of changing every place that calls IsDestinationFileProvider(), we update them to use our new function:

  IntegrationMappingProfile.cs:
  // INSTEAD OF:
  if (mapping.Job.Destination.IsDestinationFileProvider())

  // USE:
  if (mapping.Job.Destination.SupportsSameAsSourceBehavior()) or (mapping.Job.Destination.IsDestinationFileProvider())

  ActivityMappingsListScreen.cs:
  // INSTEAD OF:
  bool isDestinationFileProvider = Job?.Destination?.IsDestinationFileProvider() ?? false;

  // USE:
  bool supportsSameAsSource = (Job?.Destination?.SupportsSameAsSourceBehavior() or Job?.Destination?.IsDestinationFileProvider()) ?? false;

 
John Collins
Reply

deleted

 

 

You must be logged in to post in the forum