Scopes

What is a scope ?

Scopes are objects containing the list of the tables to synchronize.
They are stored in the scope_info table, on each side (Server data source / All Client data sources).

Note

You can change the default table name scope_info using the ScopeInfoTableName property from SyncOptions:

var options = new SyncOptions();
options.ScopeInfoTableName = "table_information";

Basically, a scope is defining a setup and its database schema, and is identified by a unique name.

For example, you can define multiples scopes representing:

  • A first scope called “products” : Contains the Product , ProductCategory and ProductModel tables.

  • A second scope called “customers”: Contains the Customer and related SalesOrderHeader tables.

  • A third scope with the default name (default “DefaultScope”), containing all the tables to sync.

The scope_info table (and the scope record) is automatically created by DMS when you start a synchronization.
You can also create it manually if you want to, using the CreateScopeInfoTableAsync method.

Example:

var serverProvider = new SqlSyncChangeTrackingProvider(serverConnectionString);
var clientProvider = new SqliteSyncProvider(clientConnectionString);

var setup = new SyncSetup("ProductCategory", "ProductModel", "Product",
    "Address", "Customer", "CustomerAddress", "SalesOrderHeader", "SalesOrderDetail" );

var agent = new SyncAgent(clientProvider, serverProvider);
var s1 = await agent.SynchronizeAsync(setup);

Here is the scope_info table, once the sync is complete:

sync_scope_name

sync_scope_setup

sync_scope_schema

DefaultScope

{ “t” : [{“ProductCategory”, “Product”, “ProductModel”, “Address”, “Customer”, …}] }

{ “t” : [{“Prod….”}] }

Note

The sync_scope_setup column is a serialized json object, containing all the tables and options you defined from your SyncSetup instance.

The corresponding object in DMS is the ScopeInfo class. It basically contains all fields from the scope_info table.

Methods & Properties

You can access a SyncScope stored on the server or client, using a LocalOrchestrator or RemoteOrchestrator instance (directly from a SyncAgent instance or by creating a new instance directly)

Properties

Once a first scope sync has been done, you will have, on both sides, a scope_info table, containing:

  • A scope name: Defines a user friendly name (unique) for your scope. Default name is DefaultScope.

  • A setup, serialized: Contains all the tables and options you defined from your SyncSetup instance.

  • A schema, serialized: Contains all the tables, filters, parameters and so on, for this scope.

  • The last cleanup timestamp: This timestamp is used to cleanup the tracking tables. It’s the last time the cleanup was done.

  • The version: DMS database version.

The corresponding .NET objet is the ScopeInfo class:

public class ScopeInfo
{
    public string Name { get; set; }
    public SyncSet Schema { get; set; }
    public SyncSetup Setup { get; set; }
    public string Version { get; set; }
    public long? LastCleanupTimestamp { get; set; }
    public string Properties { get; set; }
}

GetScopeInfoAsync

Get the scope info from the database. This method will return a ScopeInfo object:

Note

If the scope_info table is not existing, it is created.

If no scope record is found, an empty scope will be created with empty schema and setup properties.

var localOrchestrator = new LocalOrchestrator(clientProvider);
var scopeInfo = await localOrchestrator.GetScopeInfoAsync();

if (scopeInfo.Schema == null)
    return;

foreach (var schemaTable in scopeInfo.Schema.Tables)
{
    Console.WriteLine($"Table Name: {schemaTable.TableName}");

    foreach (var column in schemaTable.Columns)
        Console.WriteLine($"\t{column}. {(column.AllowDBNull ? "NULL": "")}");
}
ScopeInfo

On RemoteOrchestrator, you can use a SyncSetup argument to get a ScopeInfo object containing the Setup and Schema properties filled.

var remoteOrchestrator = new RemoteOrchestrator(clientProvider);
var setup = new SyncSetup("Product, ProductCategory");
var scopeInfo = await remoteOrchestrator.GetScopeInfoAsync(setup);

foreach (var schemaTable in scopeInfo.Schema.Tables)
{
    Console.WriteLine($"Table Name: {schemaTable.TableName}");

    foreach (var column in schemaTable.Columns)
        Console.WriteLine($"\t{column}. {(column.AllowDBNull ? "NULL": "")}");
}

GetAllScopeInfosAsync

Get all scope infos from a data source.

var localOrchestrator = new LocalOrchestrator(clientProvider);
var scopeInfo = await localOrchestrator.GetAllScopeInfosAsync();

SaveScopeInfoAsync

Save a scope info to the local data source.

var scopeInfo = await localOrchestrator.GetScopeInfoAsync();
scopeInfo.Setup = setup;
scopeInfo.Schema = schema;
scopeInfo.ScopeName = "v1";
await localOrchestrator.SaveScopeInfoAsync(scopeInfo);

DeleteScopeInfoAsync

Delete a scope info from the local data source.

var scopeInfo = await localOrchestrator.GetScopeInfoAsync("v0");
await localOrchestrator.DeleteScopeInfoAsync(scopeInfo);

CreateScopeInfoTableAsync

Create a scope info table in local data source.

await localOrchestrator.CreateScopeInfoTableAsync();

ExistScopeInfoTableAsync

Check if a scope_info table exists in the local data source

var exists = await localOrchestrator.ExistScopeInfoTableAsync();

Multi Scopes

In some scenario, you may want to sync some tables at one time, and some others tables at another time.

These kind of scenarios are possible using the multi scopes sync architecture.

How does it work ?

To be able to create a multi scopes scenario, you just have to:

  • Create two SyncSetup instances with your tables / filters and options.

  • Sync your databases calling SynchronizeAsync with a different scope name for each setup.

  • Or call ProvisionAsync with your scope name.

Example

Here is a full example, where we sync separately the Product table, then the Customer table:

// Create 2 Sql Sync providers
var serverProvider = new SqlSyncProvider(DbHelper.GetDatabaseConnectionString(serverDbName));
var clientProvider = new SqlSyncProvider(DbHelper.GetDatabaseConnectionString(clientDbName));

// Create 2 setup
var setupProducts = new SyncSetup("ProductCategory", "ProductModel", "Product");
var setupCustomers = new SyncSetup("Address", "Customer", "CustomerAddress",
            "SalesOrderHeader", "SalesOrderDetail");

// Create an agent
var agent = new SyncAgent(clientProvider, serverProvider);

// Using the Progress pattern to handle progession during the synchronization
var progress = new SynchronousProgress<ProgressArgs>(s =>
    Console.WriteLine($"{s.Context.SyncStage}:\t{s.Message}");
);

Console.WriteLine("Hit 1 for sync Products. Hit 2 for sync customers and sales");
var k = Console.ReadKey().Key;

if (k == ConsoleKey.D1)
{
    Console.WriteLine("Sync Products:");
    var s1 = await agent.SynchronizeAsync("products", setupProducts, progress);
    Console.WriteLine(s1);
}
else
{
    Console.WriteLine("Sync Customers and Sales:");
    var s1 = await agent.SynchronizeAsync("customers", setupCustomers, progress);
    Console.WriteLine(s1);
}

Once you have made the 2 syncs, your local syns_scope table should looks like that:

sync_scope_name

sync_scope_schema

sync_scope_setup

products

{ “t” : [{……}] }

{ “t” : [{……}] }

customers

{ “t” : [{……}] }

{ “t” : [{……}] }