none
Problems syncing a table with an Image column RRS feed

  • Question

  • Hi,

    (This is a Sync Framework question, but to set the scene, here is the background info)

    I'm currently developing a Windows Mobile Forms application that uses a local SQLCE database, and ADO.NET Sync Service for Devices V1 - this syncs up to a Windows WCF Service, running on IIS, which is reading/writing to a SQL 2008 Express database.  All is working good so far, with plenty of tables syncing back and forth.

    I've just added a new table which is quite small and has an Image column, which is used to hold a signature (small images approx 300x180 pixels). Here is a quick outline of the schema (some simple foreign key columns missed out for readability):

    FSRSignature_Key [uniqueidentifier] PRIMARY KEY NOT NULL,
    Signature_Data [image] NOT NULL,
    {concurrency/change tracking columns, etc...}

    Now, I have saved a record to this table, and the image data is stored in the Signature_Data column. I've looked at the raw data through SSMS and can see it as Hex data (approx 43KB of data). Here is a snippet:

    424D8E510100000000003600000028000000E20000007F000000
    010018000000000000000000C40E0000C40E000000000000000
    0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
    FFFFFFF...

    Now, I can re-load the data from the table and get back to an actual on-screen image, so know it is saved correctly. But when I Synchronise I get past the first few tables, get to this one and get an Exception:

    {"TargetInvocationException"}
    {"The remote server returned an unexpected response: (400) Bad Request."}

    Now, I've traced through to find the error and it is occurring in the CFClientBase.cs class, in the getReply() method at the line:

    return requestChannel.Request(msg);

    I looked at the SOAP message to check the content and was shocked to find that the Image data was incorrectly formatted and not being escaped (causing the XML to be malformed). Here is a snippet of the offending XML in the SOAP message:

    <diffgr:diffgram xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" xmlns:diffgr="urn:schemas-microsoft-com:xml-diffgram-v1">
              <NewDataSet xmlns="">
                <ReaFSRSignature diffgr:id="ReaFSRSignature1" msdata:rowOrder="0" diffgr:hasChanges="inserted">
                  <FSRSignature_Key>ec919982-b70f-45ff-bd28-730694887e80</FSRSignature_Key>
                  <FSR_Call_Num>1056</FSR_Call_Num>
                  <FSR_Num>-3</FSR_Num>
                 <Signature_Data>Qk2OUQEAAAAAADYAAAAoAAAA4gAAAH8AAAABABg
                  AAAAAAAAAAADEDgAAxA4AAAAAAAAAAAAA/////////////////////////////
                  ///////////////////////////////////////////////////////////////AAAA////////////
                  /////////////////////////////////////////////////////////////////////////////////.........

    Now, as far as I know, I don't have any control over how the Image data is handled when converting it to XML especially since this is done on the Windows Mobile client (which doesn't have Compact WCF) but this doesn't look right and I'm not sure how to fix it.

    Hours of Googling and forum searching are not turning up any suggestions.

    Can anyone help please?

    Kind Regards,
    Paul McMurray
    Wednesday, January 6, 2010 2:57 PM

Answers

  • ISSUE SOLVED: I have solved this issue and am embarassed to say that it was down to one simple omission.

    As mentioned, I host the WCF Sync Service in IIS as part of an ASP.NET website. This means that the configuration for the WCF service is within the web.config file.

    Even though I had set up the service binding element, I had failed to specify this in the endpoint element attribute. I had

    endpoint binding="basicHttpBinding" contract="IREAIISyncServiceContract"

    ...when I should have had:

    endpoint binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IREAIISyncServiceContract" contract="IREAIISyncServiceContract"

    And of course, the configuration is now being read and applied, and my syncing works.

    DOH!

    • Marked as answer by P.Mc Friday, January 8, 2010 10:37 AM
    • Edited by P.Mc Friday, January 8, 2010 11:16 AM layout
    Friday, January 8, 2010 10:37 AM

