Share via


Tutorial: Creating and Deploying a WCF Sync Service to Azure

The walkthroughs in this tutorial provide steps to create a sync service by using Azure Tools for Microsoft Visual Studio (see the Windows Azure MSDN site) and deploy the service to Windows Azure. This section contains the following sub-sections.  

Section

Description

Walkthrough: Creating a Sample SQL Database

 

This walkthrough provides steps to create a sample database and to use the SyncSvcUtil utility to create sync related artifacts in the database.

Walkthrough: Creating a Sync Service in Windows Azure

This walkthrough provides steps to create a simple sync service that will be hosted in Windows Azure.

Walkthrough: Creating a Silverlight Offline Client Application

This walkthrough provides steps to develop an offline-capable Silverlight application that consumes a sync service.


Walkthrough: Creating a Sample SQL Database

In this walkthrough you will create a SQL Database that is used by a sample sync service and use the SyncSvcUtil utility to create sync related artifacts in the database.

Task 1: Creating a sample database

In this task you will create a new database that will be used by the sync service.

  1. Create SQL Database named listdb.

  2. Execute the following SQL script against the listdb database. You may want to use the Project Houston tool on http://www.sqlazurelabs.com/ tool to do this.  

       Caution

    Don’t use instlistdb.sql file C:\Program Files\Microsoft SDKs\Microsoft Sync Framework\4.0\Samples\config as that SQL file is for the first tutorial Tutorial: Creating and Consuming a Sync Service, which covers a non-Azure scenario.

    /****** Object:  Table [dbo].[User]    Script Date: 05/25/2010 13:23:55 ******/

    SET ANSI_NULLS ON

    GO

    SET QUOTED_IDENTIFIER ON

    GO

    CREATE TABLE [dbo].[User](

    [ID] [uniqueidentifier] NOT NULL,

    [Name] [nvarchar](50) NOT NULL,

     ``CONSTRAINT [PK_User] PRIMARY KEY CLUSTERED 

    (

    [ID] ASC

    )

    )

    GO

    /****** Object:  Table [dbo].[Tag]    Script Date: 05/25/2010 13:23:55 ******/

    SET ANSI_NULLS ON

    GO

    SET QUOTED_IDENTIFIER ON

    GO

    CREATE TABLE [dbo].[Tag](

    [ID] [int] IDENTITY(1,1) NOT NULL,

    [Name] [nvarchar](100) NOT NULL,

     ``CONSTRAINT [PK_Tag] PRIMARY KEY CLUSTERED 

    (

    [ID] ASC

    )

    )

    GO

    /****** Object:  Table [dbo].[Status]    Script Date: 05/25/2010 13:23:55 ******/

    SET ANSI_NULLS ON

    GO

    SET QUOTED_IDENTIFIER ON

    GO

    CREATE TABLE [dbo].[Status](

    [ID] [int] IDENTITY(1,1) NOT NULL,

    [Name] [nvarchar](50) NOT NULL,

     ``CONSTRAINT [PK_Status] PRIMARY KEY CLUSTERED 

    (

    [ID] ASC

    )

    )

    GO

    /****** Object:  Table [dbo].[Priority]    Script Date: 05/25/2010 13:23:55 ******/

    SET ANSI_NULLS ON

    GO

    SET QUOTED_IDENTIFIER ON

    GO

    CREATE TABLE [dbo].[Priority](

    [ID] [int] IDENTITY(1,1) NOT NULL,

    [Name] [nvarchar](50) NOT NULL,

     ``CONSTRAINT [PK_Priority] PRIMARY KEY CLUSTERED 

    (

    [ID] ASC

    )

    )

    GO

    /****** Object:  Table [dbo].[List]    Script Date: 05/25/2010 13:23:55 ******/

    SET ANSI_NULLS ON

    GO

    SET QUOTED_IDENTIFIER ON

    GO

    CREATE TABLE [dbo].[List](

    [ID] [uniqueidentifier] NOT NULL,

    [Name] [nvarchar](100) NOT NULL,

    [Description] [nvarchar](250) NULL,

    [UserID] [uniqueidentifier] NOT NULL,

    [CreatedDate] [datetime] NOT NULL,

     ``CONSTRAINT [PK_List] PRIMARY KEY CLUSTERED 

    (

    [ID] ASC

    )

    )

    GO

    /****** Object:  Table [dbo].[Item]    Script Date: 05/25/2010 13:23:55 ******/

    SET ANSI_NULLS ON

    GO

    SET QUOTED_IDENTIFIER ON

    GO

    CREATE TABLE [dbo].[Item](

    [ID] [uniqueidentifier] NOT NULL,

    [ListID] [uniqueidentifier] NOT NULL,

    [UserID] [uniqueidentifier] NOT NULL,

    [Name] [nvarchar](50) NOT NULL,

    [Description] [nvarchar](250) NULL,

    [Priority] [int] NULL,

    [Status] [int] NULL,

    [StartDate] [datetime] NULL,

    [EndDate] [datetime] NULL,

     ``CONSTRAINT [PK_Item] PRIMARY KEY CLUSTERED 

    (

    [ID] ASC

    )

    )

    GO

    /****** Object:  Table [dbo].[TagItemMapping]    Script Date: 05/25/2010 13:23:55 ******/

    SET ANSI_NULLS ON

    GO

    SET QUOTED_IDENTIFIER ON

    GO

    CREATE TABLE [dbo].[TagItemMapping](

    [TagID] [int] NOT NULL,

    [ItemID] [uniqueidentifier] NOT NULL,

    [UserID] [uniqueidentifier] NOT NULL,

     ``CONSTRAINT [PK_TagItemMapping] PRIMARY KEY CLUSTERED 

    (

    [TagID] ASC,

    [ItemID] ASC,

    [UserID] ASC

    )

    )

    GO

      

    /****** Object:  Default [DF_List_ID]    Script Date: 05/25/2010 13:23:55 ******/

    ALTER TABLE [dbo].[List] ADD  CONSTRAINT [DF_List_ID]  DEFAULT (newid()) FOR [ID]

    GO

    /****** Object:  Default [DF_List_CreatedDate]    Script Date: 05/25/2010 13:23:55 ******/

    ALTER TABLE [dbo].[List] ADD  CONSTRAINT [DF_List_CreatedDate]  DEFAULT (getdate()) FOR [CreatedDate]

    GO

    /****** Object:  Default [DF_User_ID]    Script Date: 05/25/2010 13:23:55 ******/

    ALTER TABLE [dbo].[User] ADD  CONSTRAINT [DF_User_ID]  DEFAULT (newid()) FOR [ID]

    GO

    /****** Object:  ForeignKey [FK_Item_List]    Script Date: 05/25/2010 13:23:55 ******/

    ALTER TABLE [dbo].[Item]  WITH CHECK ADD  CONSTRAINT [FK_Item_List] FOREIGN KEY([ListID])

    REFERENCES [dbo].[List] ([ID])

    GO

    ALTER TABLE [dbo].[Item] CHECK CONSTRAINT [FK_Item_List]

    GO

    /****** Object:  ForeignKey [FK_Item_Priority]    Script Date: 05/25/2010 13:23:55 ******/

    ALTER TABLE [dbo].[Item]  WITH CHECK ADD  CONSTRAINT [FK_Item_Priority] FOREIGN KEY([Priority])

    REFERENCES [dbo].[Priority] ([ID])

    GO

    ALTER TABLE [dbo].[Item] CHECK CONSTRAINT [FK_Item_Priority]

    GO

    /****** Object:  ForeignKey [FK_Item_Status]    Script Date: 05/25/2010 13:23:55 ******/

    ALTER TABLE [dbo].[Item]  WITH CHECK ADD  CONSTRAINT [FK_Item_Status] FOREIGN KEY([Status])

    REFERENCES [dbo].[Status] ([ID])

    GO

    ALTER TABLE [dbo].[Item] CHECK CONSTRAINT [FK_Item_Status]

    GO

    /****** Object:  ForeignKey [FK_Item_User]    Script Date: 05/25/2010 13:23:55 ******/

    ALTER TABLE [dbo].[Item]  WITH CHECK ADD  CONSTRAINT [FK_Item_User] FOREIGN KEY([UserID])

    REFERENCES [dbo].[User] ([ID])

    GO

    ALTER TABLE [dbo].[Item] CHECK CONSTRAINT [FK_Item_User]

    GO

    /****** Object:  ForeignKey [FK_List_User]    Script Date: 05/25/2010 13:23:55 ******/

    ALTER TABLE [dbo].[List]  WITH CHECK ADD  CONSTRAINT [FK_List_User] FOREIGN KEY([UserID])

    REFERENCES [dbo].[User] ([ID])

    GO

    ALTER TABLE [dbo].[List] CHECK CONSTRAINT [FK_List_User]

    GO

    /****** Object:  ForeignKey [FK_TagItemMapping_Item]    Script Date: 05/25/2010 13:23:55 ******/

    ALTER TABLE [dbo].[TagItemMapping]  WITH CHECK ADD  CONSTRAINT [FK_TagItemMapping_Item] FOREIGN KEY([ItemID])

    REFERENCES [dbo].[Item] ([ID])

    GO

    ALTER TABLE [dbo].[TagItemMapping] CHECK CONSTRAINT [FK_TagItemMapping_Item]

    GO

    /****** Object:  ForeignKey [FK_TagItemMapping_Tag]    Script Date: 05/25/2010 13:23:55 ******/

    ALTER TABLE [dbo].[TagItemMapping]  WITH CHECK ADD  CONSTRAINT [FK_TagItemMapping_Tag] FOREIGN KEY([TagID])

    REFERENCES [dbo].[Tag] ([ID])

    GO

    ALTER TABLE [dbo].[TagItemMapping] CHECK CONSTRAINT [FK_TagItemMapping_Tag]

    GO

    /****** Object:  ForeignKey [FK_TagItemMapping_User]    Script Date: 05/25/2010 13:23:55 ******/

    ALTER TABLE [dbo].[TagItemMapping]  WITH CHECK ADD  CONSTRAINT [FK_TagItemMapping_User] FOREIGN KEY([UserID])

    REFERENCES [dbo].[User] ([ID])

    GO

    ALTER TABLE [dbo].[TagItemMapping] CHECK CONSTRAINT [FK_TagItemMapping_User]

    GO

    -- Populate data

    INSERT INTO [Priority] ([Name]) VALUES ('Low')

    INSERT INTO [Priority] ([Name]) VALUES ('Medium')

    INSERT INTO [Priority] ([Name]) VALUES ('High')

    GO

    INSERT INTO [Status] ([Name]) VALUES ('Not Started')

    INSERT INTO [Status] ([Name]) VALUES ('Planning')

    INSERT INTO [Status] ([Name]) VALUES ('In Progress')

    INSERT INTO [Status] ([Name]) VALUES ('Completed')

    INSERT INTO [Status] ([Name]) VALUES ('Abandoned')

    GO

    INSERT INTO dbo.Tag (Name) VALUES ('ToDo')

    INSERT INTO dbo.Tag (Name) VALUES ('Shopping')

    INSERT INTO dbo.Tag (Name) VALUES ('Family')

    INSERT INTO dbo.Tag (Name) VALUES ('Work')

    INSERT INTO dbo.Tag (Name) VALUES ('Produce')

    INSERT INTO dbo.Tag (Name) VALUES ('Groceries')

    INSERT INTO dbo.Tag (Name) VALUES ('Clothing')

    INSERT INTO dbo.Tag (Name) VALUES ('Entertainment')

    INSERT INTO dbo.Tag (Name) VALUES ('Travel')

    INSERT INTO dbo.Tag (Name) VALUES ('Vacation')

    INSERT INTO dbo.Tag (Name) VALUES ('Tickets')

    INSERT INTO dbo.Tag (Name) VALUES ('Restaurant')

    INSERT INTO dbo.Tag (Name) VALUES ('Friends')

    INSERT INTO dbo.Tag (Name) VALUES ('Homework')

    INSERT INTO dbo.Tag (Name) VALUES ('Bills')

    INSERT INTO dbo.Tag (Name) VALUES ('Mortgage')

    Here is the database diagram for the listdb database:

     

