locked
Conflict "Creation" (Server Reject/Client Revert) Based on Custom Criteria RRS feed

  • Question

  • Hi

    I've been trying to modify the generated _insert/_update/_delete stored procedures to reject certain changes from clients at the server based on custom rules - and then overwrite these changes at the client. Using RAISERROR in the stored procedures works up to a point in that the changes are not applied at the server and the ApplyChangeFailed event is raised with the DBConflictType of ErrorsOccurred. The problem is that the conflict is not replaced at the client (as with conflicting updates for example) and the client will keep sending the same change on subsequent synchronizations. Any suggestions on how to achieve this?

     

    Additional Information:

    I'm working with a combination of the n-tier (e.g. http://code.msdn.microsoft.com/Release/ProjectReleases.aspx?ProjectName=sync&ReleaseId=3762) and dynamic filtering (e.g. http://jtabadero.spaces.live.com/blog/cns!BF49A449953D0591!1187.entry and http://social.microsoft.com/Forums/en/syncdevdiscussions/thread/4af321b6-4678-4620-af46-98c560cc2bc6) approaches. A custom DBSyncProivder is used on the server (to allow for additional parameters to be added to the stored procedures) and the client uses the SqlSyncProvider. ApplyChangeFailed never seems to fire on the client (even for regular update conflicts) - not sure if this is because it is not the destination (it is initiating the synchronization), the n-tier scenario or if I'm making a mistake somewhere.

    Wednesday, May 26, 2010 3:25 PM

Answers

  • I would imagine this behavior as legit since ApplyChangeFailed raised on the server means that server did not "learn" the change. So when next time client sends over the changes to the server, it determines to send the change again as server's knowledge that client received for change detection does not include the rejected change.

     

    If your desired behavior is that server rejects certain changes from client and not see them again, two things are required:

    1) ApplyChangeFailed should not be raised on the server. This would prevent client from resending the change.

    2) Version for data-rows that you want to send to the client to overwrite the change server rejected needs to be bumped up. This is needed since next time server sends the client any changes, client would get these overwrites as has not seen these new versions.

    (on a side note, (1) and (2) indicate a scenario quite similar to server detecting a conflict with the incoming client change and resolving it in favor of server's version of data. This leads to server sending client the data it wants to see at the client and client not resending the data that server rejected ever again. However, from your description it does not look like you have conflict scenario on the server, do you?)

    One way you could do (1) is hook up to ApplyingChange event. This event allows you to see the change in the dataset that's being applied. You could remove the change from the dataset which in turn won't raise ApplyChangeFailed event. This is also cleaner than modifying the generated store procs. This will ensure client not resending the rejected change again to the server.

    For client to now get the compensating change (overwrite), you need to apply a dummy update on the server. This update should just touch the row without changing any data. This will bump up the version for the row(s) involved and client would get these updates on next download from the server. However beware, this dummy update would flow ALL the clients, not just the client you intend to send the update to.

    You could code up work around for sending the dummy update to only one specific client, but that requires you keep track of overriding updates for specific clients in a side table. And hooking up to ChangeSelection events (likely the ChangeSelected event). Once all the changes are selected on the server, you can include changes from the side table for this specific client.

    Hope this gives you some leads to work with.

    Thanks,
    Sameer

     

     

     

    Wednesday, May 26, 2010 6:58 PM
  • Instead of the using SP to reject changes on server, you may want to register for ApplyingChangeEvent and modify the DataSet available in this event.

     

    Thanks,


    Ann Tang
    Friday, June 4, 2010 11:07 PM

All replies

  • I would imagine this behavior as legit since ApplyChangeFailed raised on the server means that server did not "learn" the change. So when next time client sends over the changes to the server, it determines to send the change again as server's knowledge that client received for change detection does not include the rejected change.

     

    If your desired behavior is that server rejects certain changes from client and not see them again, two things are required:

    1) ApplyChangeFailed should not be raised on the server. This would prevent client from resending the change.

    2) Version for data-rows that you want to send to the client to overwrite the change server rejected needs to be bumped up. This is needed since next time server sends the client any changes, client would get these overwrites as has not seen these new versions.

    (on a side note, (1) and (2) indicate a scenario quite similar to server detecting a conflict with the incoming client change and resolving it in favor of server's version of data. This leads to server sending client the data it wants to see at the client and client not resending the data that server rejected ever again. However, from your description it does not look like you have conflict scenario on the server, do you?)

    One way you could do (1) is hook up to ApplyingChange event. This event allows you to see the change in the dataset that's being applied. You could remove the change from the dataset which in turn won't raise ApplyChangeFailed event. This is also cleaner than modifying the generated store procs. This will ensure client not resending the rejected change again to the server.

    For client to now get the compensating change (overwrite), you need to apply a dummy update on the server. This update should just touch the row without changing any data. This will bump up the version for the row(s) involved and client would get these updates on next download from the server. However beware, this dummy update would flow ALL the clients, not just the client you intend to send the update to.

    You could code up work around for sending the dummy update to only one specific client, but that requires you keep track of overriding updates for specific clients in a side table. And hooking up to ChangeSelection events (likely the ChangeSelected event). Once all the changes are selected on the server, you can include changes from the side table for this specific client.

    Hope this gives you some leads to work with.

    Thanks,
    Sameer

     

     

     

    Wednesday, May 26, 2010 6:58 PM
  • Hi Sameer. Thanks a lot for your reply and these are some good leads.

     

    There would seem to be 2 requirements as you suggested. I forgot to mention that if the stored procedure does not call RAISERROR, but simply omits making the change, then the client keeps sending the change over and over again. This can be seen in the context dataset in ApplyingChanges on the server. In this case, ApplyChangeFailed doesn't fire though. I assume this is because of point (2).

     

    You are correct that there is no conflict scenario on the server as such, business rules dictate that changes need to be prevented in certain cases. A similar kind example would be something like the supplier cannot edit the price once their customer has accepted their offer. I am a little surprised that the ability to reject changes at the server and then revert them on the client to keep consistency is not part of the framework - perhaps this will be added in future.

     

    I like the idea of dummy updates on the server and the downside that these will be sent out to all clients shouldn't be much of a problem in this case. I can see it becoming a little more complicated, such as if a client insert is declined at the server and needs to be overwritten at the client, it will need to be inserted and then deleted at the server. I'll look more closely at what the generated triggers do and see if it will be easier to work with the tracking table in some cases. I guess it will also be possible to work with the tracking tables in ApplyingChanges by writing some data access code for those tables - or go back to working in the stored procs, which is a little messier.

    I would also like to know under what circumstances (if any) could I expect ApplyChangeFailed to fire at the client - or can this not happen with all clients only synchronizing with the central server? Otherwise thanks - let me know if any other ideas have come to mind.

     

    [I have marked the query as unanswered for now (in case you will not be alerted to the response otherwise) and will mark either your first or next response later on.]

     

     

     

     

    Thursday, May 27, 2010 8:02 AM
  • Instead of the using SP to reject changes on server, you may want to register for ApplyingChangeEvent and modify the DataSet available in this event.

     

    Thanks,


    Ann Tang
    Friday, June 4, 2010 11:07 PM