我已经在我的Xamarin Forms应用程序中基于Microsoft Sample提供的文档/示例实现了Azure - Offline Sync。
在提供的示例/文档中,他们使用默认的服务处理程序。
//简单的错误/冲突处理。真正的应用程序可以通过IMobileServiceSyncHandler处理各种错误,如网络状况,服务器冲突和其他错误。
因为如果Pull / Push失败,我需要实现3次重试逻辑。根据文档,我创建了一个自定义服务处理程序(IMobileServiceSyncHandler)。
请在这里找到我的代码逻辑。
public class CustomSyncHandler : IMobileServiceSyncHandler
{
public async Task<JObject> ExecuteTableOperationAsync(IMobileServiceTableOperation operation)
{
MobileServiceInvalidOperationException error = null;
Func<Task<JObject>> tryExecuteAsync = operation.ExecuteAsync;
int retryCount = 3;
for (int i = 0; i < retryCount; i++)
{
try
{
error = null;
var result = await tryExecuteAsync();
return result;
}
catch (MobileServiceConflictException e)
{
error = e;
}
catch (MobileServicePreconditionFailedException e)
{
error = e;
}
catch (MobileServiceInvalidOperationException e)
{
error = e;
}
catch (Exception e)
{
throw e;
}
if (error != null)
{
if(retryCount <=3) continue;
else
{
//Need to implement
//Update failed, reverting to server's copy.
}
}
}
return null;
}
public Task OnPushCompleteAsync(MobileServicePushCompletionResult result)
{
return Task.FromResult(0);
}
}
但我不知道如果所有3次重试失败,如何处理/恢复服务器副本。
在TODO示例中,他们根据MobileServicePushFailedException恢复它。但是当我们实现IMobileServiceSyncHandler时可用。更重要的是,如果我们包含自定义IMobileServiceSyncHandler,它将不会在PushAsync / PullAsync之后执行代码。即使是try catch也不会在任何异常的情况下触发。
try
{
await this.client.SyncContext.PushAsync();
await this.todoTable.PullAsync(
//The first parameter is a query name that is used internally by the client SDK to implement incremental sync.
//Use a different query name for each unique query in your program
"allTodoItems",
this.todoTable.CreateQuery());
}
catch (MobileServicePushFailedException exc)
{
if (exc.PushResult != null)
{
syncErrors = exc.PushResult.Errors;
}
}
// Simple error/conflict handling. A real application would handle the various errors like network conditions,
// server conflicts and others via the IMobileServiceSyncHandler.
if (syncErrors != null)
{
foreach (var error in syncErrors)
{
if (error.OperationKind == MobileServiceTableOperationKind.Update && error.Result != null)
{
//Update failed, reverting to server's copy.
await error.CancelAndUpdateItemAsync(error.Result);
}
else
{
// Discard local change.
await error.CancelAndDiscardItemAsync();
}
Debug.WriteLine(@"Error executing sync operation. Item: {0} ({1}). Operation discarded.", error.TableName, error.Item["id"]);
}
}
}
注意
在我的应用程序中,我只是尝试在任何服务器错误的情况下重试3次。我不是在寻求解决冲突。 Thant是我没有添加相同代码的原因。
如果有人遇到类似的问题并解决了,请帮忙。
曲目。
您说您不是要尝试解决冲突,但是您需要通过接受对象的服务器版本或更新客户端操作来以某种方式解决它们(可能不会告诉用户可能发生了什么)。否则,它会在每次重试操作时不断告诉您相同的冲突。
您需要具有Microsoft.WindowsAzure.MobileServices.Sync.MobileServiceSyncHandler类的子类,该类重写OnPushCompleteAsync()以处理冲突和其他错误。我们称之为SyncHandler类:
public class SyncHandler : MobileServiceSyncHandler
{
public override async Task OnPushCompleteAsync(MobileServicePushCompletionResult result)
{
foreach (var error in result.Errors)
{
await ResolveConflictAsync(error);
}
await base.OnPushCompleteAsync(result);
}
private static async Task ResolveConflictAsync(MobileServiceTableOperationError error)
{
Debug.WriteLine($"Resolve Conflict for Item: {error.Item} vs serverItem: {error.Result}");
var serverItem = error.Result;
var localItem = error.Item;
if (Equals(serverItem, localItem))
{
// Items are the same, so ignore the conflict
await error.CancelAndUpdateItemAsync(serverItem);
}
else // check server item and local item or the error for criteria you care about
{
// Cancels the table operation and discards the local instance of the item.
await error.CancelAndDiscardItemAsync();
}
}
}
初始化MobileServiceClient时,请包含此SyncHandler()的实例:
await MobileServiceClient.SyncContext.InitializeAsync(store, new SyncHandler()).ConfigureAwait(false);
阅读MobileServiceTableOperationError以查看您可以处理的其他冲突以及允许解决它们的方法。
该例外带有服务器版本的副本。因此,在我执行IMobileServiceSyncHandler
时,我只返回error.Value
,这似乎有效。
在this MSDN blog中可以找到更广泛的这种逻辑示例。
同一作者有另一个例子,他展示了如何解决冲突,支持服务器副本或客户端副本here。