Task 2: Provisioning the database

In this task you will provision the listdb database using the SyncSvcUtil.exe tool that is included in the zip package.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  1. Make a copy of listdbconfig.xml file in the C:\Program Files\Microsoft SDKs\Microsoft Sync Framework\4.0\samples folder and name it as listdbazureconfig.xml.

  2. Open listdbazureconfig.xml file in an editor, update the targetDatabse element to point to the SQL Database, and save the XML file. Here is an example of the update.

    
    
          <    Databases    >   
                        <      TargetDatabase Name="listdb" DbServer="azure server name" DbName="listdb" UserName="user with access to Azure DB" Password="password for the user" UseIntegratedAuth="false" />   
          </    Databases    >  
    

     

       Note

    After you logon to SQL Database, you should see the list of databases available on the server. Select listdb from list and then click Connection Strings button to see the correct format for connection string. Confirm that the server name and user name in configuration file are same as the ones displayed by the dialog box.

  3.  Open a Command Prompt window and switch to C:\Program Files\Microsoft SDKs\Microsoft Sync Framework\4.0\Samples\config folder. In the Command Prompt window, type the following command and then press ENTER.

    ..\..\bin\SyncSvcUtil /mode:provision /scopeconfig:listdbazureconfig.xml
    

    The SyncSvcUtil tool will provision the listdb SQL Database with all sync related artifacts (tracking tables, etc…).

    You will see the output similar to the following:

    Reading specified config file... 
    Generating DbSyncScopeDescription for scope DefaultScope... 
    Provisioning Database ListSample for template scope DefaultScope... 
    SyncSvcUtil completed with no errors.
    

       Note

    To provision the database using SyncSvcUtilHelper.exe, a UI tool built on top of SyncSvcUtil.exe, see Server Scope Provisioning or Deprovisioning. Using this UI tool, you can also generate a new or edit an existing sync configuration XML file. See Creating or Editing a Sync Configuration File for more details.

  4. Confirm that the tracking tables are created in the listdb SQL Database. 

   Important

