2

みなさん、良い一日を、

私が話しているバージョンは今ではかなり時代遅れになっていることは知っていますが、それは職場での作業にこだわっているツールです. これは StackOverflow に関する私の最初の質問であり、適切な書式設定を取得できることを願っています ;-) 長いテキストについてはご容赦ください。私は多くの詳細を提供することに慣れています。より正確な答えを提供してください;-)

ほぼ 10 年間 IT に携わってきましたが、適切に選択されたキーワードと表現をグーグル検索することで、常に自分の質問に対する答え (つまり、問題の解決策) を見つけることができました。前述の Sync Framework は、インターネット コミュニティにはあまり知られていないか、ほとんどの人間にとって最も単純な概念を理解しようとするのが本当に面倒なようです。広範な調査の結果、Sync Framework 1.0 と C# 言語を使用して SQL Express を同期する単純な例を 1 つ見つける必要がありました。MSDN にもありません! 私は ASP.NET / C# にかなり慣れていませんが、概念を理解しており、SQL Server 2008 データベースからデータを正常に格納および取得する Web アプリケーションを使用しています。これは、クライアントによって2年間使用されています。クライアントがデータをオフラインにし、オフラインで更新してからサーバーと同期できるようにする必要があります。UPDATE、INSERT、および DELETE は両端で発生します。

私が見つけようとしているのは非常に単純です (またはそう思った): SQL Server 変更追跡情報 (カスタム変更追跡ではない) を使用してサーバー (SQL Server 2008) とクライアント コンピューター (SQL Server 2008 Express、コンパクト版ではありません)。最も単純なケースは、列がほとんどない単一のテーブルです。私は SQL Server の部分を十分に理解しており、データベースの両側でクライアント Web アプリケーションからの同期要求を受信できるように準備しました (Change Tracking を有効にし、PrimaryKeyID にはデータ型 GUID があり、サーバー上のアプリケーションのユーザー アカウントには VIEW_CHANGE_TRACKING 権限があります)。など)

