locked
Performance: Pruning SyncContext returned by ApplyChanges? RRS feed

  • Question

  • Hi,

    DbServerSyncProvider. ApplyChanges returns a SyncContext. 

    The SyncContext contains two references to the DataSet that was sent as an input argument to ApplyChanges; one in SyncContext.DataSet and one in SyncContext.GroupProgress.Changes (I am wrapping my table updates in a a SyncGroup).

    In the N-tier scenario this SyncContext is serialized and set over the wire. Each DataSet reference is serialized separately; i.e. I get 2 copies back of the DataSet I sent in..! Needless to say, this is very painful when sending large DataSets containing e.g. binary data. 

    So... can I safely strip the large tables from the SyncContext's DataSet before ApplyChanges returns, or are they used by the Sync Framework on the client side?

    Thanks,

    Andreas




    • Moved by Hengzhe Li Friday, April 22, 2011 3:18 AM (From:SyncFx - Microsoft Sync Framework Database Providers [ReadOnly])
    Thursday, March 19, 2009 2:16 PM

Answers

  • Hi Andhallberg,
    You are right in noticing that the SyncContext.DataSet and SyncContext.GroupProgress.Changes have the same reference to the DataSet. During serialization these references are hit twice when serializing the uber object graph and hence you see the same DataSet being serialized twice. There are a couple of ways you can workaround this.

    1. Use Preserve Object Reference option if using WCF DataContractSerializer. http://blogs.msdn.com/sowmy/archive/2006/03/26/561188.aspx. This way you serialize the object only once.
    2. If not using WCF or your serializer does not support object reference then you can set the SyncContext.DataSet property to null before sending it to the remote side. On the remote side after the event has been deserialized you can once again set the SyncContext.DataSet property to the same reference as SyncContext.GroupProgress.Changes property. This way you have the dataset serialized only once. Both the properties has got to be not null before being passed on to the Synchronized so ensure that they are correctly set post deserialization.

    Maheshwar Jayaraman
    Maheshwar Jayaraman - WCF -- http://blogs.msdn.com/dotnetremoting
    • Marked as answer by andhallberg Thursday, May 7, 2009 3:33 PM
    Tuesday, April 28, 2009 10:17 PM
    Moderator

