Mathieson
Mathieson

Reputation: 1948

Azure Mobile Services 412 failures in Xamarin

I'm consistently getting 412 errors (PreconditionFailed) whenever trying to update an item over MobileServices. I believe this has started recently, but unfortunately the error gives me very little information.

I've setup a conflict resolver with the idea of taking the client version as the winner in the case of any conflict, as per https://azure.microsoft.com/en-us/documentation/articles/mobile-services-windows-store-dotnet-handling-conflicts-offline-data/. To do so I'm repeating the call to push. However I get the 412 failure on BOTH pushes, not just the first.

Here's my handler code . . .

    public class AzureConflictHandler : IMobileServiceSyncHandler
{
    readonly ILogger _logger;
    public AzureConflictHandler(ILogger logger){
        _logger = logger;
    }
    #region IMobileServiceSyncHandler implementation

    public Task OnPushCompleteAsync (MobileServicePushCompletionResult result)
    {
        return Task.FromResult (false);
    }

    public async Task<JObject> ExecuteTableOperationAsync (IMobileServiceTableOperation operation)
    {
        try{
            await operation.ExecuteAsync ();
            return null;
        }                 
        catch (MobileServiceConflictException ex)
        {
            _logger.HandleException (ex);
        }
        catch (MobileServicePreconditionFailedException ex)
        {
            _logger.HandleException (ex);

            //https://codemilltech.com/why-cant-we-be-friends-conflict-resolution-in-azure-mobile-services/
        }
        catch(Exception e){
            _logger.HandleException (e);
            throw;
        }

        try
        {
            //retry so we'll take the client value
            await operation.ExecuteAsync();
            return null;
        }
        catch (MobileServicePreconditionFailedException e)
        {
            _logger.HandleException(e, LogLevel.Fatal);
            return e.Value;
        }
    }

    #endregion


}

Has anyone else seen this error? I have a Version field in my dataobject, as follows . . .

        public string id { get; set; }
    public string EntityType { get; set; }
    public Guid EntityID { get; set; }
    public string EntityJSON { get; set; }
    public DateTimeOffset LastUpdatedDate { get; set; }

    [Microsoft.WindowsAzure.MobileServices.Version]
    public string Version { set; get; }

Upvotes: 1

Views: 383

Answers (1)

phillipv
phillipv

Reputation: 1717

I'm not sure why that tutorial states that, but it is currently incorrect.

Calling execute a second time, will not change the result, the local item is never automatically modified. (Technically if you only wanted last write wins (client) you could just drop version from your data model and you'd never get a 412.)

Assuming long term you want a more complex strategy here, you need to update the version of the local item, with the server's version copy.

catch (MobileServicePreconditionFailedException ex) {
  var serverValue = ex.Value;

  // Resolve in favor of our client by just using the server's version
  var item = operation.Item;
  item[MobileServiceSystemColumns.Version] = serverValue[MobileServiceSystemColumns.Version];

  // this will save the item changes to the local store so next Push()
  // will resolve this
  operation.UpdateOperationAsync(item)
  throw ex;

}

In your code above though you retry right away, but be aware that it should be in a loop, as another client could be updating it as well, etc.

In that case you can do something more like

while (some condition) {
  try {
     return await operation.ExecuteAsync();
  } catch (obileServicePreconditionFailedException ex) {
    var serverItem = ex.Value;
    operation.item[MobileServiceSystemColumns.Version] = serverValue[MobileServiceSystemColumns.Version];
  } catch ...
}

https://github.com/Azure/mobile-services-samples/blob/master/TodoOffline/WindowsUniversal/TodoOffline/TodoOffline.Shared/SyncHandler.cs#L27

Upvotes: 7

Related Questions