Charteris Community Server

Welcome to the Charteris plc Community
Welcome to Charteris Community Server Sign in | Join | Help
in Search

Ivor Bright's blog

July 2010 - Posts

  • Installing .Net 1.1 applications on Windows Server 2008 R2

    I recently needed to get an ASP.Net 1.1 application working on Windows Server 2008 R2. I naively assumed that this was just going to work but then the whole 32/64 bit problem emerged.

    .Net 1.1 is 32 bit only and Server 2008 R2 is 64 bit only! Well – WOW64 has been around quite a while so surely this would be a piece of cake. I proceeded to download the .Net Framework Version 1.1 Redistributable Package and when you attempt to install – you get your first hint about the potential problems ahead.

    At this stage – you have 2 choices:

    • Attempt to upgrade your application to a 64 bit version of the framework
    • Attempt to get .Net 1.1 working on Server 2008 R2

    My preference was to get .Net 1.1 working as I didn’t want to go through a test cycle for an application I didn’t really understand on a new version of the framework. However – rather than just choosing “Run Program” from the Program Compatibility Assistant dialog – I did some research and used the following process to get my application to work.

    1 - Install IIS Metabase Compatibility

    To install on Windows 2008 Server – click Start and then “Server Manager”.

    Under Web Server (IIS), click Add Role Services.

    Ensure that IIS Metabase Compatibility is installed.

    2 - Install .Net 1.1

    I installed .Net 1.1 in the following order:

    I still got a compatibility warning, but chose “Run Program” to continue.

    Installing Service Pack 1 is likely to require a reboot.

    3 – Enable ASP.Net 1.1 ISAPI Extension

    Following the steps in option 2 made ASP.NET v1.1.4322 available on my ISAPI and CGI Restrictions dialog, but was disabled by default. Open Internet Explorer, click on your server name and choose ISAPI and CGI Restrictions from the IIS section. Enable ASP.Net v1.1.4322 as a valid ISAPI extension.

    4 – Adjust machine.config

    We need ASP.NET 1.1 to ignore IIS configuration sections, so open machine.config for Framework 1.1 (%windir%\Microsoft.NET\Framework\v1.1.4322\config\machine.config) and add the following towards the end of configSections.

       1: <section name="system.webServer" type="System.Configuration.IgnoreSectionHandler, 
       2:     System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" /> 

    5 – Adjust the Application pool

    I now needed to tell my application to use the application pool ASP.NET 1.1. Use IIS Manager, choose the site that you are working with and choose “Advanced Settings”. Adjust the application pool to use ASP.NET 1.1 which will use .Net Framework 1.1.

    6 – Fix applicationHost.config bug

    IIS runtime detected that I was running on a 64 bit operating system, so it attempted to load .net framework configuration from Microsoft.Net\Framework64, but this doesn’t exist for .Net Framework 1.1. The solution is to copy the 32 bit version into the appropriate 64 bit folder.

    • Create \Windows\\Framework64\v1.1.4322\config
    • Copy machine.config from \Windows\\Framework\v1.1.4322\Config\

    7 - Check any application registry settings

    My application relied upon a number of custom application registry settings. In the 32 era – this wasn’t a problem as registry settings were in the “same” expected place. e.g. most people put registry settings under HKEY_LOCAL_MACHINE \ SOFTWARE someplace. However – on a 64 bit machine – the 64 bit software settings are stored in this expected place, while 32 bit settings are stored under HKEY_LOCAL_MACHINE \ SOFTWARE \ Wow6432Node.

    This is probably easiest to explain by giving an example. My application needed to access HKEY_LOCAL_MACHINE \ SOFTWARE \ Microsoft \ ConfigurationManagement \ hashKey. In order to make this setting available to a 32 bit application running on a 64 bit server, you need to store the key as  HKEY_LOCAL_MACHINE \ SOFTWARE \ Wow6432Node \ Microsoft \ ConfigurationManagement \ hashKey.

    I initially exported the keys from an existing Server 2003 machine and imported them onto Server 2008 which put them into HKEY_LOCAL_MACHINE \ SOFTWARE \ Microsoft \ ConfigurationManagement \ hashKey. This is where Windows expects you to store your 64 bit settings, so you need to move them to the corresponding location under Wow6432Node.

    This caused a lot of pain and confusion trying to understand why my application couldn’t read registry settings which were obviously there. The solution is to put your 32 bit registry settings where your 32 bit application will attempt to read them – under Wow6432Node.

    Some useful references for understanding the 32/64 bit registry issues are:

    Finally, after following all of the above – my 32 bit .net 1.1 application  was working correctly on Server 2008 R2.

    Useful links

    Technorati Tags: ,
    Posted Jul 15 2010, 05:17 PM by IvorB with 26 comment(s)
    Filed under:
  • Sync Framework 2.0 Series – Part 2 – Using Local Database Caching for an occasionally connected application.

    This is part 2 in the Sync Framework 2.0 series and deals with building an occasionally connected application using Local Database Caching. Part 1 in the series dealt with installing the Sync Framework and using the File Sync Provider.

    My original goal with this post was to see if I could use the Sync Framework to build a C# version of SQL Replication to keep two SQL Server databases in sync. However – I found a limited amount of information available in the public domain, so I have decided to take an initial step into SQL Synchronisation by building an “occasionally connected application” using Local Database Caching. The basic idea is to build something like the following:

    Users of the application can work in local mode where all their changes are persisted to a local database, and at intervals they can sync their changes back to the main corporate database. The best initial source of information I found for this type of application was on MDSN called “Occasionally Connected Applications” – an excellent series of articles that discuss various aspects of this topic. The manner you approach this type of application depends upon what technologies you are going to use to build it – for my example – I’m going to use SQL Server 2008 R2, Visual Studio 2010 and of course – Sync Framework 2.0.

    When it comes to tracking database changes, there are 2 general approaches that can be taken.

    • Add columns and triggers to the tables being monitored.
    • Use SQL Server Change Tracking

    In general – I find Database Administrators quite reluctant to add columns to tables for this kind of work. Generally – the application is in production when someone decides that they would like to be able to add a disconnected approach, and it is often considered to risky to add these additional tracking columns and triggers. Other people don’t like mixing the data we need to store for an entity with metadata we use to embellish an entity. For this reason – I’m going to focus on using SQL Server Change Tracking and further details on change tracking can be found on MSDN.

    First step is to choose the data that your application needs to work with. I wanted an example that was as simple as possible so I chose the pubs database. pubs isn’t installed by default on SQL Server 2008, so I had to download the database from Microsoft. After attaching to SQL Server 2008 – I came across my first stumbling block – using the Local Database Caching wizard (more on this later) didn’t detect it as a database which I could use SQL Server Change Tracking on as “Use SQL Server change tracking” was disabled.

    I struggled to find useful information on the internet about this using my favourite search engine (perhaps I was using poor search criteria?), but in the end I made a lucky guess – perhaps it didn’t like my database as it wasn’t really a SQL Server 2008 database? It was really a SQL Server 2000 database attached to SQL Server 2008. I quickly created a new database and used the Data Import Wizard to copy the “authors” table from pubs to my new database and while this was a step forwards as “Use SQL Server change tracking was now enabled”, my next stumbling block was that it wouldn’t let me add a new Cached Table - the Add button was disabled!

    More lost time using my favourite search engine and another lucky guess solved my problem – perhaps it was to do with the way my authors table was created. The Data Import Wizard created my table as well as copying the contents, and it decided not to create a primary key. Sync Framework likes primary keys so a quick “Add Primary Key” operation later – and the Local Database Caching wizard was a lot happier.

    To recap – when you are choosing your data source which you wish to use for an occasionally connection application and you want to use SQL Server Change Tracking – make sure its SQL Server 2008 and you have a primary key on any table that you want to synchronise.

    Now we have our data that we want to synchronise – let’s start building our application. I’m going to use a simple WinForms Desktop application. Tweak the default form so we have a tab control with 2 tabs – one to display the contents on the server and one to display the contents on our client and some buttons to perform useful operations. I’ve built the following:

    Right click the project and choose “Add New Item” and on the Data section – choose “Local Database Caching”. This will bring up the “Configure Data Synchronisation” wizard where you specify the data that you wish to use. Specify the Server connection for the source of the data and the Client connection should default to a new Compact database. Choose the table which you wish to cache by clicking the Add button. The dialog should look something like the following:

    Next step in the wizard is to choose the database model, and I went with the Dataset approach. This is good for getting a prototype up and running, but if you are building something for a production environment – you might want to consider using an Entity Data Model.

    You end up with a typed dataset for the local version of the authors table. Visual Studio will have added references to Microsoft.Synchronisation, Microsoft.Synchronisation.Data, Microsoft.Synchronisation.Server and Microsoft.Synchronisation.ServerCe but it managed to completely screw up the references on my machine. I’m working on a 64 bit machine and it created a strange hybrid of 32 and 64 bit references. I manually removed and re-added all 64 bit references on my machine and then changed the target platform to be “Any CPU” (It had defaulted to x86).

    Next step is populate the client DataGridView and this requires minimal coding. I have 2 form level variables as follows:

       1: #region Variables
       3: private AuthorsDataSet authorsClientDataSet = new AuthorsDataSet();
       4: private AuthorsDataSetTableAdapters.AuthorsTableAdapter authorsClientAdapter = 
       5:     new AuthorsDataSetTableAdapters.AuthorsTableAdapter();
       7: #endregion

    In the event handler for the click event for the populate button on the client tab – I have the following code:

       1: /// <summary>
       2: /// Populate the client data grid view control
       3: /// </summary>
       4: /// <param name="sender"></param>
       5: /// <param name="e"></param>
       6: private void buttonClientAuthorsPopulate_Click(object sender, EventArgs e)
       7: {
       8:     try
       9:     {
      10:         authorsClientAdapter.Fill(authorsClientDataSet.Authors);
      12:         this.dataGridViewClientAuthors.DataSource = authorsClientDataSet.Authors;
      13:     }
      14:     catch (Exception Ex)
      15:     {
      16:         MessageBox.Show("Failed to populate the client - perhaps the client compact " +
      17:             "database hasn't been created yet!" + Environment.NewLine + Ex.Message);
      18:     }
      19: }

    To make our prototype easy to test – it’s useful to be able to change and save the data using the application (rather than testing by changing the data directly in the database), so I implemented the save functionality for the client as follows:

       1: /// <summary>
       2: /// Save any changes on the client
       3: /// </summary>
       4: /// <param name="sender"></param>
       5: /// <param name="e"></param>
       6: private void buttonClientSave_Click(object sender, EventArgs e)
       7: {
       8:     try
       9:     {
      10:         this.Validate();
      11:         this.dataGridViewClientAuthors.EndEdit();
      12:         this.authorsClientAdapter.Update(this.authorsClientDataSet);
      14:         PopulateClient();
      15:         MessageBox.Show("Saved");
      16:     }
      17:     catch(Exception ex)
      18:     {
      19:         MessageBox.Show("Error " + ex.Message);
      20:     }
      21: }

    In order to make the server tab work, I decided to manually add a typed dataset for the server version of the authors table. There were a number of ways I could have made the server tab work, but using a typed dataset meant that the code looked very similar to the client version.

    You should now have working versions of the server and client tabs which allow you to view, change and save values. Next step is to implement the Synchronisation functionality – we want the user to be able to perform synchronisations between their local database and the central server database. Initial implementation for performing the synchronisation is very straightforward:

       1: /// <summary>
       2: /// Perform a database change sync between our client and the server.
       3: /// </summary>
       4: /// <param name="sender"></param>
       5: /// <param name="e"></param>
       6: private void buttonSync_Click(object sender, EventArgs e)
       7: {
       8:     AuthorCacheSyncAgent syncAgent = new AuthorCacheSyncAgent();
       9:     Microsoft.Synchronization.Data.SyncStatistics syncStats = syncAgent.Synchronize();
      11:     PopulateServer();
      12:     PopulateClient();
      14:     StringBuilder message = new StringBuilder();
      15:     message.Append("Changes downloaded: ");
      16:     message.Append(syncStats.TotalChangesDownloaded.ToString());
      17:     message.AppendLine();
      18:     message.Append("Changes uploaded: ");
      19:     message.Append(syncStats.TotalChangesUploaded.ToString());
      21:     MessageBox.Show(message.ToString());
      22: }

    When I tried to run my application for the first time – it failed with the following error: “The specified change tracking operation is not supported. To carry out this operation on the table, disable the change tracking on the table, and enable the change tracking.” More frantic searching but at least I found other people having the same problem. The easiest way to get rid of this exception is to set the “Copy to Output Directory” for your local compact database to “Do not copy”.

    You should now have a working “occasionally connected application” which can synchronise changes from the server to your client. However – what if you want to be able to synchronise both ways? This is actually easy to achieve – you just need to implement the OnInitialized() method in the cache file that the wizard generated.

       1: /// <summary>
       2: /// Code generated Sync Agent which we can extend
       3: /// </summary>
       4: public partial class AuthorCacheSyncAgent 
       5: {
       6:     /// <summary>
       7:     /// Initialise the sync agent to perform a bidirectional sync.
       8:     /// </summary>
       9:     partial void OnInitialized()
      10:     {
      11:         Authors.SyncDirection = SyncDirection.Bidirectional;
      12:     }
      13: }

    This allows you to perform a 2 way sync. However – what happens if you change a record on both the server and the client and perform a sync. The behaviour you should find is that the server always wins and the client change is thrown away. What happens if this isn’t the behaviour you require? You can add custom conflict handling by extending the client and server sync provider that the wizard generated. Delve into the generated code to find the names of the client and server providers, and create a partial class for the client provider as follows:

       1: /// <summary>
       2: /// Extends the code generated client sync provider for authors.
       3: /// </summary>
       4: public partial class AuthorCacheClientSyncProvider
       5: {
       6:     /// <summary>
       7:     /// Allows the main form to configure the event handler for the 
       8:     /// ApplyChangeFailed event
       9:     /// </summary>
      10:     public void AddHandlers()
      11:     {
      12:         this.ApplyChangeFailed +=
      13:             new System.EventHandler<ApplyChangeFailedEventArgs>
      14:             (AuthorCacheClientSyncProvider_ApplyChangeFailed);
      15:     }
      17:     /// <summary>
      18:     /// Allow the client to win a conflict by choosing to continue.
      19:     /// </summary>
      20:     /// <param name="sender"></param>
      21:     /// <param name="e"></param>
      22:     private void AuthorCacheClientSyncProvider_ApplyChangeFailed(object sender,
      23:         Microsoft.Synchronization.Data.ApplyChangeFailedEventArgs e)
      24:     {
      26:         if (e.Conflict.ConflictType ==
      27:             Microsoft.Synchronization.Data.ConflictType.ClientUpdateServerUpdate)
      28:         {
      29:             e.Action = Microsoft.Synchronization.Data.ApplyAction.Continue;
      30:         }
      31:     }
      32: }

    Similarly – create a partial class for the server provider as follows:

       1: /// <summary>
       2: /// Extend the server sync provider for authors to allow a client to win a conflict.
       3: /// </summary>
       4: public partial class AuthorCacheServerSyncProvider
       5: {
       6:     /// <summary>
       7:     /// Hooks up the ApplyChangeFailed event handler.
       8:     /// </summary>
       9:     partial void OnInitialized()
      10:     {
      11:         this.ApplyChangeFailed +=
      12:             new System.EventHandler<ApplyChangeFailedEventArgs>
      13:             (AuthorCacheServerSyncProvider_ApplyChangeFailed);
      14:     }
      16:     /// <summary>
      17:     /// When a conflict occurs, force write the client change.
      18:     /// </summary>
      19:     /// <param name="sender"></param>
      20:     /// <param name="e"></param>
      21:     private void AuthorCacheServerSyncProvider_ApplyChangeFailed(object sender,
      22:         Microsoft.Synchronization.Data.ApplyChangeFailedEventArgs e)
      23:     {
      24:         if (e.Conflict.ConflictType ==
      25:             Microsoft.Synchronization.Data.ConflictType.ClientUpdateServerUpdate)
      26:         {
      27:             string message = "A client/server conflict was detected at the server.";
      28:             MessageBox.Show(message);
      29:             e.Action = Microsoft.Synchronization.Data.ApplyAction.RetryWithForceWrite;
      30:         }
      31:     }
      32: }

    Finally you need to tweak the implementation to perform the synchronisation as follows:

       1: /// <summary>
       2: /// Perform a database change sync between our client and the server.
       3: /// </summary>
       4: /// <param name="sender"></param>
       5: /// <param name="e"></param>
       6: private void buttonSync_Click(object sender, EventArgs e)
       7: {
       8:     AuthorCacheSyncAgent syncAgent = new AuthorCacheSyncAgent();
       9:     AuthorCacheClientSyncProvider clientSyncProvider = 
      10:         (AuthorCacheClientSyncProvider)syncAgent.LocalProvider;
      11:     clientSyncProvider.AddHandlers();
      13:     Microsoft.Synchronization.Data.SyncStatistics syncStats = syncAgent.Synchronize();
      15:     PopulateServer();
      16:     PopulateClient();
      18:     StringBuilder message = new StringBuilder();
      19:     message.Append("Changes downloaded: ");
      20:     message.Append(syncStats.TotalChangesDownloaded.ToString());
      21:     message.AppendLine();
      22:     message.Append("Changes uploaded: ");
      23:     message.Append(syncStats.TotalChangesUploaded.ToString());
      25:     MessageBox.Show(message.ToString());
      26: }

    The latest implementation now means that the client always wins and the server change is throw away.. In reality you might want to prompt the user or log the details and allow manual intervention, but at least we can control how we manage the conflict.

    This prototype application can be downloaded from here. You will need to create a SQL Server 2008 database with an Authors table containing the standard authors information. I used a SQL ConnectionString for a user called sqlsync but you can edit these in the app.config.

    Hopefully you will agree with me that using the Local Database Cache means that we don’t have to write much code in order to produce a working occasionally connected application.  However – the main problems are limited example in the public domain and some puzzling times when things don’t work quite as expected!

    Technorati Tags:
  • Sync Framework 2.0 Series – Part 1 – Using the File Sync Provider

    This is the beginning of what will hopefully become a series of posts about Microsoft Sync Framework 2.0.

    Microsoft Sync Framework is “A comprehensive synchronization platform that enables collaboration and offline access for applications, services, and devices with support for any data type, any data store, any transfer protocol, and any network topology”. I wanted to use the sync framework recently but struggled to find really good content using my favourite search engine. I decided to work through some of the samples in the SDK and try and explore around some of the issues, and hopefully my experiences will help jump-start others who wish to use the platform.

    The best place to start reading is on msdn at the Microsoft Sync Framework Developer Center

    One of the first things you will want to do is download the Sync Framework 2.0 SDK and this can be found at: Sync Framework 2.0 SDK.

    I’m using Visual Studio 2010 to develop a Windows Forms application so the first thing I needed to do was add a reference to Microsoft.Synchronisation and Microsoft.Synchronisation.Files. However – a gotcha (at least on my machine) are that the available references on the .Net tab are for Sync Framework 1.0. Its worth checking the Path column on the Add References dialog to make sure you are adding a reference to Sync Framework 2.0. In the end – I did a Browse to the SDK folder and added my reference that way. Some constructors etc have changed subtly from version 1.0, so you need to use the right sample with the right version of the SDK to make things a bit easier.

    Another gotcha that I came across is that my Visual Studio 2010 defaulted my Windows Forms Application to a platform target of x86 despite the fact that I was using a 64 bit machine. This became an issue as I had installed the 64 bit version of the Sync Framework SDK, so I started getting run time exceptions like the following: "Retrieving the COM class factory for component with CLSID {C201C012-C929-4D72-B9C5-341D48630630} failed due to the following error: 80040154 Class not registered (Exception from HRESULT: 0x80040154 (REGDB_E_CLASSNOTREG)).". After a lot of frustration – I found by changing the platform target to “Any CPU”, my run time COM error went away.

    I think the issue is created by the managed implementation actually being a wrapper around the real non managed implementation, so when I installed the 64 bit version of the SDK – I got the 64 bit registry entries. When Visual Studio built the 32 bit version of the application – it couldn’t find the 32 bit registry entries and gave me a run time error.

    I wanted to build an example that looked and worked a bit like SyncToy. If you are unfamiliar with SyncToy – its “a free application that synchronizes files and folders between locations. Typical uses include sharing files, such as photos, with other computers and creating backup copies of files and folders.” and is actually built with Sync Framework 2.0.

    So some might ask the question – why build something when a solution already exists and my answer is that its allows me to extend the solution beyond SyncToy. e.g. It would be relatively easy to extend my solution to use a Windows service and FileSystemWatchers to detect changes to the file system and automatically initiate a synchronisation. SyncToy simply initiates a synchronisation on demand.

    My example prompts the user for a source and target folder, and the user can press a button to synchronise the files between the 2 folders. To get a simple example working, you need to create a FileSyncProvider for the source folder, a FileSyncProvider for your target folder and a SyncOrchestrator to perform the synchronisation operation. I’ve put my code into a class, and my button click event handler calls it.

       1: /// <summary>
       2: /// Perform a simple synchronisation
       3: /// </summary>
       4: public class SimpleSyncHelper
       5: {
       6:     /// <summary>
       7:     /// Perform the synchronisation
       8:     /// </summary>
       9:     /// <param name="sourceFolder"></param>
      10:     /// <param name="targetFolder"></param>
      11:     public void PerformSynchronisation(string sourceFolder, 
      12:         string targetFolder)
      13:     {
      14:         FileSyncProvider sourceProvider = new FileSyncProvider(sourceFolder);
      15:         FileSyncProvider targetProvider = new FileSyncProvider(targetFolder);
      17:         SyncOrchestrator agent = new SyncOrchestrator();
      18:         agent.LocalProvider = sourceProvider;
      19:         agent.RemoteProvider = targetProvider;
      21:         // Perform 2 way sync
      22:         agent.Direction = SyncDirectionOrder.UploadAndDownload; 
      23:         agent.Synchronize();
      24:     }
      25: }

    This works really well but when I looked at the file synchronisation example from the SDK, it was coded differently and the piece that attracted my attention was: “Explicitly detect changes on both replicas upfront, to avoid two change detection passes for the two-way sync”. It turns out that the implementation of SyncOrchestrator-Synchronize() actually splits the operation into 2 – 1 to perform the upload and 1 to perform the download. When it performs the upload part – it detects changes on both the source and the target, and when it performs the download part – it detects changes on both the source and target. This effectively means that 4 “detect change” operations are performed and explains why the SDK sample was keen to avoid the two change detection phases.

    To prove to yourself that the code performs as above, hook up a DetectingChanges event handler for both the sourceProvider and the targetProvider and add a MessageBox.Show() to the handler and you should observer 4 message boxes.

    To get around the two change detection passes, you should perform an explicit detection on both the source and the target.

       1: /// <summary>
       2: /// Perform a DetectChange operation on a FileSyncProvider.
       3: /// </summary>
       4: /// <param name="replicaRootPath"></param>
       5: /// <param name="filter"></param>
       6: /// <param name="options"></param>
       7: private void DetectChangesOnFileSystemReplica(string replicaRootPath, 
       8:     FileSyncScopeFilter filter, FileSyncOptions options)
       9: {
      10:     using (FileSyncProvider provider = new FileSyncProvider(replicaRootPath,
      11:         filter, options))
      12:     {
      13:         provider.DetectChanges();
      14:     }
      15: }

    The Framework captures the change information which can then be used later in the synchronisation process. You can then change your code that instead of performing a SyncDirectionOrder of UploadAndDownload – you perform 2 SyncDirectionOrder with a value of Upload – once between the source and the target and once between the target and the source. Make sure you use the FileSyncOptions.ExplicitDetectChanges to prevent unnecessary change detection passes.

    As well as the DetectingChanges event, there are a number of other EventHandlers that you can hook up on a FileSyncProvider to get useful information:

    • ApplyingChange – occurs when a change is about to be applied.
    • AppliedChange – occurs when a change has been applied
    • SkippedChange – occurs when a change is skipped.

    The return value from a SyncOrchestration – Synchronise() operation is of type SyncOperationStatistics and it contains useful information about the changes applied as part of the synchronisation. This could be used to display a report for the user after the synchronisation operation. It contains properties like:

    • UploadChangesApplied – total number of changes that were successfully applied during the upload session.
    • DownloadChangesApplied – total number of changes that were successfully applied during the download session.
    • SyncStartTime – when the synchronisation started.
    • SyncEndTime – when the synchronisation ended.

    So far – we have seen how we can listen for events occurring as part of the synchronisation, and get details for a report at the end of the synchronisation, but what if we want to display a progress bar to indicate to the user how long is left in the synchronisation process? The Sync Framework doesn’t provide this kind of functionality by default as it would incur a reasonable overhead which would be wasted for those who do not require it. However – the FileSyncProvider does provide a preview mode! When this is set to true – it performs all the work of calculating what changes need to be made without actually performing the changes. This allows us to perform the operation initially in preview mode to calculate what changes need to be applied (which we can access by looking at the SyncOperationStatistics), we can capture the events from the operation and display accurate progress information to the user.

    I’ve put all of the above information into a sample application that looks like the following:

    The source code for this sample application can be downloaded from here.

    Hopefully future posts in this series will demonstrate the use of other providers in Microsoft Sync Framework 2.0.

    Technorati Tags:
Powered by Community Server (Commercial Edition), by Telligent Systems