See http://msdn.microsoft.com/en-us/library/ee336282.aspx if you experience any connectivity issues.


Walkthrough: Creating a Sync Service in Windows Azure

In this walkthrough you will create a sync service that can be hosted in Windows Azure.

Prerequisites

You must have the following products/files installed on your computer to practice the walkthrough.

  1. Visual Studio 2008 or Visual Studio 2010 with C# language components

  2. Download and install the Windows Azure Tools for Microsoft Visual Studio from http://www.microsoft.com/windowsazure/windowsazuresdk/.

  3. If you are using x86 Windows computer, install only x86 version (SyncSDK-v2.1-x86-ENU.msi) of Sync Framework 2.1 SDK.

  4. The following two x86 redistribution packages of Sync Framework 2.1 from Sync Framework 2.1 Redistributable Packages.

    1. Synchronization-v2.1-x86-ENU.msi

    2. DatabaseProviders-v3.1-x86-ENU.msi

   Note

In this tutorial, you will be hosting a Silverlight client application in the Visual Studio Developer Server, which requires 32-bit components of Sync Framework 2.1 to run the client successfully. If you are actually deploying and hosting the client to IIS or Windows Azure, you just need 64-bit components.

It is not possible have full 32-bit Sync Framework 2.1 SDK installed side-by-side with the 64-bit SDK; therefore you will have to install one version of SDK (64-bit) completely and only selected components of other version (32-bit) of SDK

Task 1: Generating code for sync service

