Welcome to MSDN Blogs Sign in | Join | Help


[Note: Source for this sample can be downloaded from http://www.maheshwar.net/projects/Sync/DbSyncProviderSample.zip. Just ensure that you modify the connectionstrings in properties\settings file to point to the correct database. This sample is based off the RC0 build recently released.]

This post is to be a starting guide for people wanting to use Microsoft Sync Framework to synchronize databases. We are shipping a customized database provider, DbSyncProvider, that lets users synchronize tables in a peer to peer fashion. Note that we also ship a DbServerSyncProvider/DbClientSyncProvider combination in Sync Services For ADO.NET API that synchronizes data between Server and Client in a hub-and-spoke topology. DbSyncProvider will allow hub-and-spoke synchronization (if that is what is required) but the real power lies in the peer to peer synchronization.

This post will walk through the steps required to sync an existing table using DbSyncProvider. We will use a simple WinForms application to view/edit and synchronize a Customer table across two DB's.

image

This post assumes that the table Customer cannot be modified and hence we will use a separate table (DeCoupled Tracking), CustomerMetadata, to track item metadata. To keep our queries simple we will use the default DB metadata column names. I will show at the very end how simple it is to modify the above solution for a table that maintains the metadata in the same table (Coupled Tracking). For sake of simplicity lets approach the database synchronization configuration in these following steps.

  1. DbSyncProvider Specifics
  2. Create Scope Information table
  3. Add metadata for Customer table
  4. Configure Source DbSyncProvider
  5. Specify Synchronization Scope commands
  6. Configure Destination DbSyncProvider
  7. Conclusion: Putting it all together

Step 1. DbSyncProvider Specifics

A DbSyncProvider represents a set of tables within a database defined by a “scope”that are to be synchronized. It contains information such as the connection string and the synchronization scope name, as well as Knowledge information that is used to determine what information is known by the local peer. Each table that needs to be synchronized from the database needs to be represented by a DbSyncAdapter and should be added to the SyncAdapters collection on the provider. Each DbSyncAdapter will contain SqlCommands for the following tasks.

  • Selecting incremental changes that are not in the destination’s knowledge.

  • Performing Inserts/Updates/Deletes from remote peers and updating corresponding metadata.

  • Cleanup tombstone metadata.

  • Select a particular row with a given id.

At the provider level, users need to specify SqlCommands for the following tasks.

  • Select the current timestamp for the local provider.
  • Select/update Scope info for given scope_name.

With our sample we will see the very basic properties that needs to be configured at the provider/syncadapter level to get started. To keep this demo simple, I am going to refrain from using stored procedures and rather depend on plain vanilla select/insert/delete SQL commands. For this demo we will create two databases SampleDB1 and SampleDB2 and will create two DbSyncProviders to represent them. Create the above mentioned databases.

image

Note: For RC release, DbSyncProviders expects to have snapshot isolation turned on at each database. This also means that this can be used against Sql Server 2005 and higher. So after creating them run the following command in both databases.

image

Step 2. Create Scope Information Table

As I mentioned earlier the provider needs the synchronization scope name and other scope related commands (update/select). Scope represents a set of tables that are involved in a sync session and each scope will need to maintain knowledge (data and tombstone) information for all peers that this scope has synchronized with in the past. We will create a Scope_Info table to maintain this information. The design for the scope_info table in SampleDB1 database is as follows.

image

It contains the scope id, sync knowledge, tombstone knowledge and the last updated timestamp to hold all scope related information. Scope Id is a unique identifier Lets create a new scope 'DbSyncDemo' and use this to synchronize our user tables. Initially it will contain null for DbSyncSession.SyncScopeKnowledge and DbSyncSession.SyncScopeCleanupKnowledge. This will tell the core Sync runtime that this database has not synchronized with any peer.

image

We need the scope table in all peers that will be involved in the synchronization session so create the same table in the SampleDb2 database as well.

Note: All column names of this table matches the constant values for scope metadata columns in DbSyncSession. The provider will look for these column names from the result set when selecting or updating scope.

Step 3. Add metadata for Customer table

Lets take a look at the Customer table. It contains fields for name, age and country of each customer. Design of Customer table is as follows.

image 

We use id as the Primary Key for this table. Next we need to create a CustomerMetadata table that will hold row metadata for each row in Customer table. Each row in the table represents an Item in the Sync world and hence our metadata table needs to maintain the modified timestamp, creation id, creation timestamp, update key, update timestamp and tombstone metadata for each corresponding row in Customer table. We will use the PK column ‘id’ from the base table to correlated entries in the metadata table. Once again we will use the constant metadata names from DbSyncSession class to name the columns in this table. Design of CustomerMetadata table is as follows.

image

Next we need to update the metadata table each time a row is inserted/updated/deleted. For that we will add triggers on Customer table. We will create 3 triggers, one each for Insert, Update and Delete. Lets take a look at the insert trigger.

CREATE TRIGGER [dbo].[customer_insert_trigger]
   ON  [dbo].[Customer]
   For INSERT
AS
BEGIN
    -- Insert statements for trigger here
    insert into CustomerMetadata (id, sync_row_is_tombstone,sync_create_peer_key, sync_create_peer_timestamp, sync_update_peer_key, sync_update_peer_timestamp)
    select id, 0,0,@@DBTS+1,0,@@DBTS+1 from inserted
END

Since in Sync, '0' is the id for the current peer we enter 0 as the create/update key. Similarly we enter 0 for the tombstone column denoting that it is not a tombstone row.

The update trigger is much simpler. In case of updated we only need to update the update key (will always be 0 denoting current peer) and update timestamp.

CREATE TRIGGER [dbo].[customer_update_trigger]
   ON  [dbo].[Customer]
   For update
AS
BEGIN
    -- Insert statements for trigger here
    update meta
        set sync_update_peer_key = 0,
        sync_update_peer_timestamp = @@DBTS +1   
    from CustomerMetadata meta join inserted i on  i.id = meta.id
END

Finally the delete trigger will set the tombstone column to 1 (in addition to modifying the update key/timestamp pair).

CREATE TRIGGER [dbo].[customer_delete_trigger]
   ON  [dbo].[Customer]
   For delete
AS
BEGIN
    -- Insert statements for trigger here
    update meta
        set sync_row_is_tombstone = 1,
        sync_update_peer_key = 0,
       
sync_update_peer_timestamp = @@DBTS+1 
    from CustomerMetadata meta join deleted i on i.id = meta.id
END

Create the above tables, triggers in SampleDb1 and SampleDb2 databases. Next we are ready to configure DbSyncProvider's for each of these endpoints. For starters purpose we will just sync in one direction, i.e SampleDb1 -> SampleDb2. I use this example as opposed to discussing a two way sync as I want to list the specific properties that needs to be set for a "source" vs "destination" provider.

 

Step 4. Configure Source DbSyncProvider

Lets start by creating a DbSyncProvider object and pass the connection string for SampleDb1. Since we are using Decoupled metadata table we will set the ChangeTracking property on the provider. to ChangeTrackingModel.Decoupled.

image

Next, create a DbSyncAdapter for Customer table.

image

Each DbSyncAdapter needs to know the list of Id columns whose value it will use to create a unique SyncId for each row metadata. In our case we will add column "Id" to the RowIdColumns collection. Next the only command that this DbSyncAdapter needs to provide is the SelectIncrementalChanges command. This will return the rows that have been modified/added/deleted since the destination provider last sync'd.

image

As you can see we specify the SyncMinTimestamp metadata column as an input parameter. This input parameter would be dynamically filled by the provider. The value is computed by merging the local knowledge with the remote knowledge information returned by the destination provider for GetSyncBatchParamters() method call. The above select command will return all new/updated and deleted rows as well. Next we tell the DbSyncAdapter the names of the tracking columns which represent the TombStone flag and the last change timestamp for the row. DbSyncProvider will populate the IsTombstone property on SyncRowMetadata  with the value returned from the IsTombstoneColumn and remove the tracking columns from the DataSet returned to the destination provider.

Finally add the DbSyncAdapter to the providers collection.

image

Next we need to specify Scope commands on the provider.

Step 5. Specify Synchronization Scope commands

Each DbSyncProvider needs certain commands to figure out the current timestamp of the database and to read/update scope information.

We need to set the following 3 properties on DbSyncProvider to enable reading timestamp and scope information.

DbSyncProvider.SelectNewTimestampProvider: This command will enable the provider to determine the current timestamp of the provider (in our sample it will be the current transaction count). Runtime will look for the timestamp in the DbSyncSession.SyncNewTimestamp metadata column so we will specify an output parameter  in our query.

image

DbSyncProvider.SelectScopeInfoCommand:

This command will be used by the provider to select the data knowledge and cleanup knowledge that this provider knows about for this scope. We will retrieve the required information from the query as output parameters. The required output parameters are DbSyncSession.SyncScopeId, SyncScopeKnoeledge, SyncScopeCleanupKnowledge and SyncScopeTimestamp.

image

DbSyncProvider.UpdateScopeInfoCommand:

This command will be used by the destination provider to update knowledge and cleanup information each time the provider completes a successful sync from a remote provider. This command is also used when users calls DbSyncProvider.CleanupMetadata() function.

image

The runtime will make sure that it passes the right  values for all the input parameters. We need to pass the rowcount back to the system in SyncRowCount metadata parameter so that the runtime can detect when update of the scope fails.

Finally configure the source DbSyncProvider with the above three commands.

image

Thats it. SampleDb1 is now configured to be a DownloadOnly source provider.

Step 6. Configure Destination DbSyncProvider

Next we need to create DbSyncProvider representing SampleDb2 database. For this we will create a DbSyncProvider and add the scope specific commands we added in step 4 and 5.  Since the database schemas are same we will reuse the same SqlCommands.

image

Then we create a DbSyncAdapter for table Customer.

image

We will then add the following destination specific properties on the sync adapter.

DbSyncAdapter.InsertCommand:

image

In this case we are specifying column names we want to insert in Customer table. As you can see the individual column names are specified as input parameters and these parameters will be set with the corresponding column values for each row added on the source provider. We need to explicitly populate the DbSyncSession.SyncRowCount metadata parameter in our insert command as this is how the provider will determine if the insert succeeded or not. Count will always be 1 for a successful insert but will be 0 for either a constraint violation or when a row with the same PK already exists.

In case of a PK violation and other conflict scenarios, the provider would try to run the DbSyncAdapter.SelectRowCommand to see what is currently in the table. So we would provide the SelectRowCommand as follows.

image

I am going to reserve conflict detection to a separate blog post as the workflow and commands involved are slightly different (based on type of PK conflict such as Local delete, remote update or local update, remote update). Suffice to say if Insert failed and SelectRowCommand returns 0 results then it means that its a constraint violation and it will raise DbSyncProvider.ApplyChangeFailed event if one is registered.

DbSyncAdapter.InsertMetadataCommand

On a successful insert, the provider will then try to insert/update metadata for the currently inserted row. We need to provide the following insert command to insert metadata in CustomerMetadata table.

image

We need to return the rowcount back to DbSyncProvider so it can raise a ApplyMetadataFailed event in case its 0.

DbSyncAdapter.UpdateMetadataCommand

The update command for our sample is as follows.

image 

On a successful insert of a row(rowcount for InsertCommand > 0), DbSyncProvider assumes the Couple metadata tracking model and hence will first try to execute the UpdateMetadataCommand. In our case the UpdateMetadataCommand will return 0 rowcount and  the system will check to see that the model is decoupled and then execute the InsertMetadataCommand. If that fails the ApplyMetadataFailed event will be raised.

DbSyncAdapter.UpdateCommand:

This command will update existing record in Customer table. Similar to the Insert command we need to return the rowcount back as an output parameter. If update count is 0 then we try to detect whether the row exists by running SelectRowMetadata and resolve the error. If SelectRowMetadata returns 0 then runtime will retry the update as an Insert.

image

On successful update, the provider will then execute UpdateMetadataCommand. If that fails (rowcount < 1) then the same fallback procedure mentioned for InsertCommand will be followed.

DbSyncAdapter.DeleteCommand:

Finally in the delete command we will remove the row from the Customer table.

image

Once again runtime expects us to return the rowcount as an output parameter. On failure to delete the record it will try execute SelectRowCommand to try to detect conflict as mentioned above. If that fails then it means that the remote peer created and deleted a row before this peer could synchronize. So the system would try to keep tombstone record for that entry by adding a metadata record for that deleted row to guarantee consistent metadata across peers.

DbSyncAdapter.DeleteMetadataCommand

This command is never used directly. Its only used during a conflict during InsertCommand (and the system tries it as an update) or a conflict in UpdateCommand (in that case the system tries it as an insert). In that case DbSyncProvider will first run the DeleteMetadataCommand and if that is successful will then retry the insert/update.

In our sample we will use the following DeleteMetadataCommand.

image

With the above mentioned 7 commands the destination specific DbSyncAdapter configuration is complete. We will add this table to the destination DbSyncProvider.

image 

Step 7. Conclusion: Putting it all together

With the source and destination providers set we will start Synchronization by creating a SyncOrchestrator object, set the local and remote providers and set the Direction property to SyncDirectionOrder.Download.

image

Finally we will call Synchronize when the user presses the "Synchronize" button on the winform.

Running the sample:

Lets start off by running the sample. We will add two rows for John and Jane Doe. Add the rows on the left hand side Customer table and press SaveChanges button. Then refresh the tables to see the metadata created by the triggers.

image

Next press synchronize and see the left side tables getting the values. Also ensure that the create and update key on the left side metadata column points to 1 and not 0.

image

I will leave the update/delete exercise up to the user. To modify the sample to be a two way synchronization, just add the source level DbSyncAdapter commands to SampleDb2 provider and similarly add the destination specific DbSyncAdapter commands to SampleDb1 provider. Then finally change the Direction property on the SyncOrchestrator to DownloadAndUpload.

Conclusion:

To summarize, we did the following to enable syncing Customer table across peers.

  1. Created a metadata table for DeCoupled tracking in all peers.
  2. Enabled Snapshot isolation on all peers.
  3. Configured a DbSyncProvider/DbSyncAdapter for SampleDb1 peer and added source peer specific commands.
  4. Configured a DbSyncProvider/DbSyncAdapter for SampleDb2 peer and added destination peer specific commands.

Maheshwar Jayaraman

We have released a new sample on our Sync code gallery site that demonstrates use of Sql Express database for offline caching and synchronization purposes. Sync Services for ADO.Net (a.k.a Occasionaly Connected Systems OCS) lets users take a database offline and enabled synchronization with the server in a hub-spoke topology. The released version shipped a full client provider implementation for Sql CE database. Users have been very eager to use SQL Express as their client and this sample ships an express client provider implementation and a test client demonstrating its usage. From our Sync blog.

"In this sample, two tables (orders and order_details) are on the server database and also on the local client database.  The sample synchronizes edits to these table to keep their data identical.
This sample demonstrates:

  • Using SQL Express to cache changes for a client application.
  • A customized SQLExpressClientSyncProvider class that wraps around Microsoft.Synchronization.Data.Server.DbServerSyncProvider.

I would like to point out that even though we have done a fair amount of testing in house with this sample, it is still only posted as a sample.  In the future we certainly plan of including a fully supported version of a SQL Express client provider within a future release of Sync Services for ADO.NET.  "

The sample Express provider does not support schema initialization so your client schema should be pre populated. The sample uses triggers for change tracking on server and client. Please take the sample for a spin and provide feedback at our forum.

 

Cross posting from http://maheshwar.net/Blog

Now that Silverlight Beta2 has been released I have upgraded the SL samples on this site to the new build. I have moved the current beta1 samples to a subdirectory under the current sample. Source code for the new Beta2 sample is also updated at http://code.msdn.microsoft.com/silverlightws

Beta1 Pox Proxy Sample: http://maheshwar.net/Projects/SLPoxSample/Beta1/SLPoxSample.aspx

Beta2 Pox Proxy Sample: http://maheshwar.net/Projects/SLPoxSample/SLPoxSample.aspx

Source Code for POX Proxy: https://code.msdn.microsoft.com/Release/ProjectReleases.aspx?ProjectName=silverlightws&ReleaseId=601

Maheshwar

1 Comments
Filed under:

I have been using Live Mesh for quite some time now as they were our first Silverlight internal customer. Its been a pleasure to work with the Mesh team and its even more fun to be seeing your code being used in real life large scale online app. I have 2 Live Mesh invites to hand out. First two people to leave thier live/hotmail ids get them.

Update: They are all gone. If people want any more I see the Long Zheng has started a website www.ShareMesh.com for inviting and requesting Mesh invites. New users can register there.

Maheshwar Jayaraman

After nearly 4 years with Connected Systems, I have decided to move to the Data Replication and Sync team working on the Microsoft Sync Framework. I wanted to post about the Web Proxy features we shipped in Silverlight Beta1 but see that our PM Yavor and Eugene have been doing a wonderful job keeping the SilverlightWS blog uptodate with interesting posts. I would definetly recommend subscribing to that blog. Some posts that I wanted to point out are

http://blogs.msdn.com/silverlightws/archive/2008/04/16/debugging-web-service-usage-in-silverlight-2.aspx

http://blogs.msdn.com/silverlightws/archive/2008/03/30/some-tips-on-cross-domain-calls.aspx

 Maheshwar Jayaraman

Technorati Tags: ,

Hope everyone had a chance to download and play with the Silverlight 2.0 Beta1 bits. I wanted to put up a quick post detailing the features we have shipped in this Beta. We have shipped total of 5 features split across 5 assemblies.

Core Assembly:

  • System.Runtime.Serialization.dll - Data Contract OM and Data Contract Serializer
  • System.ServiceModel.Web.dll - Data Contract JSON Serializer
  • System.ServiceModel.dll - WCF Client side proxy OM and runtime.

Extension/SDK Assembly:

  • System.ServiceModel.Syndication.dll - RSS/ATOM OM
  • System.Xml.Serialization.dll - Xml Serializer

Core assemblies are the assemblies that gets installed on to your box when you install Silverlight 2.0 and SDK assemblies are installed with the SDK. When your app uses SDK assemblies its treated like any other third party dll and gets bundled with user app in the XAP file. All references to core assemblies are loaded from the users Silverlight installation directory. All WebServices components have API/wire parity with recently released Orcas version. We have strived very hard to ensure that the wire representation of Silverlight serialized types are compatible with Orcas serializer.

System.Runtime.Serialization.dll

This contains the DataContract public OM and the DataContractSerializer (DCS) class. The Silverlight DCS serializer supports the following public properties:

  1. Supports DataContractAttributes, CollectionDataContractAttribute, EnumMemberAttribute, DataMemberAttribute and KnownTypeAttributes on types.
  2. XmlDictionaryReader and XmlDictionaryWriters
  3. Ability to serialize Plain Old Clr Objects (POCO or non data contract types)

We are waiting to hear from users to decide on supporting features like IExtensibleDataObject, IDataContractSurrogate and PreserveObjectReferences. Type ISerializable is no longer available in Silverlight and so DCS can no longer serialize pure ISerializable types (it was fallback to serializing it as a POCO type provided the type has a public no argument constructor).

 

System.ServiceModel.Web.dll

We have completely rewritten DataContractJsonSerializer (DCJS) to ensure that the assembly fits in the reduced size requirements of Silverlight. This type is semi-compatible with the Orcas runtime. It maintains a subset of the public method but no longer derives from XmlObjectSerializer. Its a stand alone type and supports serializing any DataContract/POCO type to JSON. JSON produced by the Silverlight serializer is wire compatible with Orcas and so Orcas serializer should be able to deserialize that JSON just fine. Support for working with JXML (XmlJsonReader and XmlJsonWriter classes) is not available in the core and we have shipped it as a standalone sample. We believe the scenarios for JXML on the client is very limited but are open to feedback.

 

System.Xml.Serialization.dll

We have shipped an almost full featured port of the desktop XmlSerializer. Again we have strived very hard to ensure wire compat with existing Orcas serializer. One big implementation difference is the fact that the Silverlight Xml Serializer uses Lightweight Code Gen (LCG) to generate (de)serialize types on the fly. The Orcas serializer used Code Dom to generate the code required to read/write types. This implies that users will not be able to see the generate code. This behavior is actually pretty good as executing IL is way faster than executing raw code. Silverlight doesnt support features like XElement/XNode yet and hence the XmlSerializer doesnt support serializing them yet.

 

Note:

As you can see, I have stressed the "wire" compat nature of Silverlight serializers and did not talk about "Type" compat. Type compat between the serializers would mean that a desktop DataContract/XmlSeriailzable types can be reused in Silverlight. Silverlight being a subset of Orcas, certain serialization features/classes are not available. For example, a ISerializable type in desktop will obliviously will not be serializable in Silverlight. THe only supported way of porting types to Silverlight is "Schema" based. By Schema based I mean, users would need to create type on one side, export the schema for that type and then reimport it on the other side. For Beta1, the only way for importing schema is via the "Add Service Reference" option on a Silverlight project. We will ship the silverlight verison of SvcUtil, SlSvcUtil.exe, in Beta2.

 

System.ServiceModel.Syndication.dll

This is the only assembly that is complete feature port from Orcas. All Orcas RSS/Atom scenarios are fully supported in Silverlight.

 

System.ServiceModel.dll

WCF client side proxy deserves a separate post of its own and I will post one shortly.

 

Maheshwar Jayaraman.

6 Comments
Filed under:

Scott Guthrie laid out all the details regarding Silverlight 2.0 Beta1 this morning and if you haven't had a chance to see the keynote then you can view it from VisitMix site. Scott briefly mentioned the support for networking stack in Silverlight and I would like to quote him.

"We have a very robust networking stack in SL2.  We have support for REST, for SOAP, for WS-* - we support calling out to anything.  We support calling external services, not just back to the server-of-origin.  We even have support for sockets, if you want to program at that level."

Our PM, Yavor has already kicked off the information flow by posting bunch of posts on SilverlightWS blog. Lets quickly recap all the information that has been announced so far.

  1. Install Silverlight Beta1 Runtime- http://www.microsoft.com/silverlight/resources/installationFiles.aspx?v=2.0
  2. Install Silverlight tools (Runtime, SDK, VS Tools) - http://www.microsoft.com/downloads/details.aspx?FamilyId=E0BAE58E-9C0B-4090-A1DB-F134D9F095FD&displaylang=en
  3. Silverlight WebServices MSDN Samples site - http://code.msdn.microsoft.com/silverlightws/Release/ProjectReleases.aspx?ReleaseId=601

We have shipped a prototype for a generic POX Proxy client that we are hoping to get feedback on. We want to evaluate how many people care about a generic POX proxy and what specific features they look for in such a proxy. We are hoping to use all the feedback to decide whether we need to formally add it to the produce or not. This blog post is to give a step by step guide on the sample app we wrote that uses the POX proxy to invoke EBay POX services. We will use the POX proxy to invoke the FindItems call of the EBay POX service via XML and JSON payloads and display the returned results on a DataGrid. Here is the final look of the sample.

PoxSample

Step1: Understanding the requirements of the remote POX Service.

EBay have exposed a POX service endpoint for its shopping services and information on it can be found at http://developer.ebay.com/products/shopping/. We would be using the POX proxy API to invoke the FindItems call. FindItems takes a FindItemsRequest and returns a FindItemsResponse. To have a strongly typed programming model, we need to first create CLR types for FindItemsRequest and FindItemsResponse. EBay doesn't support a Metadata Endpoint and so I manually mapped the types information from Ebay shopping WSDL (You could do the same from the XSD as well). Silverlight's System.Xml.dll doesn't support XmlElement or XElement in Beta1 yet so all xs:any elements from the WSDL have been mapped to object. Since we want the same type to be serialized via XmlSerializer and DataContractJsonSerializer, we will decorate the types with XmlElement and DataContract attributes. Here is how the FindItemsRequest types looks (You can refer the Shopping.cs file from the code to refer the complete set of types).

System.Xml.Serialization.XmlRoot(Namespace="urn:ebay:apis:eBLBaseComponents", ElementName="FindItemsRequest")]

