Bidirectional Synchronization (and an extensible model)

(Português) - (English)

I'm playing with Sync Framework and stopped to write a quick hint for those who want to use a bidirectional synchronization between two sources of data. Suppose you have created a new project and added a local database cache item, that generates a .sync file, a .sdf file and a typed dataset, based on the information you provided to the wizards. When you try to sync the client and the server, by default you can't get a bidirectional synchronization and the wizard doesn't provide an interface for you to define this, so you have to code that.

The way to configure this is real simple, you need just to define a table property with bidirectional sync. One way to accomplish this by editing the Sync Agent's generated code (changes in bold):

private void InitializeSyncTables() {
    // Create SyncTables.
    this._clienteSyncTable = new ClienteSyncTable();
    this._clienteSyncTable.SyncDirection = Microsoft.Synchronization.Data.SyncDirection.Bidirectional;
    this._clienteSyncTable.SyncGroup = new Microsoft.Synchronization.Data.SyncGroup("ClienteSyncTableSyncGroup");
    this.Configuration.SyncTables.Add(this._clienteSyncTable);
    this._estadoSyncTable = new EstadoSyncTable();
    this._estadoSyncTable.SyncDirection = Microsoft.Synchronization.Data.SyncDirection.Bidirectional;
    this._estadoSyncTable.SyncGroup = new Microsoft.Synchronization.Data.SyncGroup("EstadoSyncTableSyncGroup");
    this.Configuration.SyncTables.Add(this._estadoSyncTable);
}

Note: in this case, I'm not considering conflict detection and resolution, in a later post I'll address this.
The problem with this approach is that if you use the wizard to update your project, the instructions you inserted will be erased. Of course, this isn't the recommended approach, if you note in the sync agent constructor, you will notice the following method invocation order:

            this.InitializeSyncProviders();
            this.InitializeSyncTables();
            this.OnInitialized();

If you look at the OnInitialized method, you'll notice that it's a partial method without a body, so it works like an extension point to the class, invoked after the sync agent has configured the providers and the tables, enabling you to alter some configuration. So, in your case, instead of editing the generated code, I'll create a new class and add the code I want in the OnInitialized method.

public partial class BDLocalSyncAgent {

    partial void OnInitialized() {
        this.Estado.SyncDirection = Microsoft.Synchronization.Data.SyncDirection.Bidirectional;
        this.Cliente.SyncDirection = Microsoft.Synchronization.Data.SyncDirection.Bidirectional;
    }
}

Note about partial methods: An optional implementation may be defined in the same part or another part. If the implementation is not supplied, then the method and all calls to the method are removed at compile time.

What I want to emphasize here is the approach used by the Sync Framework developer. It created a point that enables the developer to add some code, without messing with the original methods. Every time that you are writing your framework or working in your home made code generator, you should think in advance where you want to allow developers to add their own behavior, by creating some extension points.

For more information about the bidirectional synchronization, read the document pointed in this post:http://blogs.msdn.com/sync/archive/2008/06/16/extending-visual-studio-2008-sp1-sync-designer-to-support-bi-directional-synchronization.aspx

Eu estou brincando com o Sync Framework e parei para escrever uma dica rápida para aqueles que querem usar sincronização bidirecional entre duas fontes de dados. Suponha que você já criou um projeto, adicionou o item "local database cache", que gera um arquivo .sync, um arquivo .sdf e um dataset tipado, baseado nas informações que você forneceu através dos wizards. Quando você tenta sincronizar o cliente com o servidor, por padrão você não consegue uma sincronização bidirecional e o wizard não possui uma interface que nos permite definir isso, então precisamos codificar.

A maneira de configurar isso é realmente simples, você somente precisa definir uma propriedade da tabela para sync bidirecional. Uma maneira de conseguir isso é editando o código gerado para o agente de sync (alterações em negrito):

private void InitializeSyncTables() {
    // Create SyncTables.
    this._clienteSyncTable = new ClienteSyncTable();
    this._clienteSyncTable.SyncDirection = Microsoft.Synchronization.Data.SyncDirection.Bidirectional;
    this._clienteSyncTable.SyncGroup = new Microsoft.Synchronization.Data.SyncGroup("ClienteSyncTableSyncGroup");
    this.Configuration.SyncTables.Add(this._clienteSyncTable);
    this._estadoSyncTable = new EstadoSyncTable();
    this._estadoSyncTable.SyncDirection = Microsoft.Synchronization.Data.SyncDirection.Bidirectional;
    this._estadoSyncTable.SyncGroup = new Microsoft.Synchronization.Data.SyncGroup("EstadoSyncTableSyncGroup");
    this.Configuration.SyncTables.Add(this._estadoSyncTable);
}

Nota: nesse caso eu não estou levando em conta a detecção e resolução de conflitos.
O problema dessa abordagem é que se você utiliza o wizard para atualizar o seu projeto, as instruções que foram adicionadas serão apagadas. É claro que essa não é a abordagem recomendada, se você olhar o construtor do agente de sync, vai notar os seguintes métodos sendo invocados:

            this.InitializeSyncProviders();
            this.InitializeSyncTables();
            this.OnInitialized();

Analisando o método OnInitialized, verá que ele é um método parcial sem corpo, então funciona como um ponto de extensão para a classe, invocado após o agente de sync ter configurado os providers e as tabelas, possibilitando que você altere alguma configuração. Então, nesse caso, ao invés de editar o código gerado, vamos criar uma nova classe e adicionar o código desejado no método OnInitialized.

public partial class BDLocalSyncAgent {

    partial void OnInitialized() {
        this.Estado.SyncDirection = Microsoft.Synchronization.Data.SyncDirection.Bidirectional;
        this.Cliente.SyncDirection = Microsoft.Synchronization.Data.SyncDirection.Bidirectional;
    }
}

Nota sobre método parcial: uma implementação pode ser definida opcionalmente. Se a implementação não for fornecida, então o método e as chamadas a ele serão removidos em tempo de compilação.

O que eu quero enfatizar aqui é a abordagem utilizada pelo desenvolvedor do Sync Framework. Ele criou um ponto que permite o desenvolvedor adicionar algum código sem sujar os métodos originais. Toda vez que você estiver escrevendo seu framework ou trabalhando com seu gerador de código caseiro, deve pensar com antecedência aonde você quer que os desenvolvedores adicionem código customizado, criando pontos de extensão.
Para mais informações sobre a sincronização bidirecional, leia o documento apontado no post: http://blogs.msdn.com/sync/archive/2008/06/16/extending-visual-studio-2008-sp1-sync-designer-to-support-bi-directional-synchronization.aspx

[]s
Luciano Caixeta Moreira
luciano.moreira@microsoft.com

===============================================
This post is provided "AS IS" and confers no right
===============================================