In this task you will create code files for the service by using the SyncSvcUtil.exe tool

  1. Open listdbazureconfig.xml file and confirm that the TargetDatabase attributes point to SQL Database.

    <TargetDatabase Name="listsample" DbServer="<SQL Database Server>" DbName="listdb" UserName="<User Name>" Password="" UseIntegratedAuth="false" />
    

     

       Note

    After you logon to SQL Database, you should see the list of databases available on the server. Select listdb from list and then click Connection Strings button to see the correct format for connection string. Confirm that the server name and user name in configuration file are same as the ones displayed by the dialog box.

  2.  Open a Command Prompt window with administrative rights and switch to C:\Program Files\Microsoft SDKs\Microsoft Sync Framework\4.0\Samples\config folder. In the Command Prompt window, type the following command and then press ENTER. This command creates code files for the service by using the SyncSvcUtil.exe tool.

    ..\..\bin\SyncSvcUtil /mode:codegen /target:server /scopeconfig:listdbazureconfig.xml
    

       Note

    If your computer has the x64 version of Windows, use c:\Program Files(x86) folder wherever c:\Program Files is mentioned in this tutorial to access Sync Framework 4.0 October 2010 CTP components.

    The tool creates three files: DefaultScopeEntities.cs, DefaultScopeSyncService.svc, DefaultScopeSyncService.svc.cs in the current folder (C:\Program Files\Microsoft SDKs\Microsoft Sync Framework\4.0\samples\Config). You will use these files later in Task 4 when you create a Visual Studio project. Here is the sample output from the previous command:

    Reading specified config file... 
    Generating DbSyncScopeDescription for scope DefaultScope... 
    Generating files... 
    SyncSvcUtil completed with no errors.
    

       Note

    You can also use the SyncSvcUtilHelper, a UI tool built on top of SyncSvcUtil command-line tool, to generate server side code. See Generating Code for a Server, Generic Client, or Isolated Store Client for more details.

Task 2: Creating a Visual Studio project

In this task, you will create a Visual Studio project for the sync service.

  1. Open Microsoft Visual Studio 2008 with Administrator permissions: Click Start, point to All Programs, point to Microsoft Visual Studio 2008, right-click Microsoft Visual Studio 2008, and then click Run as Administrator.

       Note

    If you are using Microsoft Visual Studio 2010, use steps similar to preceding steps to launch Visual Studio 2010.

  2. If the User Account Control dialog appears, click Continue.

  3. From the File menu, click New and then click Project.

  4. In the New Project dialog, expand Visual C# in the project types list and select Windows Azure Cloud Service.

  5. Enter the Name ListService. Click OK to create the project.

  6. In the New Cloud Service Project dialog that opens, select and double –click ASP.NET Web Role and then click OK.

  7. In the Solution Explorer, right-click the WebRole1 project, point to Add, and then click Add Existing Item.

  8. Navigate to C:\Program Files\Microsoft SDKs\Microsoft Sync Framework\4.0 \samples and select the 3 files (DefaultScopeEntities.cs, DefaultScopeSyncService.svc, DefaultScopeSyncService.svc.cs), and then click Add to add these files to the project.

  9. Examine the Solution Explorer and make sure that the files appear in the project tree.

  10. In the Solution Explorer, right-click the WebRole1 project, point to Add, and then click Add Reference.

  11. In the Add Reference dialog box, click Browse tab, navigate to C C:\Program Files\Microsoft SDKs\Microsoft Sync Framework\4.0\server\Microsoft.Synchronization.Services.dll, and then click OK.

  12. Repeat previous step to add references to Microsoft.Synchronization.dll, Microsoft.Synchronization.Data.dll, and Microsoft.Synchronization.Data.SqlServer.dll from Sync Framework 2.1 installation folder (ex: C:\Program Files (x86)\Microsoft SDKs\Microsoft Sync Framework\2.1\Runtime\x86 or C:\Program Files (x86)\Microsoft SDKs\Microsoft Sync Framework\2.1\Runtime\ADO.NET\V3.1\x86.).

  13. Multiple-select the four files you added to References, right-click, and then click Properties.

  14. In the Properties window, set the value of Aliases property to global and Copy Local property to True.

  15. Create a source file named activationcontext.cs file with the following content and add the file to WebRole1 project.

    using System; 
    using System.Collections.Generic; 
    using System.Linq; 
    using System.Text; 
    using System.Web; 
    using System.Runtime.InteropServices; 
    using System.IO; 
      
    namespace Microsoft.Samples.Synchronization 
    { 
        public class  ActivationContext 
        { 
            // Activation Context API Functions  
      
            [DllImport("Kernel32.dll", SetLastError = true)] 
            private extern  static IntPtr CreateActCtx(ref ACTCTX actctx); 
      
            // Activation context structure  
            private struct  ACTCTX 
            { 
                public int  cbSize; 
                public uint  dwFlags; 
                public string  lpSource; 
                public ushort  wProcessorArchitecture; 
                public ushort  wLangId; 
                public string  lpAssemblyDirectory; 
                public string  lpResourceName; 
                public string  lpApplicationName; 
            } 
      
            private const  int ACTCTX_FLAG_ASSEMBLY_DIRECTORY_VALID = 0x004; 
            private const  int ACTCTX_FLAG_SET_PROCESS_DEFAULT = 0x00000010; 
            private IntPtr m_hActCtx = (IntPtr)0; 
            public const  UInt32 ERROR_SXS_PROCESS_DEFAULT_ALREADY_SET = 14011; 
      
            /// <summary> 
            /// Explicitly load a manifest and create the process-default  
            /// activation context. It takes effect immediately and stays   
            /// there until the process exits.  
            /// </summary> 
            static public  void CreateActivationContext() 
            { 
                string rootFolder = AppDomain.CurrentDomain.BaseDirectory;  
                string manifestPath = Path.Combine(rootFolder, "webapp.manifest"); 
                UInt32 dwError = 0; 
      
                // Build the activation context information structure  
                ACTCTX info = new  ACTCTX(); 
                info.cbSize = Marshal.SizeOf(typeof(ACTCTX)); 
                info.dwFlags = ACTCTX_FLAG_SET_PROCESS_DEFAULT; 
                info.lpSource = manifestPath; 
                if (null != rootFolder && "" != rootFolder) 
                { 
                    info.lpAssemblyDirectory = rootFolder; 
                    info.dwFlags |= ACTCTX_FLAG_ASSEMBLY_DIRECTORY_VALID; 
                } 
      
                dwError = 0; 
      
                // Create the activation context  
                IntPtr result = CreateActCtx(ref info); 
                if (-1 == result.ToInt32()) 
                { 
                    dwError = (UInt32)Marshal.GetLastWin32Error(); 
                } 
      
                if (-1 == result.ToInt32() && ActivationContext.ERROR_SXS_PROCESS_DEFAULT_ALREADY_SET != dwError) 
                { 
                    string err = string.Format("Cannot create process-default win32 sxs context, error={0} manifest={1}", dwError, manifestPath); 
                    ApplicationException ex = new  ApplicationException(err); 
                    throw ex; 
                } 
            } 
        } 
    }
    
  16. Right-click WebRole1 project, point to Add, and then click New Folder. Type synchronization.assembly for the name of the folder. Add the following five files to the folder.

    • Microsoft.Synchronization.dll

    • Microsoft.Synchronization.Data.dll

    • Microsoft.Synchronization.Data.SqlServer.dll

    • Synchronization21.dll

    • Create a file named synchronization.assembly.manifest, add the following content, and add the file to this folder.

      <?xml version='1.0' encoding='UTF-8' standalone='yes'?> 
      <assembly xmlns='urn:schemas-microsoft-com:asm.v1' manifestVersion='1.0'> 
        
      <assemblyIdentity
           type="x64" 
           name="synchronization.assembly"
           version="1.0.0.0"
        
      /> 
        
        <file name = "synchronization21.dll"> 
          <comClass clsid="{EC413D66-6221-4ebb-AC55-4900FB321011}" threadingModel="Both"/> 
        </file> 
        
        
      </assembly>
      
  17. Multiple-select all files under synchronization.assembly folder, right-click, and then click Properties. Set the value of Build Action property to Content and Copy To Output Directory to Copy Always.

  18. Create a file named webapp.manifest, add the following content, and add the file to WebRole1 project.

    <?xml version='1.0' encoding='UTF-8' standalone='yes'?> 
    <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> 
      
      <assemblyIdentity name="webapp" version="8.0.0.0" type="x64" /> 
      
      <dependency> 
        <dependentAssembly> 
          <assemblyIdentity name="synchronization.assembly" version="1.0.0.0" type="x64" /> 
        </dependentAssembly> 
      </dependency> 
      
    </assembly>
    
  19. Set the value of Build Action property to Content and Copy To Output Directory to Copy Always for the webapp.manifest file by using Properties window.

  20. Add the following statement to the OnStart method before base.OnStart method call in the WebRole.cs file.

    Microsoft.Samples.Synchronization.ActivationContext.CreateActivationContext();
    

   Note