[DataContract]

public partial class FindItemsRequestType : AbstractRequestType

{

private string queryKeywordsField;

private SimpleItemSortCodeType itemSortField;

private bool itemSortFieldSpecified;

private SortOrderCodeType sortOrderField;

private bool sortOrderFieldSpecified;

private int maxEntriesField;

private bool maxEntriesFieldSpecified;

private string postalCodeField;

private string[] sellerIDField;

private string[] sellerIDExcludeField;

/// <remarks/>

[System.Xml.Serialization.XmlElementAttribute(Order=0)]

[DataMember(EmitDefaultValue=false)]

public string QueryKeywords

{

get

  {

    return this.queryKeywordsField;

  }

set

  {

    this.queryKeywordsField = value;

  }

}

/// <remarks/>

[System.Xml.Serialization.XmlElementAttribute(Order=1)]

[DataMember(EmitDefaultValue = false)]

public SimpleItemSortCodeType ItemSort

{

  get

  {

    return this.itemSortField;

  }

  set

  {

    this.itemSortField = value;

  }

}

........

Step 2: Prepare and Issue FindItemsRequestType

Next step is to use the POX proxy API to issue a call to the FindItems request. We pulled all the POX related code to EbayUtility.cs file. All requests to the Ebay service have some common fields such as appid and version and we will populate the PoxProxyRequest with those common entries. You can also subclass EbayPOXProxyRequest from POXProxyRequest and do the common stuff within that class.

//Add custom Stuff

POXProxyRequest ebayRequest = new POXProxyRequest(new Uri("http://open.api.ebay.com/shopping"));

ebayRequest.WebMethod = POXUtils.HttpVerbs.Post;

ebayRequest.QueryParameters["callName"] = "findItems";

ebayRequest.QueryParameters["appid"] = "YOUR_EBAY_APP_ID";

ebayRequest.QueryParameters["version"] = "523";

We then specify the request and response encoding (JSON or XML) as a query and also modify the content type to match the encoding.

//Set content type to either JSON or XML

ebayRequest.QueryParameters["requestencoding"] = this.parent.ebayPoxEncodingType;

ebayRequest.QueryParameters["responseencoding"] = this.parent.ebayPoxEncodingType;

ebayRequest.ContentType = this.parent.contentType;

We then create an instance of FindItemsRequestType and populate it will all required fields. The only required filed in FindItemsRequest is QueryKeywords and we will populate that field in addition to the couple of optional fields, MaxEntires and PostalCode field. Refer the EbayControl.Xaml file for the input fields.

//Prepare the request

FindItemsRequestType fir = new FindItemsRequestType();

fir.QueryKeywords = this.parent.ebayControl.searchQueryTxtBox.Text;

fir.MaxEntries = Int32.Parse(this.parent.ebayControl.maxEntriesTxtBox.Text);

fir.MaxEntriesSpecified = true;

if (!string.IsNullOrEmpty(this.parent.ebayControl.postalCodeTxtBox.Text))

{

    fir.PostalCode = this.parent.ebayControl.postalCodeTxtBox.Text;

}

Subscribe to the PoxProxyResponse's GetResponseCompleted event so that we can populate our DataGrid with the search results. Also, we will subscribe to the RequestSerialized and ResponseSerialized event so we can use that to show the actual request/response payload content to the user.

//Callback to call when the POX response is received

ebayRequest.GetResponseCompleted += new EventHandler<GetResponseCompletedEventArgs>(this.parent.ebayClient_GetResponseCompleted);

//Display the Actual serialized/deserialized contents on UI

ebayRequest.RequestSerialized = this.parent.PrintStreamContents;

ebayRequest.ResponseDeSerialized = this.parent.PrintStreamContents;

Finally issue the request.

//Issue the Async request.

ebayRequest.GetResponseAsync<FindItemsRequestType>(fir);

Step 3: Receive Response and Populate DataGrid

When the response is received from the service, the POX Proxy will invoke the callback provided in the previous step. We then retrieve POXProxyResponse from the GetCompletedEventArgs object. We will use this POXProxyResponse to retrieve the FindItemsResponseType.

POXProxyResponse resp = e.Response;

FindItemsResponseType fir = resp.GetResponse<FindItemsResponseType>();

resultsListBox.ItemsSource = fir.Item;

FindItemsResponseType contains an array of SimpleItemType objects that we will assign to the DataGrid. Since we have a strongly typed object of the response we can use data binding to pull values from the object.

<ListBox x:Name="resultsListBox" Visibility="Collapsed" Width="Auto" SelectionChanged="resultsListBox_SelectionChanged">

