Reputation: 1948
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
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 ...
}
Upvotes: 7