You will need to add clientaccesspolicy.xml file to the WebRole1 project if you want the WebRole1 to host the Silverlight application and set the Build Action property to Content and Copy to Output Directory to Copy Always using Properties window. Here is the content of a sample clientaccesspolicy.xml file.

<?xml version="1.0" encoding="utf-8"?> 
<access-policy> 
    <cross-domain-access>    
        <policy >    
            <allow-from http-request-headers="*">    
                <domain uri="*"/>    
                <domain uri="http://*"/>    
                <domain uri="file://*"/>    
            </allow-from>    
            <grant-to>    
                <resource path="/" include-subpaths="true"/>   
            </grant-to> 
        </policy>   
    </cross-domain-access>   
</access-policy>

 

Task 3: Configuring and deploying the sync service

In this task, you will configure the service and build it.

  1. In the Solution Explorer, expand the files under DefaultScopeSyncService.svc and then double-click DefaultScopeSyncService.svc.cs to open the file.

  2. Uncomment the following lines in the InitializeService method.

    config.ServerConnectionString = "connection string here"; 
    config.SetEnableScope("scope name goes here");
    
  3.  Replace connection string here with the following:

    Data Source=<SQLAzure_Server_Name>;Initial Catalog=listDb;User ID=username;Password=;Trusted_Connection=False;
    
  4. Replace “scope name goes here” with DefaultScope.

  5. Add the following lines of code to the InitializeService method.

    // configure filter parameters used by the service 
    config.AddFilterParameterConfiguration("userid", "User",  "@ID", typeof(System.Guid)); 
    config.AddFilterParameterConfiguration("userid", "Item",  "@UserID", typeof(System.Guid)); 
    config.AddFilterParameterConfiguration("userid", "List",  "@UserID", typeof(System.Guid)); 
    config.AddFilterParameterConfiguration("userid", "TagItemMapping", "@UserID",  typeof(System.Guid)); 
      
    // enable Diagnostic Dashboard feature for the service  
    config.EnableDiagnosticPage = true; 
      
    // enable verbose errors  
    config.UseVerboseErrors = true;
    
  6. From the Build menu, click Build Solution. This will build the solution.

    If you have the Sync Framework installed on your computer, you can run this service on the Development Fabric at this point

       Note

    The table name (ex: User) and the parameter name (ex. @ID) correspond to the definitions in the listdbconfig.xml file that was used for provisioning the database.

  7. To deploy the sync service to Windows Azure:

    1. Comment the DiagnosticMonitor.Start method call in the OnStart method of WebRole class in the WebRole.cs file (or) change the DiagnosticConnectionString setting to use Azure storage: In Solution Explorer, expand ListService, expand Roles, right-click WebRole1, and then click Properties. Click button to enter Azure storage credentials.

    2. Right-click ListService, and then click Publish. You should see a folder opened in a Windows explorer window that contains two files: ListService.cspkg and ServiceConfiguration.cscfg.

    3. Create a service on Windows Azure, upload these two files, and start the service.

  8. Verify that the deployment was indeed successful.

  9. Verify that the deployment was indeed successful.

    1. Open a Web browser and navigate to http://[servicename].cloudapp.net/defaultscopeSyncService.svc/$syncscopes, and confirm that you see sync scopes available from the service.

    2. Use the following syntax to see diagnostics information for the service: http://[servicename].cloudapp.net/defaultscopeSyncService.svc/$diag.

