Microsoft.Azure.Mobile 客户端 - 使用自定义 IMobileServiceSyncHandler 处理服务器错误 - Xamarin Forms

Microsoft.Azure.Mobile Client - Handling Server Error using custom IMobileServiceSyncHandler - Xamarin Forms

我已根据 Microsoft Sample 在我的 Xamarin Forms 应用程序中提供的文档/示例实现了 Azure - 离线同步。

在提供的示例/文档中,他们使用默认的服务处理程序。

// 简单的 error/conflict 处理。一个真正的应用程序将通过 IMobileServiceSyncHandler 处理各种错误,如网络条件、服务器冲突和其他错误。

因为如果拉/推失败,我需要实施重试逻辑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 次以防出现任何服务器错误。我不是要解决冲突。这就是我没有添加相同代码的原因。

如果有人遇到类似问题并解决了,请帮忙。

斯蒂兹

异常带有服务器版本的副本。因此,在我实施 IMobileServiceSyncHandler 时,我只是 return error.Value 并且这似乎有效。

可以在 this MSDN blog 中找到这种逻辑的更广泛的示例。

同一作者还有另一个例子,他展示了如何解决有利于服务器副本或客户端副本的冲突,here

你说你不是要解决冲突,但你需要通过接受对象的服务器版本或更新客户端操作来以一种或另一种方式解决它们(也许不告诉用户发生了什么) .否则它会在每次重试操作时不断告诉您相同的冲突。

您需要 Microsoft.WindowsAzure.MobileServices.Sync.MobileServiceSyncHandler class 的子class,它会覆盖 OnPushCompleteAsync() 以处理冲突和其他错误。让我们调用 class 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 以了解您可以处理的其他冲突及其解决冲突的方法。