All replies

  • I've had another look at this with a fresh set of eyes and have noticed that the XML is actually well-formed - after saving it out to a file and loading it into an XML editor it certainly passes the grade.  I was thinking that the backslashes should have been escaped, but a quick look through my XML book says that this is not the case and that they are allowed.

    I also noticed something else. This is the section of XML describing the table that holds the signature data:

    <oDataSet>
            <xs:schema id="NewDataSet" xmlns="" xmlns:xs="http://www.w3.org/2001/XMLSchema" 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="ReaFSRSignature" msdata:Locale="">
                      <xs:complexType>
                        <xs:sequence>
                          ...
                          <xs:element name="Signature_Data" type="xs:base64Binary" minOccurs="0" />
                          ...

    I noticed that the XML datatype for the column is xs:base64Binary which explains why the Hex data I listed in my original post doesn't appear the same in the SOAP message. Also, the Base64 encoding used is according to RFC2045 (which is A-Z, a-z, 0-9, + and /, but also = can be used to pad out data at the end) and I've cheked that all the characters used are within this restricted character set.

    So now we can ignore my previous ramblings that the XML was not well-formed or that the binary data was not represented the same as in SQL.

    -----------------------------------------------------------------------------------------------------
    UPDATE: The MSF call to ApplyChanges() doesn't make it to the Server - the exception is being thrown on the client. This is the Exception in full:

    TargetInvocationException:
    Stacktrace:
       at System.Reflection.RuntimeMethodInfo.InternalInvoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture, Boolean verifyAccess, StackCrawlMark& stackMark)
       at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
       at System.Reflection.MethodBase.Invoke(Object obj, Object[] parameters)
       at Microsoft.Synchronization.Data.ServerSyncProviderProxy.ApplyChanges(SyncGroupMetadata groupMetadata, DataSet dataSet, SyncSession syncSession)
       at Microsoft.Synchronization.SyncAgent.UploadChanges(SyncGroupMetadata groupMetadata)
       at Microsoft.Synchronization.SyncAgent.Synchronize()
       at Tesseract.REAII.SyncClientObjects.SyncEngine.Synchronise()
       at Tesseract.REAII.SyncClientObjects.SyncEngine.Sync()
       at Tesseract.REAII.SyncClientObjects.SyncEngine._Lambda$__1()

    InnerException: "The remote server returned an unexpected response: (400) Bad Request."
    Stacktrace:
       at System.ServiceModel.Channels.HttpChannelUtilities.ValidateRequestReplyResponse(HttpWebRequest request, HttpWebResponse response, HttpChannelFactory factory, WebException responseException)
       at System.ServiceModel.Channels.HttpChannelFactory.HttpRequestChannel.HttpChannelRequest.WaitForReply(TimeSpan timeout)
       at System.ServiceModel.Channels.RequestChannel.Request(Message message, TimeSpan timeout)
       at System.ServiceModel.Channels.RequestChannel.Request(Message message)
       at Tesseract.REAII.ClientObjectsCS.CFClientBase`1.getReply(Message msg)
       at Tesseract.REAII.ClientObjectsCS.CFClientBase`1.Invoke[TREQUEST,TRESPONSE](CFInvokeInfo info, ApplyChangesRequest request)
       at Tesseract.REAII.ClientObjectsCS.REAIISyncServiceClient.ApplyChanges(ApplyChangesRequest request)
       at Tesseract.REAII.ClientObjectsCS.REAIISyncServiceClient.ApplyChanges(SyncGroupMetadata oGroupMetadata, DataSet oDataSet, SyncSession oSyncSession)
       at System.Reflection.RuntimeMethodInfo.InternalInvoke(RuntimeMethodInfo rtmi, Object obj, BindingFlags invokeAttr, Binder binder, Object parameters, CultureInfo culture, Boolean isBinderDefault, Assembly caller, Boolean verifyAccess, StackCrawlMark& stackMark)
       at System.Reflection.RuntimeMethodInfo.InternalInvoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture, Boolean verifyAccess, StackCrawlMark& stackMark)
       at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
       at System.Reflection.MethodBase.Invoke(Object obj, Object[] parameters)
       at Microsoft.Synchronization.Data.ServerSyncProviderProxy.ApplyChanges(SyncGroupMetadata groupMetadata, DataSet dataSet, SyncSession syncSession)
       at Microsoft.Synchronization.SyncAgent.UploadChanges(SyncGroupMetadata groupMetadata)
       at Microsoft.Synchronization.SyncAgent.Synchronize()
       at Tesseract.REAII.SyncClientObjects.SyncEngine.Synchronise()
       at Tesseract.REAII.SyncClientObjects.SyncEngine.Sync()
       at Tesseract.REAII.SyncClientObjects.SyncEngine._Lambda$__1()

    (Basically, the MSF is operating through two classes which were created using NetCFSvcUtil.exe: CFClientBase.cs and REAIISyncServiceClient.cs (renamed from the default LocalDataCache1Service.cs).
    -----------------------------------------------------------------------------------------------------

    -----------------------------------------------------------------------------------------------------
    2nd UPDATE:

    In my test record, I have reduced the data in the Signature_Data column to a few bytes and now the table syncs correctly. When I put it back up (i.e. load an actual signature image in) the problem reoccurs. It would seem it either doesn't like the size of the data.

    I have set the various message size attributes in the wcf service section of the web.config file (WCF service hosted in IIS) to 4000000 and also set the value of httpBinding.MaxReceivedMessageSize to int.MaxValue, but this still doesn't allow the request through.
    -----------------------------------------------------------------------------------------------------

    So I am at a loss - I've triple checked my code for the SyncTables both on the client and the server and cannot find any issues - the code for these was taken from other SyncTable code in my project which is working fine.

    Any suggestions appreciated.

    Kind Regards,
    Paul McMurray
    • Edited by P.Mc Thursday, January 7, 2010 4:00 PM another update
    Thursday, January 7, 2010 11:48 AM
  • Paul,

    After reading through your post, my initial inclination is to say that you might be better served by someone from the netcf or WCF team.  That said, I wanted to ping you before moving the thread.  Have you found something that might lead you to believe that this issue is related to MSF?  If not, let me know if you are on board with moving this post to the WCF/netcf forum.

    Regards,


    Sean Kelley
    Senior Program Manager
    Microsoft
    Friday, January 8, 2010 1:16 AM
    Moderator
  • Hi Sean,

    Thanks for the reply - I'm happy for you to do that - any help from any avenue is much appreciated!

    Kind Regards,
    Paul McMurray
    Friday, January 8, 2010 8:56 AM
  • ISSUE SOLVED: I have solved this issue and am embarassed to say that it was down to one simple omission.

    As mentioned, I host the WCF Sync Service in IIS as part of an ASP.NET website. This means that the configuration for the WCF service is within the web.config file.

    Even though I had set up the service binding element, I had failed to specify this in the endpoint element attribute. I had

    endpoint binding="basicHttpBinding" contract="IREAIISyncServiceContract"

    ...when I should have had:

    endpoint binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IREAIISyncServiceContract" contract="IREAIISyncServiceContract"

    And of course, the configuration is now being read and applied, and my syncing works.

    DOH!

    • Marked as answer by P.Mc Friday, January 8, 2010 10:37 AM
    • Edited by P.Mc Friday, January 8, 2010 11:16 AM layout
    Friday, January 8, 2010 10:37 AM
  • How do u get the soap message of the ldcMEGSyncAgent.Synchronize() method???
    Monday, March 26, 2012 11:29 PM