The walkthroughs in this tutorial provide steps to create a sync service by using Windows 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.
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.
In this task you will create a new database that will be used by the sync service.
Create SQL Database named listdb.
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.
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
CREATE TABLE [dbo].[User](
[ID] [uniqueidentifier] NOT NULL,
[Name] [nvarchar](50) NOT NULL,
CONSTRAINT [PK_User] PRIMARY KEY CLUSTERED
(
[ID] ASC
)
/****** Object: Table [dbo].[Tag] Script Date: 05/25/2010 13:23:55 ******/
CREATE TABLE [dbo].[Tag](
[ID] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](100) NOT NULL,
CONSTRAINT [PK_Tag] PRIMARY KEY CLUSTERED
/****** Object: Table [dbo].[Status] Script Date: 05/25/2010 13:23:55 ******/
CREATE TABLE [dbo].[Status](
CONSTRAINT [PK_Status] PRIMARY KEY CLUSTERED
/****** Object: Table [dbo].[Priority] Script Date: 05/25/2010 13:23:55 ******/
CREATE TABLE [dbo].[Priority](
CONSTRAINT [PK_Priority] PRIMARY KEY CLUSTERED
/****** Object: Table [dbo].[List] Script Date: 05/25/2010 13:23:55 ******/
CREATE TABLE [dbo].[List](
[Description] [nvarchar](250) NULL,
[UserID] [uniqueidentifier] NOT NULL,
[CreatedDate] [datetime] NOT NULL,
CONSTRAINT [PK_List] PRIMARY KEY CLUSTERED
/****** Object: Table [dbo].[Item] Script Date: 05/25/2010 13:23:55 ******/
CREATE TABLE [dbo].[Item](
[ListID] [uniqueidentifier] NOT NULL,
[Priority] [int] NULL,
[Status] [int] NULL,
[StartDate] [datetime] NULL,
[EndDate] [datetime] NULL,
CONSTRAINT [PK_Item] PRIMARY KEY CLUSTERED
/****** Object: Table [dbo].[TagItemMapping] Script Date: 05/25/2010 13:23:55 ******/
CREATE TABLE [dbo].[TagItemMapping](
[TagID] [int] NOT NULL,
[ItemID] [uniqueidentifier] NOT NULL,
CONSTRAINT [PK_TagItemMapping] PRIMARY KEY CLUSTERED
[TagID] ASC,
[ItemID] ASC,
[UserID] ASC
/****** 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]
/****** 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]
/****** 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]
/****** 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])
ALTER TABLE [dbo].[Item] CHECK CONSTRAINT [FK_Item_List]
/****** 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])
ALTER TABLE [dbo].[Item] CHECK CONSTRAINT [FK_Item_Priority]
/****** 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])
ALTER TABLE [dbo].[Item] CHECK CONSTRAINT [FK_Item_Status]
/****** 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])
ALTER TABLE [dbo].[Item] CHECK CONSTRAINT [FK_Item_User]
/****** 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])
ALTER TABLE [dbo].[List] CHECK CONSTRAINT [FK_List_User]
/****** 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])
ALTER TABLE [dbo].[TagItemMapping] CHECK CONSTRAINT [FK_TagItemMapping_Item]
/****** 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])
ALTER TABLE [dbo].[TagItemMapping] CHECK CONSTRAINT [FK_TagItemMapping_Tag]
/****** 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])
ALTER TABLE [dbo].[TagItemMapping] CHECK CONSTRAINT [FK_TagItemMapping_User]
-- Populate data
INSERT INTO [Priority] ([Name]) VALUES ('Low')
INSERT INTO [Priority] ([Name]) VALUES ('Medium')
INSERT INTO [Priority] ([Name]) VALUES ('High')
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')
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:
In this task you will provision the listdb database using the SyncSvcUtil.exe tool that is included in the zip package.
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.
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
UserName
"user with access to Azure DB"
Password
"password for the user"
UseIntegratedAuth
"false"
/>
</
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.
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.
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.
Confirm that the tracking tables are created in the listdb SQL Database.
See http://msdn.microsoft.com/en-us/library/ee336282.aspx if you experience any connectivity issues.
In this walkthrough you will create a sync service that can be hosted in Windows Azure.
You must have the following products/files installed on your computer to practice the walkthrough.
Visual Studio 2008 or Visual Studio 2010 with C# language components
Download and install the Windows Azure Tools for Microsoft Visual Studio from http://www.microsoft.com/windowsazure/windowsazuresdk/.
If you are using x86 Windows computer, install only x86 version (SyncSDK-v2.1-x86-ENU.msi) of Sync Framework 2.1 SDK.
The following two x86 redistribution packages of Sync Framework 2.1 from Sync Framework 2.1 Redistributable Packages.
Synchronization-v2.1-x86-ENU.msi
DatabaseProviders-v3.1-x86-ENU.msi
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
In this task you will create code files for the service by using the SyncSvcUtil.exe tool
Open listdbazureconfig.xml file and confirm that the TargetDatabase attributes point to SQL Database.
"listsample"
DbServer="<SQL Database Server>" DbName="listdb" UserName="<
User
Name>" Password="<
>" UseIntegratedAuth="false" />
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
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:
Generating files...
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.
In this task, you will create a Visual Studio project for the sync service.
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.
If you are using Microsoft Visual Studio 2010, use steps similar to preceding steps to launch Visual Studio 2010.
If the User Account Control dialog appears, click Continue.
From the File menu, click New and then click Project.
In the New Project dialog, expand Visual C# in the project types list and select Windows Azure Cloud Service.
Enter the Name ListService. Click OK to create the project.
In the New Cloud Service Project dialog that opens, select and double –click ASP.NET Web Role and then click OK.
In the Solution Explorer, right-click the WebRole1 project, point to Add, and then click Add Existing Item.
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.
Examine the Solution Explorer and make sure that the files appear in the project tree.
In the Solution Explorer, right-click the WebRole1 project, point to Add, and then click Add Reference.
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.
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.).
Multiple-select the four files you added to References, right-click, and then click Properties.
In the Properties window, set the value of Aliases property to global and Copy Local property to True.
Create a source file named activationcontext.cs file with the following content and add the file to WebRole1 project.
using
System;
System.Collections.Generic;
System.Linq;
System.Text;
System.Web;
System.Runtime.InteropServices;
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
struct
ACTCTX
int
cbSize;
uint
dwFlags;
string
lpSource;
ushort
wProcessorArchitecture;
wLangId;
lpAssemblyDirectory;
lpResourceName;
lpApplicationName;
}
const
ACTCTX_FLAG_ASSEMBLY_DIRECTORY_VALID = 0x004;
ACTCTX_FLAG_SET_PROCESS_DEFAULT = 0x00000010;
IntPtr m_hActCtx = (IntPtr)0;
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>
void
CreateActivationContext()
rootFolder = AppDomain.CurrentDomain.BaseDirectory;
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(
info);
(-1 == result.ToInt32())
dwError = (UInt32)Marshal.GetLastWin32Error();
(-1 == result.ToInt32() && ActivationContext.ERROR_SXS_PROCESS_DEFAULT_ALREADY_SET != dwError)
err =
.Format(
"Cannot create process-default win32 sxs context, error={0} manifest={1}"
, dwError, manifestPath);
ApplicationException ex =
ApplicationException(err);
throw
ex;
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
assemblyIdentity
type
"x64"
name
"synchronization.assembly"
"1.0.0.0"
file
"synchronization21.dll"
comClass
clsid
"{EC413D66-6221-4ebb-AC55-4900FB321011}"
threadingModel
"Both"
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.
Create a file named webapp.manifest, add the following content, and add the file to WebRole1 project.
"urn:schemas-microsoft-com:asm.v1"
"1.0"
"webapp"
"8.0.0.0"
dependency
dependentAssembly
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.
Add the following statement to the OnStart method before base.OnStart method call in the WebRole.cs file.
Microsoft.Samples.Synchronization.ActivationContext.CreateActivationContext();
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.
"utf-8"
access-policy
cross-domain-access
policy
allow-from
http-request-headers
"*"
domain
uri
"http://*"
"file://*"
grant-to
resource
path
"/"
include-subpaths
"true"
In this task, you will configure the service and build it.
In the Solution Explorer, expand the files under DefaultScopeSyncService.svc and then double-click DefaultScopeSyncService.svc.cs to open the file.
Uncomment the following lines in the InitializeService method.
config.ServerConnectionString =
"connection string here"
;
config.SetEnableScope(
"scope name goes here"
Replace connection string here with the following:
Data Source=<SQLAzure_Server_Name>;Initial Catalog=listDb;User ID=username;Password=password;Trusted_Connection=False;
Replace “scope name goes here” with DefaultScope.
Add the following lines of code to the InitializeService method.
// configure filter parameters used by the service
config.AddFilterParameterConfiguration(
"userid"
,
"User"
"@ID"
(System.Guid));
"Item"
"@UserID"
"List"
"TagItemMapping"
// enable Diagnostic Dashboard feature for the service
config.EnableDiagnosticPage =
// enable verbose errors
config.UseVerboseErrors =
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
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.
To deploy the sync service to Windows Azure:
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.
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.
Create a service on Windows Azure, upload these two files, and start the service.
Verify that the deployment was indeed successful.
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.
Use the following syntax to see diagnostics information for the service: http://[servicename].cloudapp.net/defaultscopeSyncService.svc/$diag.
In this walkthrough, you will develop an offline-capable Silverlight application that consumes a sync Service.
You must have the following products/components installed on your computer.
Visual Studio 2008 SP1 or Visual Studio 2010 with C# language components
Silverlight 3 Tools for Visual Studio 2008 or Silverlight 4 Tools for Visual Studio 2010
In this task, you will generate code files for a Silverlight client using the SyncSvcUtil.exe tool.
Open a Command Prompt window with administrative rights, and switch to C:\Program Files\Microsoft SDKs\Microsoft Sync Framework\4.0\Samples\Config folder.
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.
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.
In this task, you will create a Visual Studio Silverlight project which will consume the sync service.
In the Solution Explorer window, right-click Solution ‘ListService’, point to Add, and then click New Project.
In the New Project dialog, expand Visual C# in the “Project types:” list and select the Silverlight project type.
Select Silverlight Application from the templates menu.
Type the name ListClient and then click OK to create the project.
Click OK on the New Silverlight Application dialog box
In the Solution Explorer, right-click ListClient, point to Add, and then click Add Existing Item.
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.
Examine the Solution Explorer and make sure that that the files appear in the project tree.
In the Solution Explorer, right-click ListClient project, point to Add, and then click Add Reference.
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.
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.
Select Microsoft.Synchronization.ClientServices.dll and then click OK.
Follow Step 11 to bring up the Add Reference dialog box again.
Switch to the .NET tab.
Select System.ComponentModel.DataAnnotations and System.Windows.Controls.Data, and then click OK.
At this point, the project should compile successfully. On the Build menu, click Build Solution.
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).
In the Solution Explorer, expand MainPage.xaml and double-click MainPage.xaml.cs to open the file.
At the beginning of the file, add the following statements.
DefaultScope;
Microsoft.Synchronization.ClientServices.IsolatedStorage;
Inside the MainPage class, create a new variable of type DefaultScopeOfflineContext and name it context.
DefaultScopeOfflineContext context;
In the constructor of the MainPage class, add a line of code to create the context above the InitializeComponent method call.
context =
DefaultScopeOfflineContext(
"list_client"
Uri(
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.
As the scope on the server is filtered by user id, we must specify the parameter here as described in the following list.
Add the following line of code to the constructor of MainPage class above the InitializeComponent statement.
context.CacheController.ControllerBehavior.AddScopeParameters(
Guid(
"{EE9F0661-AD72-4b0b-8627-73722520A41B}"
).ToString());
On the Tools menu, click Create Guid.
Select the Registry Format radio button and then click Copy and then click Exit.
Paste the GUID in the clipboard to replace the GUID in the above AddScopeParameters method call.
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.
Now we need to display the data. In the Solution Explorer, double-click on MainPage.xaml in the ListClient project.
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
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:
Add an xml property of x:Name to the DataGrid element and make the value TagsGrid.
Set its Grid.Row property to 0.
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"
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"
Add a Button to the MainPage.xaml file after the DataGrid, specified as follows:
Button
Content
"Sync"
"1"
HorizontalAlignment
"Center"
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:
Click
"Button_Click"
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"
"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
"LayoutRoot"
Grid.RowDefinitions
RowDefinition
Height
Return to the MainPage.xaml.cs page.
In the constructor, add an event handler for the DefaultScopeOfflineContext.LoadCompleted event in the line after you assign the context variable:
context.LoadCompleted +=
EventHandler<LoadCompletedEventArgs>(context_LoadCompleted);
Following that line, add an event handler for the MainPage.Loaded event:
this
.Loaded +=
RoutedEventHandler(MainPage_Loaded);
Your MainPage constructor should now look as follows:
MainPage()
InitializeComponent();
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:
MainPage_Loaded(
object
sender, RoutedEventArgs e)
context.LoadAsync();
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:
context_LoadCompleted(
sender, LoadCompletedEventArgs e)
Dispatcher.BeginInvoke(
delegate
TagsGrid.ItemsSource = context.TagCollection;
});
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();
At this point, the code for the application is complete. The MainPage.xaml.cs file should look as follows:
System.Net;
System.Windows;
System.Windows.Controls;
System.Windows.Documents;
System.Windows.Input;
System.Windows.Media;
System.Windows.Media.Animation;
System.Windows.Shapes;
ListClient
partial
MainPage : UserControl
// create an instance of offline context type
// the code for this class is generated by
// the SyncSvcUtil utility.
// specify a value for the userid parameter for scope
"{832106DC-2532-4f7a-9E6D-050383957D38}"
// subscribe for the context.LoadCompleted event
// subscribe for the Page.Loaded event
// subscribe for the RefreshCompleted event
context.CacheController.RefreshCompleted += (sender, args) =>
(args.Error !=
// display any error occurred during refresh
// operation in a message box
MessageBox.Show(args.Error.ToString());
};
// 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.
// bind the TagCollection object of context to ItemsSource
// to TagsGrid
Button_Click(
From the Build menu, click Build Solution to the build the solution.
Make sure that the ListService project is set as the Startup project for the solution.
In the Solution Explorer, right-click Solution ‘ListService’ (2 projects), and then click Set Startup Projects.
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.
Click OK to close the Solution ‘ListService’ Property Pages dialog box.
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 ….”
In the ListService project, right-click ListClientTestPage.html, and then click Set As Start Page.
Press F5 or Ctrl+F5 to run the code. You should see an Internet Explorer window that opens html page.
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):
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.