    <ListBox.ItemTemplate>

        <DataTemplate>

            <StackPanel Orientation="Vertical">

                <Grid>

                    <Grid.ColumnDefinitions>

                        <ColumnDefinition Width="100"/>

                        <ColumnDefinition Width="60"/>

                        <ColumnDefinition Width="*"/>

                    </Grid.ColumnDefinitions>

                    <StackPanel Orientation="Horizontal" Grid.Column="0">

                        <TextBlock Text="{Binding ConvertedCurrentPrice.currencyID}" Margin="5" Foreground="Blue" VerticalAlignment="Center"/>

                        <TextBlock Text="{Binding ConvertedCurrentPrice.Value}" Foreground="Blue" VerticalAlignment="Center"/>

                    </StackPanel>

                    <Image Source="{Binding GalleryURL}" Height="55" Margin="7,7,5,5" Grid.Column="1"/>

                    <TextBlock Text="{Binding Title}" Margin="5" Foreground="Black" VerticalAlignment="Center" Grid.Column="2"/>

                </Grid>

            </StackPanel>

        </DataTemplate>

    </ListBox.ItemTemplate>

</ListBox>

Step 4: Display Request/Response Stream Data

We had subscribed to the RequestSerialized and ResponseSerialized events and on invocation we will display the Stream contents to the  user. The Stream contents will contain the actual serialized/deserialized data.

//Print the serialized/Deserialized stream content.

StringBuilder builder = new StringBuilder(this.payLoadTextBox.Content.ToString());

builder.Append(Environment.NewLine);

if (s != null)

{

    StreamReader reader = new StreamReader(s);

    builder.Append("=====================Stream Contents=======================").Append(Environment.NewLine);

    builder.Append(reader.ReadToEnd()).Append(Environment.NewLine);

    builder.Append("===========================================================");

}

else

{

    builder.Append("Stream was null");

}

builder.Append(Environment.NewLine);

this.payLoadTextBox.Content = builder.ToString();

You can switch between XML and JSON payload to use either XmlSerializer or DataContractJsonSerializer. As you can see we have made the process of invoking a POX service very straight forward. If you refer the Digg sample that Scott posted last week, you can see that the sample use WebClient to do the heavy lifting and had to use LINQ to get data from the response. The WebClient class in Silverlight is a subset of the desktop version and support for headers, non Get verbs and ability to get response streams is not available. Once you want to use WebClient/HttpWebRequest to call in to various POX requests you can see that it can get cumbersome. Users can easily create a type for the Digg response and use the POX Proxy to achieve the same effect.

Please download and use the Proxy and provide us with feedback on how we can improve this.

Maheshwar Jayaraman

3 Comments
Filed under:

Mix 2008 is just hours away and I am very excited to talk about all the work we have been doing to get the beta of Silverlight 2 ready. Scott Guthrie already kicked off the information flow with 2 wonderful posts on Silverlight 2 and Blend. We have worked hard to incorporate WCF + Orcas SP1 features in this Beta1 and I am personally very excited about the bits. Eugene, our PM, is presenting a session tomorrow on the features available for working with Data and web services.. 

Working with Data and Web Services in Microsoft Silverlight 2
Wednesday, March 5 1:30 PM - 2:45 PM, Delfino 4105
Speaker(s): Eugene Osovetsky
Audience(s): Technical
Session Type: Breakout
Learn how easy it is to utilize POX, REST, RSS, ATOM, JSON, and SOAP in your Microsoft Silverlight mashup applications. Also learn how to easily access and display data with Silverlight using LINQ and databinding.

Eugene will talk about the serializer, proxy support we are shipping with Beta1 and will also briefly talk about the direction we are thinking about for the next milestone of Silverlight 2. I have been working on Silverlight 2 for the past year and with Mix 08, I can finally talk about specifics and share more details.

Update: We have setup a team blog for Silverlight Web Services at http://blogs.msdn.com/silverlightws. We plan on posting a lot of services related content there in the next few weeks.

3 Comments
Filed under:

Just like that the .Net train continues to move along to the next version of the framework. Microsoft announced on Monday the availability of .Net 3.5 and Visual Studio 2008. Its the third version of .Net that I had the privilege of working on starting from 2.0. I didnt have an opportunity to own any one particular feature in .Net 3.5 as I had moved on to the Silverlight project. My personal favorite features are the Visual Studio F5 experience for WCF services, Uri templates and Syndication API's.

You can read more about this release from Soma's blog. http://blogs.msdn.com/somasegar/archive/2007/11/19/visual-studio-2008-and-net-framework-3-5-shipped.aspx.  

Here are the download locations.

.NET Framework v3.5

http://www.microsoft.com/downloads/details.aspx?FamilyId=333325FD-AE52-4E35-B531-508D977D32A6&displaylang=en

.NET Framework v2.0 (x86) SP1

http://www.microsoft.com/downloads/details.aspx?FamilyId=79BC3B77-E02C-4AD3-AACF-A7633F706BA5&displaylang=en

.NET Framework v2.0 (x64) SP1

http://www.microsoft.com/downloads/details.aspx?FamilyId=029196ED-04EB-471E-8A99-3C61D19A4C5A&displaylang=en

.NET Framework v2.0 (IA64) SP1

http://www.microsoft.com/downloads/details.aspx?FamilyId=32E77AE0-96EF-4ECD-A157-9BF61A7C8DAA&displaylang=en

.NET Framework v3.0 SP1

http://www.microsoft.com/downloads/details.aspx?FamilyId=EC2CA85D-B255-4425-9E65-1E88A0BDB72A&displaylang=en

Next stop: Silverlight!

1 Comments
Filed under:

After a lot of procrastination I finally got a new blog on my personal domain hosted at http://maheshwar.net/Blog. Thanks to all the people who responded via comments and emails to my earlier post asking for suggestions on domain hosting providers. I took the general consensus and got my hosting account with DiscountAsp.Net. I got the whole thing setup over a weekend and then found a very customizable blogging framework in BlogEngine.net to use for my blog. I have already made some changes to the framework it has been pretty straightforward experience so far.

I am going to cross post all my technical blogs on both sites but move my random ramblings over to my personal site. I have lot of mini Silverlight projects that I would love to host on that site once Silverlight is released (in beta or RTM format). For people who are interested in my following my other blog can subscribe to http://www.maheshwar.net/Blog/syndication.axd.

 

Microsoft just announced the Messenger IM control. I took a quick look at it and found it to be pretty useful idea and want to test this out on my blog. Sometimes I get email from people who have follow up questions on a blog post and this would be a wonderful way to have a quick conversation if I am online. I am going to try it out for a few weeks and if its successful then I am going to add it to my personal domain as well. From looking at the code, I can see that the it doesnt have my login name in it and so *hopefully* I wont be spammed.

Its not like this Windows live is the first to do this, Google already has the "chat with contacts" embedded in GMail and then there is MSN web messenger. But this is the first time I dont need a login name to use the feature.

Windows Live is releasing some nice features. Now all I need is this feature integrated in Hotmail so I can have instant conversations with online contacts without needing the desktop client.

Microsoft is releasing the source code for the framework libraries. Read more about it here. Its not like you couldnt do this earlier. You could always have used .Net Reflector but having the ability to step in to the framework classes while debugging is neat. Looks like plans are on for releasing the source code for WCF as well!

 

In .Net 2.0 (Whidbey), Remoting introduced support for IPC channels. This was a quick way for local machine processes to communicate with objects hosted locally. Now there is a small behavior that  users might not be aware of when they are unmarshalling an ObjRef from an remote appdomain that has an IpcChannelServer registered. As you