• Share
    • Twitter
    • LinkedIn
    • Facebook
    • Email
  • Feedback
  • Edit
Show / Hide Table of Contents

CustomPriming method

Some tooltip text!
• 8 minutes to read
 • 8 minutes to read

CustomPriming is the third and final method executed during the DictionaryStep pipeline, and is used to make data transformations that are not otherwise supported. To support complex data transformation, the base class exposes two helper methods to perform SQL actions towards the database: ExecuteSql and ExecuteSelect.

ExecuteSql method

While you really should use Imp files for the bulk of priming data, CustomPriming is used for circumstances when you need to perform raw SQL towards existing tables. In those cases, ExecuteSql is there to help execute non-select SQL statements.

Let’s begin with a simple Insert statement example.

In the following code, two datetime variables are declared for use as parameters to the Insert statement. Then the ExecuteSql method is invoked with two parameters: the SQL statement performing the INSERT, and then the anonymous type containing the parameter values.

public override void CustomPriming()
{
  var utcNow = DateTime.UtcNow;
  var never = DateTime.MinValue;
  ExecuteSql(@"INSERT INTO { abc} (
    { abc.abc_id},
    { abc.xyz},
    { abc.registered},
    { abc.registered_associate_id},
    { abc.updated},
    { abc.updated_associate_id},
    { abc.updatedCount}
    ) VALUES (
    { @abc_id},
    { @xyz},
    { @registered},
    { @registered_associate_id},
    { @updated},
    { @updated_associate_id},
    { @updatedCount}
    )",
    new
    {
      abc_id = 1,
      xyz = "A String",
      registered = utcNow,
      registered_associate_id = 0,
      updated = never,
      updated_associate_id = 0,
      updatedCount = 0
    });
}

There are three representations of curly braces used to define the table, fields and values:

  • {abc} means "table abc";
  • {abc.xyz} means "field xyz of table abc",
  • {@foobar} means "parameterized value foobar", and named the same as the property in the anonymous parameter class.

Continuous Database will look up the table and field names, do quoting and case conversion, and anything else that might be needed to make valid SQL, and then properly encode the parameters.

Warning

Please always use parameterized values. Doing so avoids two major hazards: formatting problems (particularly dates!) and the possibility of SQL injection. As a rule, a dictionary step should never depend on externally supplied values, but even an update from one field to another that does not use parameterization could still expose us to SQL injection from values latent in the database. There is no excuse for SQL injection caused by lack of parameterization.

GetNextId

A handy helper method GetNextId('tableName') is useful when you want to get a table's next ID value from the sequence table.

public override void CustomPriming()
{
  var utcNow = DateTime.UtcNow;
  var never = DateTime.MinValue;
  var nextIdValue = GetNextId("abc");
  ExecuteSql(@"INSERT INTO { abc} (
    { abc.abc_id},
    { abc.xyz},
    { abc.registered},
    { abc.registered_associate_id},
    { abc.updated},
    { abc.updated_associate_id},
    { abc.updatedCount}
    ) VALUES (
    { @id},
    { @xyz},
    { @registered},
    0,
    { @updated},
    0,
    0
    )",
    new
    {
      id = nextIdValue,
      xyz = "A String",
      registered = utcNow,
      updated = never
    });
}

Get next ID from sequence table automatically

Another useful trick is to use $nextId to automatically obtain the next ID value from the sequence table.

public override void CustomPriming()
{
  var utcNow = DateTime.UtcNow;
  var never = DateTime.MinValue;
  ExecuteSql(@"INSERT INTO {abc} (
    { abc.abc_id },
    { abc.xyz },
    { abc.registered },
    { abc.registered_associate_id },
    { abc.updated },
    { abc.updated_associate_id },
    { abc.updatedCount }
    ) VALUES (
    { abc.$nextId },
    { @xyz },
    { @registered },
    0,
    { @updated },
    0,
    0
    )",
    new
    {
      xyz = "A String",
      registered = utcNow,
      updated = never
    });
}

Use explicit types as parameters

The following example demonstrates how to use explicit types as parameters. This is a convenient way to bundle all the parameters in a predefined way that can be used by multiple DictionarySteps.

The explicit type can use public or private fields or properties that map to parameters. The casing of the fields or properties is case-sensitive.

ExplicitParameters is a class that contains 5 fields:

private class ExplicitParams
{
  internal int id;
  string xyz = "A string";
  int zero = 0;
  DateTime utcNow = DateTime.UtcNow;
  DateTime never = DateTime.MinValue;
}

All fields in this example are pre-populated except ID. In that case, the field is populated in the constructor of the class as seen in the following example.

public override void CustomPriming()
{
  var nextIdValue = GetNextId("abc");
  ExecuteSql(@"INSERT INTO { abc } (
    { abc.abc_id },
    { abc.xyz },
    { abc.registered },
    { abc.registered_associate_id },
    { abc.updated },
    { abc.updated_associate_id },
    { abc.updatedCount }
    ) VALUES (
    { @id },
    { @xyz },
    { @utcNow },
    0,
    { @never },
    0,
    0
    )",
    new ExplicitParams
    {
      id = nextIdValue
    });
}
Note

Remember that a DictionaryStep assembly should be self-contained, with no external referenced dependencies. Therefore, do not place explicit type for SQL parameters in external libraries.

One more common scenario is when new columns are introduced and the pre-existing data must be migrated or transformed in some way.

Transfer data from old to new field

The following code snippet is an example of a new field added to an existing table. Then the CustomPriming method executes an UPDATE statement that transfers the data from the old field into the new field and sets the updated field.

public override void Structure()
{
  ModifyTable("abc")
    .AddString("def", "Descrition", 100, notNull: false)
  ;
}

public override void CustomPriming()
{
  ExecuteSql(@"UPDATE {abc}
    SET {abc.def} = {abc.xyz}, 
      {unogroup.updated } = {@now}",
    new { now = DateTime.Now });
}

ExecuteSql is a great way to migrate data when the data is known. However, for scenarios when you don’t know the data or the needed data is in the database, you use ExecuteSelect.

ExecuteSelect method

When there is data in the database that needs to be obtained during the DictionaryStep pipeline, ExecuteSelect is there to execute a query and return the result in a DataTable. ExecuteSelect accepts two parameters: the SQL statement to execute, and optional parameters. It returns a standard DataTable object that is disconnected from the database.

A useful place for ExecuteSelect is in the ImpFileName method, to first check if priming data exists, and potentially only set it by returning the name of the imp file if the tables don’t contain any data.

public override List<string> ImpFileNames()
{
  var abcData = ExecuteSelect(@"SELECT {abc.abc_id} FROM {abc}");
  if (abcData.Rows.Count == 0)
  {
    return new List<string>
    {
       @"abc.imp"
    };
  }
  else
    return new List<string>();
}

The formatting of the SQL statements must use the same structure as the ExecuteSql method. When query criteria parameters are needed, use the second parameter to pass in an explicit or anonymous type with the fields or properties that contain the values.

var sql = @"SELECT {abc.abc_id} FROM {abc} WHERE {abc.abc_id} = {@criteria}";
var abcData = ExecuteSelect(sql, new { criteria = 123 });
In This Article
© SuperOffice. All rights reserved.
SuperOffice |  Community |  Release Notes |  Privacy |  Site feedback |  Search Docs |  About Docs |  Contribute |  Back to top