2つの間のインターフェイスとして機能し、同期セッションを管理するのはWebアプリケーションであることを知っています(C#で)。後は 2 つの接続文字列を指定し、どのテーブルを同期するかを指定し、双方向同期を指定するだけだと考えるのは非常に単純でした。どうやら、それはそれよりも複雑です。必死の試みで、私は自分のコードを Microsoft の次のコードに基づいて SQL Express に適合させようとしました (例は Compact 用です)。私は敗北を認め、恥ずかしそうに頭を下げるところです:-(

http://msdn.microsoft.com/en-us/library/bb726015%28v=sql.100%29.aspx

上記 (2 番目のセクション「SQL Server Change Tracking を使用した完全な例」) に基づいて、パスワード、統計、データに変更を適用するコードに関連するものなど、不要なものをすべて削除しました。わかりやすくするために、MS の多数のコメント行も削除しました。SSMS でスクリプトを実行する両端の SQL Server 自体に手動で変更を適用しました (したがって、生成された変更追跡情報が必要であり、Web アプリが同期を要求するときに使用できます)。質問 1: そう言うのは間違っていますか? 最後に、Compact の代わりに SQL Express に関連するオブジェクトを使用するために、いくつか変更を加えました。

質問 2: Microsoft のコードは、それがこのレプリカの最初の (最初の) 同期か、それ以降の同期かを明らかにできるようです。どうすればいいのかわからない!

最後に、最も単純な形式で残されているコードは次のとおりです (質問 3、4、5 ;-) が、いくつかのエラーが表示されます。ご協力いただき、誠にありがとうございました。コメントや提案は大歓迎です。これが解決された場合、多くの人に恩恵をもたらすと確信しています。私は自分の側でそれについて調査を続けます (上司は私に選択肢を与えません ;-) 同期に成功した場合は、ここに解決策を投稿することを約束します!

ありがとう、そして皆さん、素晴らしい一日を!

よろしくお願いします。

ジクシー

using System;
using System.Collections;
using System.Configuration;
using System.Data;
using System.Data.SqlClient;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;

using System.IO;
//using System.Data.SqlServerCe;
using Microsoft.Synchronization;
using Microsoft.Synchronization.Data;
using Microsoft.Synchronization.Data.Server;
//using Microsoft.Synchronization.Data.SqlServerCe;

namespace some_namespace
{
public class SyncProgram
{
    public SyncProgram()
    {
           // empty constructor
    }

    public static bool MainSync() // Entry point, say, called by a Sync button on an ASPX page.
    {
        bool boolSyncRes = false; // tells whether sync was a success or not

        // Initial sync: they create a new instance of the Orchestrator.
        ZyxySyncOrchestrator zyxySyncOrchestrator = new ZyxySyncOrchestrator();

        // Subsequent synchronization.
        // They don't. there was only irrelevant stats stuff here.

        boolSyncRes = true;
        return boolSyncRes;

            }
}

public class ZyxySyncOrchestrator : SyncOrchestrator
{
    public ZyxySyncOrchestrator()
    {
       Utility util = new Utility();

       this.LocalProvider = new ZyxyServerSyncProvider(); // QUESTION 3: ??? cannot implicitly convert type DbServerSyncProvider to Microsoft.Synchronization.SyncProvider

        //Instantiate a server synchronization provider and specify it
        //as the remote provider for this synchronization agent.
       this.RemoteProvider = new ZyxyServerSyncProvider(); // cannot implicitly convert type DbServerSyncProvider to Microsoft.Synchronization.SyncProvider

        // QUESTION 4: Is the following code actually creating the base (user) table ZyxySync
        // (as opposed to its change tracking metadata table)??
        // I wasn't sure whether this part of the code on Microsoft's webpage was part of 
        // populating the db with sample data and structure or if it's really meant to deal with 
        // the change tracking metadata.
        SyncTable zyxySyncTable = new SyncTable("ZyxySync");
        zyxySyncTable.CreationOption = TableCreationOption.DropExistingOrCreateNewTable;
        zyxySyncTable.SyncDirection = SyncDirection.DownloadOnly;
        this.Configuration.SyncTables.Add(zyxySyncTable);

    }
}

//Create a class that is derived from Microsoft.Synchronization.Server.DbServerSyncProvider.
public class ZyxyServerSyncProvider : DbServerSyncProvider
{
    public ZyxyServerSyncProvider()
    {
        Utility util = new Utility();
        SqlConnection serverConn = new SqlConnection(util.ServerConnString);
        this.Connection = serverConn;

        //Retrieve a new anchor value from the server. We use a timestamp value
        //that is retrieved and stored in the client database.
        //During each sync the new and last anchor values are used to determine the set of changes
         SqlCommand selectNewAnchorCommand = new SqlCommand();
        string newAnchorVariable = "@" + SyncSession.SyncNewReceivedAnchor;
        selectNewAnchorCommand.CommandText =
            "SELECT " + newAnchorVariable + " = change_tracking_current_version()";
        selectNewAnchorCommand.Parameters.Add(newAnchorVariable, SqlDbType.BigInt);
        selectNewAnchorCommand.Parameters[newAnchorVariable].Direction = ParameterDirection.Output;
        selectNewAnchorCommand.Connection = serverConn;
        this.SelectNewAnchorCommand = selectNewAnchorCommand;


        //Create a SyncAdapter for the ZyxySync table by using 
        //the SqlSyncAdapterBuilder.
       //   Specify a name for the SyncAdapter that matches the
        //    the name specified for the corresponding SyncTable.
          SqlSyncAdapterBuilder zyxyBuilder = new SqlSyncAdapterBuilder(serverConn);

        zyxyBuilder.TableName = "dbo.ZyxySync";
        zyxyBuilder.ChangeTrackingType = ChangeTrackingType.SqlServerChangeTracking;

        SyncAdapter zyxySyncAdapter = zyxyBuilder.ToSyncAdapter();
        zyxySyncAdapter.TableName = "ZyxySync";
        this.SyncAdapters.Add(zyxySyncAdapter);

    }
}

  // Class derived from Microsoft.Synchronization.Data.Server.DbServerSyncProvider
// QUESTION 5: Or should have I used the two below? I believe they only apply to SQL Compact... 
//Microsoft.Synchronization.Data.ClientSyncProvider
//Microsoft.Synchronization.Data.ServerSyncProvider
//http://msdn.microsoft.com/en-us/library/microsoft.synchronization.data.clientsyncprovider%28v=sql.100%29.aspx
//http://msdn.microsoft.com/en-us/library/microsoft.synchronization.data.server.dbserversyncprovider%28d=printer,v=sql.100%29.aspx

public class ZyxyClientSyncProvider : DbServerSyncProvider
{

    public ZyxyClientSyncProvider()
    {
        Utility util = new Utility();
        SqlConnection clientConn = new SqlConnection(util.ClientConnString);
        this.Connection = clientConn;
    }
}

public class Utility
{
    public string ClientConnString
    {
       get { return @"Data Source=localhost\LocalExpressInstance;Initial Catalog=DatabaseName;User ID=UserName;Password=WontTellYou;"; }
    }

    public string ServerConnString
    {
          get { return @" Data Source=ServerName\ServerInstance;Initial Catalog=DatabaseName;User ID=UserName;Password=WontTellYou;"; }
    }
}

}

4

2 に答える 2

1

わかりましたので、これが機能する簡単なコードサンプルです(私の場合はとにかく)。上記の手順 (Change Tracking の有効化、適切なユーザー権限の設定など) に加えて、私が理解できなかった点は次のとおりです。

1)同期フレームワークと同期セッションがすべてクライアント側で管理されるように設定できることがわかりました。サーバーにインストールされているものに依存することなく、古い 1.0 の代わりに SF 2.1 を使用できました。それは大いに役立ちました。

