locked
Error during Db sync across WCF Channel. RRS feed

  • Question

  • I am trying to configure Sync to use a custom WCF channel and I am getting an error

    "Object of type 'System.String[]' cannot be converted to type 'System.Collections.ObjectModel.Collection'1[System.String]'

     

    1) I can sync the table when not using the WCF channel and the table does not exist.

    2) I can not sync the table when using the WCF channel and the table does not exist

    3) I can sync the table when using the WCF channel and the table does exisit ( created using step 1)

    4) I have the exact same code seen in DemoServerSyncProvider() on the server and it does the update the tables correctly when modtime, addtime have changed.

     

    5) I am switch between using the WCF channel by removing 

    this.RemoteProvider = new DemoServerSyncProvider();

    and adding

    CustomChannelFactory demo = new CustomChannelFactory();

    this.RemoteProvider = new ServerSyncProviderProxy(demo);

     I am using a channel factory. Not sure if that matters.

     

    6) Call Stack

      mscorlib.dll!System.RuntimeType.CheckValue(object value, System.Reflection.Binder binder, System.Globalization.CultureInfo culture, System.Reflection.BindingFlags invokeAttr) + 0x313 bytes 


      mscorlib.dll!System.Reflection.MethodBase.CheckArguments(object[] parameters = {object[2]}, System.Reflection.Binder binder = null, System.Reflection.BindingFlags invokeAttr = Default, System.Globalization.CultureInfo culture = null, System.Signature sig = {System.Signature}) + 0xc8 bytes 


      Microsoft.Synchronization.Data.dll!Microsoft.Synchronization.Data.ServerSyncProviderProxy.GetSchema(System.Collections.ObjectModel.Collection<string> tableNames = Count = 1, Microsoft.Synchronization.Data.SyncSession syncSession = {Microsoft.Synchronization.Data.SyncSession}) + 0x124 bytes 


      Microsoft.Synchronization.Data.dll!Microsoft.Synchronization.SyncAgent.InitClientSchema() + 0x346 bytes 


      Microsoft.Synchronization.Data.dll!Microsoft.Synchronization.SyncAgent.Synchronize() + 0x178 bytes 

    Thank you in advance for any assistance.  

     

    public class DemoSyncAgent : SyncAgent

    {

    public DemoSyncAgent()

    {

    //Instantiate a client synchronization provider and specify it

    //as the local provider for this synchronization agent.

    this.LocalProvider = new ClientSyncProvider();

    //Instantiate a server synchronization provider and specify it

    //as the remote provider for this synchronization agent.

    this.RemoteProvider = new DemoServerSyncProvider();

     

    SyncGroup demoSyncGroup = new SyncGroup("sometable");

    SyncTable demoSyncTable = new SyncTable("sometable");

    demoSyncTable.CreationOption = TableCreationOption.DropExistingOrCreateNewTable;

    demoSyncTable.SyncDirection = SyncDirection.DownloadOnly;

    demoSyncTable.SyncGroup = demoSyncGroup;

    this.Configuration.SyncTables.Add(demoSyncTable);

    }

    }

    public class DemoServerSyncProvider : DbServerSyncProvider

    {

    public DemoServerSyncProvider()

    {

    Utility util = new Utility();

    SqlConnection serverConn = new SqlConnection(util.ServerConnString);

    this.Connection = serverConn;

    SqlCommand selectNewAnchorCommand = new SqlCommand();

    string newAnchorVariable = "@" + SyncSession.SyncNewReceivedAnchor;

    selectNewAnchorCommand.CommandText =

    "SELECT " + newAnchorVariable + " = CURRENT_TIMESTAMP";

    selectNewAnchorCommand.Parameters.Add(newAnchorVariable, SqlDbType.DateTime);

    selectNewAnchorCommand.Parameters[newAnchorVariable].Direction = ParameterDirection.Output;

    selectNewAnchorCommand.Connection = serverConn;

    this.SelectNewAnchorCommand = selectNewAnchorCommand;

    SyncAdapter demoSyncAdapter = new SyncAdapter("sometable");

    SqlCommand demoIncrInserts = new SqlCommand();

    demoIncrInserts.CommandText =

    "SELECT *" +

    "FROM sometable " +

    "WHERE (addtime > @sync_last_received_anchor " +

    "AND addtime <= @sync_new_received_anchor)";

    demoIncrInserts.Parameters.Add("@" + SyncSession.SyncLastReceivedAnchor, SqlDbType.DateTime);

    demoIncrInserts.Parameters.Add("@" + SyncSession.SyncNewReceivedAnchor, SqlDbType.DateTime);

    demoIncrInserts.Connection = serverConn;

    demoSyncAdapter.SelectIncrementalInsertsCommand = demoIncrInserts;

    this.SyncAdapters.Add(demoSyncAdapter);

    SqlCommand demoIncrUpdate = new SqlCommand();

    demoIncrUpdate.CommandText =

    "SELECT * " +

    "FROM sometable " +

    "WHERE (modtime > @sync_last_received_anchor " +

    "AND modtime <= @sync_new_received_anchor " +

    "AND NOT (addtime > @sync_last_received_anchor))";

    demoIncrUpdate.Parameters.Add("@" + SyncSession.SyncLastReceivedAnchor, SqlDbType.DateTime);

    demo1IncrUpdate.Parameters.Add("@" + SyncSession.SyncNewReceivedAnchor, SqlDbType.DateTime);

    demoIncrUpdate.Connection = serverConn;

    demoSyncAdapter.SelectIncrementalUpdatesCommand = demoIncrUpdate;

    }

    }

     

    • Moved by Max Wang_1983 Friday, April 22, 2011 7:57 PM forum consolidation (From:SyncFx - Microsoft Sync Framework Database Providers [ReadOnly])
    Tuesday, January 15, 2008 8:31 PM