All replies

  • Would like to repro your scenario.  How do you detect "2 copies back of the DataSet I sent in" - if you do not mind to reveal details?

    Thanks,
    Leo Zhou ------ This posting is provided "AS IS" with no warranties, and confers no rights.
    Friday, March 20, 2009 8:09 AM
    Answerer
  • Hi Leo,

    1. Create a Local Database Cache using the VS 2008 Wizard. Choose the Northwind DB,  include the "Orders" table. 
    2. For "Server project location", choose a vanilla WCF service library project.
    3. Change the WCF binding to "basicHttpBinding" so we can see what's being sent in cleartext.
    4. Configure the OrdersSyncTable with SyncDirection.UploadOnly
    5. Create some means of changing the Orders data, e.g. a databound-form
    6. Change a cell value and synchronize.

    Below is the <ApplyChangesResponse>, snooped up using e.g. Fiddler2. As you can see, the DataSet is serialized twice since there are 2 references to it in SyncContext, although it actually is the exact same DataSet. 

    And why even return the DataSet at all? Seems to me the return type for ApplyChanges should be a SyncContext baseclass sans the DataSet we just sent in. 

    Thanks,

    /A
     
    <ApplyChangesResponse xmlns="http://tempuri.org/"><ApplyChangesResult xmlns:a="http://schemas.datacontract.org/2004/07/Microsoft.Synchronization.Data" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"><a:_batchCount>0</a:_batchCount><a:_dataSet><xs:schema id="NewDataSet" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns="" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"><xs:element name="NewDataSet" msdata:IsDataSet="true" msdata:Locale=""><xs:complexType><xs:choice minOccurs="0" maxOccurs="unbounded"><xs:element name="Orders" msdata:Locale=""><xs:complexType><xs:sequence><xs:element name="OrderID" type="xs:int" minOccurs="0"/><xs:element name="CustomerID" type="xs:string" minOccurs="0"/><xs:element name="EmployeeID" type="xs:int" minOccurs="0"/><xs:element name="OrderDate" type="xs:dateTime" minOccurs="0"/><xs:element name="RequiredDate" type="xs:dateTime" minOccurs="0"/><xs:element name="ShippedDate" type="xs:dateTime" minOccurs="0"/><xs:element name="ShipVia" type="xs:int" minOccurs="0"/><xs:element name="Freight" type="xs:decimal" minOccurs="0"/><xs:element name="ShipName" type="xs:string" minOccurs="0"/><xs:element name="ShipAddress" type="xs:string" minOccurs="0"/><xs:element name="ShipCity" type="xs:string" minOccurs="0"/><xs:element name="ShipRegion" type="xs:string" minOccurs="0"/><xs:element name="ShipPostalCode" type="xs:string" minOccurs="0"/><xs:element name="ShipCountry" type="xs:string" minOccurs="0"/><xs:element name="LastEditDate" type="xs:dateTime" minOccurs="0"/><xs:element name="CreationDate" type="xs:dateTime" minOccurs="0"/></xs:sequence></xs:complexType></xs:element></xs:choice></xs:complexType></xs:element></xs:schema><diffgr:diffgram xmlns:diffgr="urn:schemas-microsoft-com:xml-diffgram-v1" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"><NewDataSet xmlns=""><Orders diffgr:id="Orders1" msdata:rowOrder="0" diffgr:hasChanges="modified"><OrderID>10248</OrderID><CustomerID>VINET</CustomerID><EmployeeID>5</EmployeeID><OrderDate>1996-07-04T00:00:00+02:00</OrderDate><RequiredDate>1996-08-01T00:00:00+02:00</RequiredDate><ShippedDate>1996-07-16T00:00:00+02:00</ShippedDate><ShipVia>3</ShipVia><Freight>32.38</Freight><ShipName>Vins et alcools Chevalier</ShipName><ShipAddress>59 rue de l'Abbaye</ShipAddress><ShipCity>Reims</ShipCity><ShipPostalCode>51100</ShipPostalCode><ShipCountry>France</ShipCountry><LastEditDate>2009-03-20T08:21:00.19+01:00</LastEditDate><CreationDate>2009-03-20T08:21:00.517+01:00</CreationDate></Orders></NewDataSet><diffgr:before><Orders diffgr:id="Orders1" msdata:rowOrder="0" xmlns=""><OrderID>10248</OrderID><CustomerID>VINET</CustomerID><EmployeeID>5</EmployeeID><OrderDate>1996-07-04T00:00:00+02:00</OrderDate><RequiredDate>1996-08-01T00:00:00+02:00</RequiredDate><ShippedDate>1996-07-16T00:00:00+02:00</ShippedDate><ShipVia>3</ShipVia><Freight>32.38</Freight><ShipName>Vins et alcools Chevalier</ShipName><ShipAddress>59 rue de l'Abbaye</ShipAddress><ShipCity>Reims</ShipCity><ShipPostalCode>51100</ShipPostalCode><ShipCountry>France</ShipCountry><LastEditDate>2009-03-20T08:21:00.19+01:00</LastEditDate><CreationDate>2009-03-20T08:21:00.517+01:00</CreationDate></Orders></diffgr:before></diffgr:diffgram></a:_dataSet><a:_groupProgress><a:_dataSet><xs:schema id="NewDataSet" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns="" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"><xs:element name="NewDataSet" msdata:IsDataSet="true" msdata:Locale=""><xs:complexType><xs:choice minOccurs="0" maxOccurs="unbounded"><xs:element name="Orders" msdata:Locale=""><xs:complexType><xs:sequence><xs:element name="OrderID" type="xs:int" minOccurs="0"/><xs:element name="CustomerID" type="xs:string" minOccurs="0"/><xs:element name="EmployeeID" type="xs:int" minOccurs="0"/><xs:element name="OrderDate" type="xs:dateTime" minOccurs="0"/><xs:element name="RequiredDate" type="xs:dateTime" minOccurs="0"/><xs:element name="ShippedDate" type="xs:dateTime" minOccurs="0"/><xs:element name="ShipVia" type="xs:int" minOccurs="0"/><xs:element name="Freight" type="xs:decimal" minOccurs="0"/><xs:element name="ShipName" type="xs:string" minOccurs="0"/><xs:element name="ShipAddress" type="xs:string" minOccurs="0"/><xs:element name="ShipCity" type="xs:string" minOccurs="0"/><xs:element name="ShipRegion" type="xs:string" minOccurs="0"/><xs:element name="ShipPostalCode" type="xs:string" minOccurs="0"/><xs:element name="ShipCountry" type="xs:string" minOccurs="0"/><xs:element name="LastEditDate" type="xs:dateTime" minOccurs="0"/><xs:element name="CreationDate" type="xs:dateTime" minOccurs="0"/></xs:sequence></xs:complexType></xs:element></xs:choice></xs:complexType></xs:element></xs:schema><diffgr:diffgram xmlns:diffgr="urn:schemas-microsoft-com:xml-diffgram-v1" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"><NewDataSet xmlns=""><Orders diffgr:id="Orders1" msdata:rowOrder="0" diffgr:hasChanges="modified"><OrderID>10248</OrderID><CustomerID>VINET</CustomerID><EmployeeID>5</EmployeeID><OrderDate>1996-07-04T00:00:00+02:00</OrderDate><RequiredDate>1996-08-01T00:00:00+02:00</RequiredDate><ShippedDate>1996-07-16T00:00:00+02:00</ShippedDate><ShipVia>3</ShipVia><Freight>32.38</Freight><ShipName>Vins et alcools Chevalier</ShipName><ShipAddress>59 rue de l'Abbaye</ShipAddress><ShipCity>Reims</ShipCity><ShipPostalCode>51100</ShipPostalCode><ShipCountry>France</ShipCountry><LastEditDate>2009-03-20T08:21:00.19+01:00</LastEditDate><CreationDate>2009-03-20T08:21:00.517+01:00</CreationDate></Orders></NewDataSet><diffgr:before><Orders diffgr:id="Orders1" msdata:rowOrder="0" xmlns=""><OrderID>10248</OrderID><CustomerID>VINET</CustomerID><EmployeeID>5</EmployeeID><OrderDate>1996-07-04T00:00:00+02:00</OrderDate><RequiredDate>1996-08-01T00:00:00+02:00</RequiredDate><ShippedDate>1996-07-16T00:00:00+02:00</ShippedDate><ShipVia>3</ShipVia><Freight>32.38</Freight><ShipName>Vins et alcools Chevalier</ShipName><ShipAddress>59 rue de l'Abbaye</ShipAddress><ShipCity>Reims</ShipCity><ShipPostalCode>51100</ShipPostalCode><ShipCountry>France</ShipCountry><LastEditDate>2009-03-20T08:21:00.19+01:00</LastEditDate><CreationDate>2009-03-20T08:21:00.517+01:00</CreationDate></Orders></diffgr:before></diffgr:diffgram></a:_dataSet><a:_groupName>OrdersSyncTableSyncGroup</a:_groupName><a:_tablesProgress><a:SyncTableProgress><a:_conflicts/><a:_dataTable i:nil="true"/><a:_deletes>0</a:_deletes><a:_inserts>0</a:_inserts><a:_rowIndex>0</a:_rowIndex><a:_rowsApplied>1</a:_rowsApplied><a:_rowsFailed>0</a:_rowsFailed><a:_tableName>Orders</a:_tableName><a:_updates>1</a:_updates></a:SyncTableProgress></a:_tablesProgress></a:_groupProgress><a:_maxAnchor i:nil="true"/><a:_newAnchor i:nil="true"/><a:_originatorId>0</a:_originatorId></ApplyChangesResult></ApplyChangesResponse>
    Friday, March 20, 2009 8:57 AM
  • Sorry for bumping, but it would be nice to hear from someone on the framework team what the best practice is for this issue. 

    Clearly we need to return a SyncContext since it is created on the server side but needed on the client side. 

    But, sending back 2 serialized copies of the DataSet that was passed in seems like a serious waste of bandwith. Can we hope for a SyncContext baseclass without the DataSet in the future?

    Thanks,

    Andreas
    Thursday, March 26, 2009 9:29 AM
  • Hi Andhallberg,
    You are right in noticing that the SyncContext.DataSet and SyncContext.GroupProgress.Changes have the same reference to the DataSet. During serialization these references are hit twice when serializing the uber object graph and hence you see the same DataSet being serialized twice. There are a couple of ways you can workaround this.

    1. Use Preserve Object Reference option if using WCF DataContractSerializer. http://blogs.msdn.com/sowmy/archive/2006/03/26/561188.aspx. This way you serialize the object only once.
    2. If not using WCF or your serializer does not support object reference then you can set the SyncContext.DataSet property to null before sending it to the remote side. On the remote side after the event has been deserialized you can once again set the SyncContext.DataSet property to the same reference as SyncContext.GroupProgress.Changes property. This way you have the dataset serialized only once. Both the properties has got to be not null before being passed on to the Synchronized so ensure that they are correctly set post deserialization.

    Maheshwar Jayaraman
    Maheshwar Jayaraman - WCF -- http://blogs.msdn.com/dotnetremoting
    • Marked as answer by andhallberg Thursday, May 7, 2009 3:33 PM
    Tuesday, April 28, 2009 10:17 PM
    Moderator
  • Maheshwar,

    Thanks for your reply! I didn't see it until today - maybe there is someway of getting the email alerts to a gmail account...? :)

    In GetChanges, I am null:ing one of the dataset references and restoring it on the client side (I wasn't aware of the "Preserve Object References" option)
    In ApplyChanges, I do a DataSet.Clear on the server since it is never used on the client side - not by me and not by the framework, afaik. It all works fine.

    Andreas

    Thursday, May 7, 2009 3:33 PM