2) 同期セッションの準備として、同期の準備ができるように、まずデータベースをプロビジョニングする必要があります。私がしたことは、クライアント接続文字列を使用して次の C# を実行し (クライアント db をプロビジョニングするため)、サーバー接続文字列を使用して再度実行することです (サーバー db をプロビジョニングするため)。これは、データベースを準備するための (両側で) 1 回実行されるプログラムです。確立するすべての同期セッションに対して実行するわけではありません。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Collections;
using System.Configuration;
using System.Data;
using System.Data.SqlClient;
using System.Text.RegularExpressions;
using System.Security.Principal;
using System.IO;

using Microsoft.Synchronization;
using Microsoft.Synchronization.Data;
using Microsoft.Synchronization.Data.Server;
using Microsoft.Synchronization.Data.SqlServer;  // SF 2.1
using Microsoft.Synchronization.SimpleProviders; // SF 2.1
using Microsoft.Synchronization.MetadataStorage; // SF 2.1

// ZYXY: Based on:
// http://msdn.microsoft.com/en-us/library/ff928603.aspx
// NOTES:
// - Microsoft Sync Framework 2.1 redistributable package must be installed on Client computers but is not required on the Server, as long as a server-side synchronization setup is performed by a client computer.
// This is a run once program.

namespace DISS_Database_Sync_Provisioning_Console
{
class Program
{
    static void Main(string[] args)
    {


        SqlConnection sqlConn = new SqlConnection("Data Source=ServerName\\InstanceName;Initial Catalog=SomeDatabase;User ID=SOmeUser;Password=SomePassword;");

     Console.Write("Provisioning database...");



        // define a new scope named DISS_Sync_Scope
        DbSyncScopeDescription scopeDesc = new DbSyncScopeDescription("DISS_Sync_Scope");

        // get the description of the ZyxySync table
        DbSyncTableDescription tableDesc = SqlSyncDescriptionBuilder.GetDescriptionForTable("dbo.ZyxySync", sqlConn);

        // add the table description to the sync scope definition
        scopeDesc.Tables.Add(tableDesc);

        // create a server scope provisioning object based on the DISS_Sync_Scope
        SqlSyncScopeProvisioning sqlProvision = new SqlSyncScopeProvisioning(sqlConn, scopeDesc);

        // skipping the creation of base table since table already exists
        sqlProvision.SetCreateTableDefault(DbSyncCreationOption.Skip);

        // start the provisioning process
        sqlProvision.Apply();

        sqlConn.Close();
        sqlConn.Dispose();

        Console.Write("\nDatabase has been successfully configured for synchronization. Please press any key to exit.");
        Console.Read();

    }
}
}

3) 以下は、同期が開始されるたびに実行されるコードです (たとえば、ユーザーが Web アプリケーションで [同期] ボタンをクリックしたときなど)。

using System;
using System.Collections;
using System.Configuration;
using System.Data;
using System.Data.SqlClient;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;

using System.Security.Principal;

using System.IO;
using Microsoft.Synchronization;
using Microsoft.Synchronization.Data;
using Microsoft.Synchronization.Data.Server;
using Microsoft.Synchronization.Data.SqlServer;  // SF 2.1
using Microsoft.Synchronization.SimpleProviders; // SF 2.1
using Microsoft.Synchronization.MetadataStorage; // SF 2.1

namespace diss_ssmb
{



public class SyncProgram
{