Answers

  •  

    I know I have to deal with such tweak with web service, but nothing needs to be changed with WCF even if the client side is with string [], not a string collection.

     

    not sure why this is different from your findings.

     

    thanks

    yunwen

    Saturday, January 19, 2008 8:05 PM
    Moderator

All replies

  •  

    are you adding the WCF service to the client project from VS or you just use the code generated from svcutil.exe ?

     

    can you take a look at the generated GetSchema() method on the generated service.cs file ? Is the tableNames passed in as string [] or collection of strng ?

     

    thanks

    yunwen

    Tuesday, January 15, 2008 10:09 PM
    Moderator
  •  

    Yunwen,

    The code is being added to the project from VS. The tableNames are being passed in as a Collection<string>.

     

    Service

    public partial class Service : IService

    {

    private DemoServerSyncProvider _serverSyncProvider = new DemoServerSyncProvider();

     

    public SyncContext ApplyChanges(SyncGroupMetadata groupMetadata, DataSet dataSet, SyncSession syncSession)

    {

    return this._serverSyncProvider.ApplyChanges(groupMetadata, dataSet, syncSession);

    }

    public SyncContext GetChanges(SyncGroupMetadata groupMetadata, SyncSession syncSession)

    {

    return this._serverSyncProvider.GetChanges(groupMetadata, syncSession);

    }

    public SyncSchema GetSchema(Collection<string> tableNames, SyncSession syncSession)

    {

    return this._serverSyncProvider.GetSchema(tableNames, syncSession);

    }

     

    public SyncServerInfo GetServerInfo(SyncSession syncSession)

    {

    return this._serverSyncProvider.GetServerInfo(syncSession);

    }

    }

     

    public class DemoServerSyncProvider : DbServerSyncProvider

    {

    public demoServerSyncProvider()

    {

    Utility util = new Utility();

    SqlConnection serverConn = new SqlConnection(util.ServerConnString);

    this.Connection = serverConn;

    SqlCommand selectNewAnchorCommand = new SqlCommand();

    string newAnchorVariable = "@" + SyncSession.SyncNewReceivedAnchor;

    selectNewAnchorCommand.CommandText =

    "SELECT " + newAnchorVariable + " = CURRENT_TIMESTAMP";

    selectNewAnchorCommand.Parameters.Add(newAnchorVariable, SqlDbType.DateTime);

    selectNewAnchorCommand.Parameters[newAnchorVariable].Direction = ParameterDirection.Output;

    selectNewAnchorCommand.Connection = serverConn;

    this.SelectNewAnchorCommand = selectNewAnchorCommand;

    SyncAdapter DemoSyncAdapter = new SyncAdapter("sometable");

    SqlCommand DemoIncrInserts = new SqlCommand();

    DemoIncrInserts.CommandText =

    "SELECT * " +

    "FROM sometable " +

    "WHERE (addtime > @sync_last_received_anchor " +

    "AND addtime <= @sync_new_received_anchor)";

    DemoIncrInserts.Parameters.Add("@" + SyncSession.SyncLastReceivedAnchor, SqlDbType.DateTime);

    DemoIncrInserts.Parameters.Add("@" + SyncSession.SyncNewReceivedAnchor, SqlDbType.DateTime);

    DemoIncrInserts.Connection = serverConn;

    DemoSyncAdapter.SelectIncrementalInsertsCommand = DemoIncrInserts;

    this.SyncAdapters.Add(DemoSyncAdapter);

    SqlCommand DemoIncrUpdate = new SqlCommand();

    DemoIncrUpdate.CommandText =

    "SELECT *" +

    "FROM sometable " +

    "WHERE (modtime > @sync_last_received_anchor " +

    "AND modtime <= @sync_new_received_anchor " +

    "AND NOT (addtime > @sync_last_received_anchor))";

    DemoIncrUpdate.Parameters.Add("@" + SyncSession.SyncLastReceivedAnchor, SqlDbType.DateTime);

    DemoIncrUpdate.Parameters.Add("@" + SyncSession.SyncNewReceivedAnchor, SqlDbType.DateTime);

    DemoIncrUpdate.Connection = serverConn;

    DemoSyncAdapter.SelectIncrementalUpdatesCommand = DemoIncrUpdate;

    }

     

     

     

    Wednesday, January 16, 2008 12:51 PM
  • I do have a question concerning the ServerSyncProviderProxy Method in Synchronization.Data.

     

    public override SyncSchema GetSchema(Collection<string> tableNames, SyncSession syncSession)
    {
        MethodInfo method = this._serviceProxy.GetType().GetMethod("GetSchema", BindingFlags.Public | BindingFlags.Instance);
        if (method == null)
        {
            throw SyncExpt.MethodNotFoundError("GetSchema");
        }
        string[] strArray = new string[tableNames.Count];
        for (int i = 0; i < tableNames.Count; i++)
        {
            strArray[i] = tableNames[i];
        }
        return (SyncSchema) method.Invoke(this._serviceProxy, new object[] { strArray, syncSession });
    }
    It looks like the managed code is invoking the the method with strArray, which is a string[] and not a collection<string>
    Could this be the issue?
    Thank you again.
     
     
    Wednesday, January 16, 2008 1:09 PM
  •  

    To patch this issue. I changed my Data contracts to take a string[], and then on the server side I converted it to Collection<string>  before calling the GetSchema code. It would appear that this is a bug in how the proxy method is invoked in the managed code.

     

    Wednesday, January 16, 2008 2:42 PM
  •  

    I know I have to deal with such tweak with web service, but nothing needs to be changed with WCF even if the client side is with string [], not a string collection.

     

    not sure why this is different from your findings.

     

    thanks

    yunwen

    Saturday, January 19, 2008 8:05 PM
    Moderator
  •  

    Are you using the WCF channel factory to create the channel on the fly. That might be a difference in the implementation. What has me puzzled is why in the ServerSyncProviderPoxy is the managed code changing the Collection<string> to a strArray.

     

      return (SyncSchema) method.Invoke(this._serviceProxy, new object[] { strArray, syncSession });

     

    This is calling the WCF proxy call with a strArray? not a Collection<string> This seems like a bug to me, or have I missed the boat.


    Thanks,

    James

     

    Tuesday, January 22, 2008 7:52 PM
  • Hi,

     

    Am hoping to get answer for my post here.

     

    I am using WCF directly with all the getschema, getchanges are the operation contracts directly. As i am using netTcpBinding, i am hosting my WCF service as a windows service and not in IIS.

     

    I am getting the same error when my synchronize method is called. Can someone please help me to overcome the issue.

     

    I tested WCF behaviour passing Collection<string> and returing SyncSchema also in a test wcf service. But in my sync scenario its not working.

     

    thanks a lot,

    Rathi

     

    Tuesday, April 22, 2008 12:37 PM
  •  

    Hello Rathi,

     

    The probelm I had was that the Manage code

    public override SyncSchema GetSchema(Collection<string> tableNames, SyncSession syncSession)
    {
       
    MethodInfo method = this._serviceProxy.GetType().GetMethod("GetSchema", BindingFlags.Public | BindingFlags.Instance);
        if (
    method == null)
        {
            throw
    SyncExpt.MethodNotFoundError("GetSchema");
        }
       
    string[] strArray = new string[tableNames.Count];
        for (
    int i = 0; i < tableNames.Count; i++)
        {
           
    strArray[i] = tableNames[i];
        }
        return (
    SyncSchema) method.Invoke(this._serviceProxy, new object[] { strArray, syncSession });
    }

    Returns a strArray. So I had to Mod the Service contract to take a strArray as a Param.  Then it works.

    Once inside the Service method I had to convert it back to a Collection<string>.  I hope that makes sense.

     

    Sevice Method

    public SyncSchema GetSchema(Object[] tableNames, SyncSession syncSession)

    {

    Collection<string> tableCollection = new Collection<string>();

    foreach (Object x in tableNames)

    {

    tableCollection.add(x.ToSting());

    }

     

    return this._serverSyncProvider.GetSchema(tableCollection, syncSession);

    }

     

    This is from memory,  and the code is just to give you an idea. May contain errors.

     

    Hope it helps.

    James.

     

     

     

    Wednesday, April 23, 2008 10:43 PM
  • Hi James,

    This is the exact same error i was getting.

     

    Correct me if i am wrong, the contract method GetSchema(Collection<string> tableNames, SyncSession syncSession)

    is called during actual sync (i mean when Synchronize() is invoked).

     

    Can we change the method signature of GetSchema() then?

     

    Thanks again,

    Rathi

     

     

     

     

     

    Thursday, April 24, 2008 3:17 AM
  • Hi James,

     

    Can you please post your complete code here? I am struck here. Though my contract in the WCF service takes object array for GetSchema(), the SyncAgent.Synchronize() method passes String collection which is failing in my case.

     

    Thanks,

    Rathi

     

    Thursday, April 24, 2008 10:16 AM
  • Rathi,

    I wish I could place the complete code for you but I no longer have access to it.  I will try to explain the fix a little better.

    The problem is that in the manage code, Microsoft calls  return (SyncSchemamethod.Invoke(this._serviceProxynew object[] { strArraysyncSession });

    This is making the call to the Service, and is doing it with an Object[], not a Collection<String> like all the examples show online. 

    So what you have to do is setup your service to take the Object[] as the param, now in the file where your service methods are you should have a private instance of a sync Provider.  in my example this is called _serverSyncProvider

    But before you can call the GetSchema method you will need to convert your object[] to a Collection<string>  which is the foreach statement I used. Syntax might need some work I did not check it. 

    so that is where comes in ( Service Method)

    public SyncSchema GetSchema(Object[] tableNames, SyncSession syncSession)

    {

    Collection<string> tableCollection = new Collection<string>();

    foreach (Object x in tableNames)

    {

    tableCollection.add(x.ToSting()); //converting object[] to Collection<string>

    }

     

    return this._serverSyncProvider.GetSchema(tableCollection, syncSession);

    }



    This is the article that I used to make sure I had the Sync stuff setup correctly.

    http://msdn2.microsoft.com/en-us/library/bb902831(SQL.100).aspx



    I let me know if I can help some more. I am sorry I do not have the complete code to share. 

    Thanks,
    James


    Thursday, April 24, 2008 1:32 PM
  • Hi James,

     

    Thanks for your help. But still i am not able to solve it as i am not calling the service method GetSchema() from anywhere in my sync code, as SyncAgent.Synchronize() takes care automatically once i set the

     

    I am attaching the code snippet here. Please see anything you could do for me.

     

    My service contract and its implementation

     

    [ServiceContract()]

    public interface IInterService

    {

    [OperationContract()]

    SyncServerInfo GetServerInfo(SyncSession session);

    [OperationContract()]

    SyncSchema GetSchema(Collection<string> tableNames, SyncSession session);

    [OperationContract()]

    SyncContext GetChanges(SyncGroupMetadata groupMetadata, SyncSession syncSession);

    [OperationContract()]

    SyncContext ApplyChanges(SyncGroupMetadata groupMetadata, DataSet dataSet, SyncSession syncSession);

    }

     

    public class InterService : IInterService

    {

    ServerSyncProviderDB _serverProvider = new ServerSyncProviderDB(HBOS.Util.SyncService.HBOSUtilities.ServerConnectionString);

    public SyncServerInfo GetServerInfo(SyncSession session)

    {

    return _serverProvider.GetServerInfo(session);

    }

    public SyncSchema GetSchema(Collection<string> tableNames, SyncSession session)

    {

    //Collection<string> tableCollection = new Collection<string>();

    //foreach (object tableName in tableNames)

    //{

    // tableCollection.Add(tableName.ToString());

    //}

    return _serverProvider.GetSchema(tableNames, session);

    }

    public SyncContext GetChanges(SyncGroupMetadata groupMetadata, SyncSession syncSession)

    {

    return _serverProvider.GetChanges(groupMetadata, syncSession);

    }

    public SyncContext ApplyChanges(SyncGroupMetadata groupMetadata, DataSet dataSet, SyncSession syncSession)

    {

    return _serverProvider.ApplyChanges(groupMetadata, dataSet, syncSession);

    }

    }

     

    --------------

    My Sync code:

    IInterviewService objInterviewService = new ChannelFactory<IInterviewService>("HBOSInt").CreateChannel();

    syncAgent.RemoteProvider = new ServerSyncProviderProxy(objInterviewService);

    .......

    SyncStatistics syncstatus = syncAgent.Synchronize();

     

    the other code remains same. Here no where i am calling the GetSchema() as syncAgent.Synchronize() will take care of calling it (i believe so, as that is the case in asmx).

     

    So where will i call the getschema method to take string array. Hope you are able to get my problem.

     

    Thanks again James,

    Rathi

    Thursday, April 24, 2008 3:16 PM
  • Hello Rathi,

    If you change this

     

    public SyncSchema GetSchema(Collection<string> tableNames, SyncSession session)

     

    To

     

    public SyncSchema GetSchema(Object[] tableNames,SyncSession session)

     

    If you put a break point in this service method after you make the change you will see that it is now being called. The reason is because when you make the call to Syncronize() from the client with a proxy it runs the following code behind the scene

     

    *******************************************************

    This was viewed in Reflector.

    public override SyncSchema GetSchema(Collection<string> tableNames, SyncSession syncSession)
    {
       
    MethodInfo method = this._serviceProxy.GetType().GetMethod("GetSchema", BindingFlags.Public | BindingFlags.Instance);
        if (
    method == null)
        {
            throw
    SyncExpt.MethodNotFoundError("GetSchema");
        }
       
    string[] strArray = new string[tableNames.Count];
        for (
    int i = 0; i < tableNames.Count; i++)
        {
           
    strArray[i] = tableNames[i];
        }
        return (
    SyncSchema) method.Invoke(this._serviceProxy, new object[] { strArray, syncSession });
    }

    **********************************************************************

    This line here is invoking your proxy.GetSchema(object[], syncSession)

    return (SyncSchema) method.Invoke(this._serviceProxy, new object[] { strArray, syncSession });

     

    SO if you do not change your sig of the Service call to take a Object[], syncSession it will not match and there for it will never be called.

     

    Thanks,

    James

     

    Friday, April 25, 2008 12:42 AM
  • Hello James,

     

    I don't know but its been a challenge for me.

     

    When i change the service method with object[] instead collection, its throwing the following exception.

     

    The message with Action 'http://tempuri.org/IInterviewService/GetSchema' cannot be processed at the receiver, due to a ContractFilter mismatch at the EndpointDispatcher. This may be because of either a contract mismatch (mismatched Actions between sender and receiver) or a binding/security mismatch between the sender and the receiver.  Check that sender and receiver have the same contract and the same binding (including security requirements, e.g. Message, Transport, None).

     

    I have the same contract signature at the service and client end as well.

     

    Any help pls?

     

    Thanks,

    Rathi

     

    Friday, April 25, 2008 8:46 AM
  • When you change it back to collection<string> and place a break point in the service, does it get called when you Syncronize. Then the problem is not the same. the issue I had was that the managed code was invoking the method with object[]. What is on the stack during the call?


    Friday, April 25, 2008 2:03 PM
  • Hello James,

     

    I got the issue resolved. I had different interface names (service contract) in the client and the server end.

     

    thanks a lot for your help.

     

    Rathi

     

    Friday, April 25, 2008 2:09 PM
  • Great to hear.

     

     

     

    Friday, April 25, 2008 7:20 PM