Walkthrough: Creating a Silverlight Offline Client Application

In this walkthrough, you will develop an offline-capable Silverlight application that consumes a sync Service.

Pre-Requisites

You must have the following products/components installed on your computer.

  1. Visual Studio 2008 SP1 or Visual Studio 2010 with C# language components

  2. Silverlight 3 Tools for Visual Studio 2008 or Silverlight 4 Tools for Visual Studio 2010

Task 1: Generating Client Code

In this task, you will generate code files for a Silverlight client using the SyncSvcUtil.exe tool.

  1. Open a Command Prompt window with administrative rights, and switch to C:\Program Files\Microsoft SDKs\Microsoft Sync Framework\4.0\Samples\Config folder.

       Note

    If your computer has the x64 version of Windows, use c:\Program Files(x86) folder wherever c:\Program Files is mentioned in this tutorial to access Sync Framework 4.0 October 2010 CTP components.

  2. At the command prompt, type the following command and then press ENTER.

    ..\..\bin\SyncSvcUtil /mode:codegen /target:isclient /scopeconfig:listdbazureconfig.xml
    

    The tool will create two files: DefaultScopeEntities.cs and DefaultScopeOfflineContext.cs in C:\Program Files\Microsoft SDKs\Microsoft Sync Framework\4.0\Samples\Config folder.

       Note

    You can also use the SyncSvcUtilHelper, a UI tool built on top of SyncSvcUtil command-line tool, to generate the code that can be used to build an isolated store client. See Generating Code for a Server, Generic Client, or Isolated Store Client for more details.

These files will be used in the next task to create the Silverlight project.

Task 2: Creating a Visual Studio Silverlight Application

In this task, you will create a Visual Studio Silverlight project which will consume the sync service.

  1. In the Solution Explorer window, right-click Solution ‘ListService’, point to Add, and then click New Project.

  2. In the New Project dialog, expand Visual C# in the “Project types:” list and select the Silverlight project type.

  3. Select Silverlight Application from the templates menu.

  4. Type the name ListClient and then click OK to create the project.

  5. Click OK on the New Silverlight Application dialog box

  6. In the Solution Explorer, right-click ListClient, point to Add, and then click Add Existing Item.

  7. Navigate to C:\Program Files\Microsoft SDKs\Microsoft Sync Framework\4.0\samples\Config and select the 2 files (DefaultScopeEntities.cs, DefaultScopeOfflineContext.cs) and then click Add.

  8. Examine the Solution Explorer and make sure that that the files appear in the project tree.

  9. In the Solution Explorer, right-click ListClient project, point to Add, and then click Add Reference.

  10.  

    In the Add Reference dialog box, go to the Browse tab and navigate to C:\Program Files\Microsoft SDKs\Microsoft Sync Framework\4.0\client\Silverlight.

       Note

    To develop Silverlight client application for Windows Phone 7, use the WP7 subfolder of C:\Program Files\Microsoft SDKs\Microsoft Sync Framework\4.0\client\WP7 folder.

     

  11. Select Microsoft.Synchronization.ClientServices.dll and then click OK.

  12. Follow Step 11 to bring up the Add Reference dialog box again.

  13. Switch to the .NET tab.

  14. Select System.ComponentModel.DataAnnotations and System.Windows.Controls.Data, and then click OK.

  15. At this point, the project should compile successfully. On the Build menu, click Build Solution.

Task 3: Building the application