    public SyncProgram()
    {
           // empty constructor
    }

    public static bool MainSync() // Entry point, say, called by a Sync button on an ASPX page.
    {
        bool boolSyncRes = false; // tells whether sync was a success or not

        // Initial sync: they create a new instance of the Orchestrator.
        ZyxySyncOrchestrator zyxySyncOrchestrator = new ZyxySyncOrchestrator();

        // Subsequent synchronization.
        // They don't. there was only irrelevant stats stuff here.

        boolSyncRes = true;
        return boolSyncRes;

            }
}

public class ZyxySyncOrchestrator : SyncOrchestrator
{
    public ZyxySyncOrchestrator()
    {
       Utility util = new Utility();


       this.LocalProvider = new ZyxyClientSyncProvider();

        //Instantiate a server synchronization provider and specify it
        //as the remote provider for this synchronization agent.
       this.RemoteProvider = new ZyxyServerSyncProvider();

       SyncTable zyxySyncTable = new SyncTable("ZyxySync");
        zyxySyncTable.CreationOption = TableCreationOption.DropExistingOrCreateNewTable;
        zyxySyncTable.SyncDirection = SyncDirection.Bidirectional;
    //    this.Configuration.SyncTables.Add(zyxySyncTable);
        this.Synchronize();
    }
}

public class ZyxyServerSyncProvider : SqlSyncProvider
{
    public ZyxyServerSyncProvider()
    {
        Utility util = new Utility();
        SqlConnection serverConn = new SqlConnection(util.ServerConnString);
        this.Connection = serverConn;
        this.ScopeName = "DISS_Sync_Scope";
        //Retrieve a new anchor value from the server. We use a timestamp value
        //that is retrieved and stored in the client database.
        //During each sync the new and last anchor values are used to determine the set of changes
         SqlCommand selectNewAnchorCommand = new SqlCommand();
        string newAnchorVariable = "@" + SyncSession.SyncNewReceivedAnchor;
        selectNewAnchorCommand.CommandText =
            "SELECT " + newAnchorVariable + " = change_tracking_current_version()";
        selectNewAnchorCommand.Parameters.Add(newAnchorVariable, SqlDbType.BigInt);
        selectNewAnchorCommand.Parameters[newAnchorVariable].Direction = ParameterDirection.Output;
        selectNewAnchorCommand.Connection = serverConn;
       // this.SelectNewAnchorCommand = selectNewAnchorCommand; // SF 2.1 commented out because SelectNewAnchorCommand isn't there.



        SqlSyncAdapterBuilder zyxyBuilder = new SqlSyncAdapterBuilder(serverConn);

        zyxyBuilder.TableName = "dbo.ZyxySync";
        zyxyBuilder.ChangeTrackingType = ChangeTrackingType.SqlServerChangeTracking;

        SyncAdapter zyxySyncAdapter = zyxyBuilder.ToSyncAdapter();
        zyxySyncAdapter.TableName = "ZyxySync";
        //    this.SyncAdapters.Add(zyxySyncAdapter); // SF 2.1 commented out because SelectNewAnchorCommand isn't there.

    }
}

public class ZyxyClientSyncProvider : SqlSyncProvider

{

    public ZyxyClientSyncProvider()
    {
        Utility util = new Utility();
        SqlConnection clientConn = new SqlConnection(util.ClientConnString);
        this.Connection = clientConn;
        this.ScopeName = "DISS_Sync_Scope";
    }
}

public class Utility
{
    public string ClientConnString
    {
       get { return @"Some connection string such as in the above code sample"; }
    }

    public string ServerConnString
    {
        get { return @"Some serverconnection string such as in the above code sample";     }
    }
}
}

4) 上記は、2 つの連続する同期セッション間で INSERT、UPDATE、および DELETE が両端で同時に発生した場合に双方向で正常に同期しましたが、競合を解決する必要がなかった場合 (たとえば、両端で同じレコードが更新された場合)。解決しなければならない競合がある場合は、さらにテストを行う必要があります。同期フレームワークはデフォルトでこのような競合をどのように解決していますか? これらの設定を調整して、次のいずれかに基づいて勝者を確立するように指示できると思います...-タイムスタンプ値-レプリカID-ユーザーロール-トランザクションタイプ-...

とにかく、ウェブからそれを理解するのに本当に苦労したので、それが誰かに役立つことを本当に願っています! 幸運を!

ジクシー

于 2012-08-24T17:19:25.003 に答える