In this task, you will write code in the project to be able to consume the Tag data from the server (other data requires that a user id to be specified as a filter).

  1. In the Solution Explorer, expand MainPage.xaml and double-click MainPage.xaml.cs to open the file.

  2. At the beginning of the file, add the following statements.

    using DefaultScope; 
    using Microsoft.Synchronization.ClientServices.IsolatedStorage;
    
  3. Inside the MainPage class, create a new variable of type DefaultScopeOfflineContext and name it context.

    DefaultScopeOfflineContext context;
    
  4. In the constructor of the MainPage class, add a line of code to create the context above the InitializeComponent method call.

    context = new  DefaultScopeOfflineContext("list_client", 
                  new Uri(new Uri(new WebClient().BaseAddress), 
                  "../DefaultScopeSyncService.svc/"));
    

    The first parameter is the relative directory within isolated storage where the offline data should be cached.

    The second parameter specifies the address of the service. Example: http://localhost:65338/DefaultScopeSyncService.svc.

  5. As the scope on the server is filtered by user id, we must specify the parameter here as described in the following list.

    1. Add the following line of code to the constructor of MainPage class above the InitializeComponent statement.

      context.CacheController.ControllerBehavior.AddScopeParameters("userid",  
                                 new Guid("{EE9F0661-AD72-4b0b-8627-73722520A41B}").ToString());
      
    2. On the Tools menu, click Create Guid.

    3. Select the Registry Format radio button and then click Copy and then click Exit.

    4. Paste the GUID in the clipboard to replace the GUID in the above AddScopeParameters method call.

         Important

      The tag data is not filtered by user id. Therefore, the GUID we pass here does not matter. For other tables in the database, the user id must match one in the User table.

  6. Now we need to display the data. In the Solution Explorer, double-click on MainPage.xaml in the ListClient project.

  7. Add two rows to the LayoutRoot grid: Add the following lines of code in between <Grid x:Name="LayoutRoot"> and </Grid>.

    <Grid.RowDefinitions> 
        <RowDefinition /> 
       <RowDefinition Height="Auto" /> 
    </Grid.RowDefinitions>
    

    In the first row, you will place a DataGrid that displays the tags. In the second row, you will place a button to start synchronization

  8. Expand the Toolbox tab and drag a DataGrid into the XML markup. Make sure that it is after Grid.RowDefinitions closing tag, but before the Grid closing tag. Then follow these steps for the data grid:

    1. Add an xml property of x:Name to the DataGrid element and make the value TagsGrid.

    2. Set its Grid.Row property to 0.

    3. Set IsReadOnly to False because we want to be able to modify the tags.

    After this, the data grid should be specified as follows (also removing the closing tag and changing the initial tag to end in “/>”:

    <data:DataGrid x:Name="TagsGrid" Grid.Row="0" IsReadOnly="False" />
    

       Important

    If you are typing in the DataGrid specification by hand, you also have to add the namespace for the DataGrid to the top-level UserControl declaration (dragging from the Toolbox does this automatically). The namespace is as follows:

    xmlns:data="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data"
    
  9. Add a Button to the MainPage.xaml file after the DataGrid, specified as follows:

    <Button Content="Sync" Grid.Row="1" HorizontalAlignment="Center"/>
    
  10. Add an event handler for the button. In order to do so, place the cursor before the end of the tag and type Click=. At this point Visual Studio should display a dropdown. Hit the TAB button to auto-generate a new event handler. Your button specification should look as follows:

    <Button Content="Sync" Click="Button_Click" Grid.Row="1" HorizontalAlignment="Center"/>
    
  11. Now the editing of the [[XAML]] is complete and we are ready to write the code. The XAML file should look as follows:

    <UserControl xmlns:data="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data"   
        x:Class="ListClient.MainPage"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
        mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480"> 
        <Grid x:Name="LayoutRoot"> 
            <Grid.RowDefinitions> 
                <RowDefinition /> 
                <RowDefinition Height="Auto" /> 
            </Grid.RowDefinitions> 
          
            <data:DataGrid x:Name="TagsGrid" Grid.Row="0" IsReadOnly="False" /> 
            <Button Content="Sync" Click="Button_Click" Grid.Row="1" HorizontalAlignment="Center"/> 
        </Grid> 
    </UserControl>
    
  12. Return to the MainPage.xaml.cs page.

  13. In the constructor, add an event handler for the DefaultScopeOfflineContext.LoadCompleted event in the line after you assign the context variable:

    context.LoadCompleted += new  EventHandler<LoadCompletedEventArgs>(context_LoadCompleted);
    
  14. Following that line, add an event handler for the MainPage.Loaded event:

    this.Loaded += new  RoutedEventHandler(MainPage_Loaded);
    
  15. Your MainPage constructor should now look as follows:

    public MainPage() 
    { 
        context = new  DefaultScopeOfflineContext("list_client", 
                      new Uri(new Uri(new WebClient().BaseAddress), 
                      "../DefaultScopeSyncService.svc/")); 
      
        context.CacheController.ControllerBehavior.AddScopeParameters("userid",  
                    new Guid("{EE9F0661-AD72-4b0b-8627-73722520A41B}").ToString()); 
      
        context.LoadCompleted += new
            EventHandler<LoadCompletedEventArgs>(context_LoadCompleted); 
      
        this.Loaded += new  RoutedEventHandler(MainPage_Loaded); 
                  
        InitializeComponent(); 
    }
    
  16. Following the MainPage constructor, create the MainPage_Loaded method (if it wasn’t generated by Visual Studio) to handle the MainPage.Loaded event.

    In this method, load the context. This is so that we can put the data into the DataGrid. Since we are loading on startup, we don’t want it to block the UI. Therefore, we call the DefaultScopeOfflineContext.LoadAsync method. The MainPage_Loaded method should look as follows:

    void MainPage_Loaded(object sender, RoutedEventArgs e) 
    { 
        context.LoadAsync(); 
    }
    
  17. We then implement the context_LoadCompleted event, which will be responsible for getting the data from the context and assigning it to the DataGrid. This is accomplished rather simply, by getting the desired collection from the context, in our case Tags, and assigning it to the DataGrid ItemsSource.

    One important note about doing this is that the context_LoadCompleted event may be called on a thread that is not the UI thread, which will cause an exception if we try to modify a UI element. As a result, you must use the Dispatcher.BeginInvoke to make sure that the work is done on the UI thread. Because of the simplicity of the code for this, we will use an anonymous delegate to update the data. The code for the method is as follows:

    void context_LoadCompleted(object sender, LoadCompletedEventArgs e) 
    { 
        Dispatcher.BeginInvoke(delegate
        { 
            TagsGrid.ItemsSource = context.TagCollection; 
        }); 
    }
    
  18. Add the following two statements to the Button_Click method. The first statement saves any changes you made in the data grid and the second statement initiates the synchronization process.

    // saves any outstanding changes made by the application 
    context.SaveChanges(); 
    
    // refreshes the cache by uploading all modified changes
    // and then by downloading the server changes 
    context.CacheController.RefreshAsync();
    
  19. At this point, the code for the application is complete. The MainPage.xaml.cs file should look as follows:

    using System; 
    using System.Collections.Generic; 
    using System.Linq; 
    using System.Net; 
    using System.Windows; 
    using System.Windows.Controls; 
    using System.Windows.Documents; 
    using System.Windows.Input; 
    using System.Windows.Media; 
    using System.Windows.Media.Animation; 
    using System.Windows.Shapes; 
      
    using DefaultScope; 
    using Microsoft.Synchronization.ClientServices.IsolatedStorage; 
      
    namespace ListClient 
    { 
        public partial  class MainPage : UserControl 
        { 
            DefaultScopeOfflineContext context; 
      
            public MainPage() 
            { 
                // create an instance of offline context type 
                // the code for this class is generated by 
                // the SyncSvcUtil utility. 
                context = new  DefaultScopeOfflineContext("list_client", 
                                        new Uri(new Uri(new WebClient().BaseAddress), 
                                        "../DefaultScopeSyncService.svc/")); 
      
                // specify a value for the userid parameter for scope 
                context.CacheController.ControllerBehavior.AddScopeParameters("userid", 
                               new Guid("{832106DC-2532-4f7a-9E6D-050383957D38}").ToString()); 
      
                // subscribe for the context.LoadCompleted event 
                context.LoadCompleted += new  EventHandler<LoadCompletedEventArgs>(context_LoadCompleted); 
      
                // subscribe for the Page.Loaded event  
                this.Loaded += new  RoutedEventHandler(MainPage_Loaded); 
      
                // subscribe for the RefreshCompleted event 
                context.CacheController.RefreshCompleted += (sender, args) => 
                { 
                    if (args.Error != null) 
                    { 
                        Dispatcher.BeginInvoke( 
                            delegate
                            { 
                                // display any error occurred during refresh 
                                // operation in a message box 
                                MessageBox.Show(args.Error.ToString()); 
                            }); 
                    } 
                }; 
      
                InitializeComponent(); 
            } 
      
            void MainPage_Loaded(object sender, RoutedEventArgs e) 
            { 
                // when the page is loaded, invoke LoadAsync on the context   
                // object to load data from the cache into memory   
                // asynchronously. This method raises the LoadCompleted event 
                // at the end of load operation. 
      
                context.LoadAsync(); 
            } 
      
            void context_LoadCompleted(object sender, LoadCompletedEventArgs e) 
            { 
                // bind the TagCollection object of context to ItemsSource      
                // to TagsGrid      
                Dispatcher.BeginInvoke(delegate
                { 
                    TagsGrid.ItemsSource = context.TagCollection; 
                }); 
            } 
      
            private void  Button_Click(object  sender, RoutedEventArgs e) 
            { 
                // saves any outstanding changes made by the application 
                context.SaveChanges(); 
      
                // refreshes the cache by uploading all modified changes 
                // and then by downloading the server changes 
                context.CacheController.RefreshAsync(); 
            } 
        } 
    }
    
  20. From the Build menu, click Build Solution to the build the solution.

  21. Make sure that the ListService project is set as the Startup project for the solution.

    1. In the Solution Explorer, right-click Solution ‘ListService’ (2 projects), and then click Set Startup Projects.

    2. Confirm that Single startup project option is selected and the ListService is specified as the startup project. If this is not the case, select the Single startup project option and select ListService project from the list.

    3. Click OK to close the Solution ‘ListService’ Property Pages dialog box.

       Caution

    If you do not set the ListService project as the startup project, you will see the following error when you perform the next step:

    “An unhandled exception (‘Unhandled Error in Silverlight Application Uri must be http or https schema Parameter name: serviceUri at ….”

  22. In the ListService project, right-click ListClientTestPage.html, and then click Set As Start Page.

  23. Press F5 or Ctrl+F5 to run the code. You should see an Internet Explorer window that opens html page.

  24. Now, click Sync to see something similar to the following:

    If you click Sync button and no data appears, you can register an event handler to discover the error. One example is as follows (if you add these lines to the constructor):

    context.CacheController.RefreshCompleted += (sender, args) => 
        { 
            if (args.Error != null) 
            { 
                Dispatcher.BeginInvoke( 
                    delegate { 
                        MessageBox.Show(args.Error.ToString()); 
                    }); 
            } 
        };
    

Optional steps

  • Add couple of rows to the Tag table in the listdb database and then click Sync to see the new data that you added to the table in the list.

  • Delete a row you added in the earlier step and then click Sync to see that deleted row disappears from the list.

  • Update a row you added in the first step and then click Sync to see that data is also updated in the list.

  • Click on Mortgage in the list and change it to something else, and then click Sync. Confirm that the value is uploaded to the listdb SQL Server database by using SQL Server Management Studio.