【问题标题】:Refactoring api calls to use await重构 api 调用以使用 await
【发布时间】:2015-07-23 06:07:52
【问题描述】:

想要更新 C# Winforms 应用程序以使用 await。 该应用程序通过 SDK 调用 MYOB Accountright API。 我正在使用 Dot Net Framework 4.5.1

旧代码是这样的

public void GetItems(  CompanyFile companyFile )
        {
            var itemSvc = new ItemService(MyConfiguration, null, MyOAuthKeyService);
            string pageFilter = string.Format("$top={0}&$skip={1}&$orderby=Date desc", PageSize,
                                              PageSize * (_currentPage - 1));
            itemSvc.GetRange(MyCompanyFile, pageFilter, MyCredentials, OnComplete, OnError);
        }

        /// <summary>
        /// Method called on Async complete
        /// </summary>
        /// <param name="statusCode"></param>
        /// <param name="items"></param>
        /// <remarks></remarks>
        private void OnComplete(System.Net.HttpStatusCode statusCode,
                                PagedCollection<Item> items)
        {
            myItems = items;
        }

    /// <summary>
    /// Callback if there is an error
    /// </summary>
    /// <param name="uri"></param>
    /// <param name="ex"></param>
    /// <remarks></remarks>
    private void OnError(Uri uri, Exception ex)
    {
        Trace.WriteLine("In OnError");

        MessageBox.Show(ex.Message);
    }

我想要这样的代码

private async Task FetchItemsAsync()
{
        var itemSvc = new ItemService(MyConfiguration, null, MyOAuthKeyService);

        string pageFilter = string.Format("$top={0}&$skip={1}&$orderby=Date desc", PageSize,
                                          PageSize * (_currentPage - 1));

        itemSvc.GetRange(MyCompanyFile, pageFilter, MyCredentials, OnComplete, OnError);

        var totalPages = (int)Math.Ceiling((double)(myItems.Count  / PageSize));

        while (_currentPage < totalPages)
        {
            await LoadMore(); // how do I write this?
        }
}

我该怎么做?

[更新5]

我试过了

    private const double PageSize = 400;
    protected CancellationTokenSource MyCancellationTokenSource;
    protected CompanyFile MyCompanyFile;
    protected IApiConfiguration MyConfiguration;
    protected ICompanyFileCredentials MyCredentials;

    protected ItemService MyItemService;
    protected IOAuthKeyService MyOAuthKeyService;

    private int _currentPage = 1;
    private int _totalPages;

    public void FetchItems(CompanyFile companyFile, IApiConfiguration configuration, ICompanyFileCredentials credentials)
    {
        MyCompanyFile = companyFile;
        MyConfiguration = configuration;
        MyCredentials = credentials;
        MyCancellationTokenSource = new CancellationTokenSource();
        MyItemService = new ItemService(MyConfiguration, null, MyOAuthKeyService);
        FetchAllItemsAsync();
    }

    private async void FetchAllItemsAsync()
    {
        try
        {
            var items = new List<Item>();
            int totalPages = 0;
            do
            {
                string pageFilter = string.Format("$top={0}&$skip={1}&$orderby=Date desc", PageSize, PageSize * (_currentPage - 1));
                CancellationToken ct = MyCancellationTokenSource.Token;
                Log("About to Await GetRange");

                Task<PagedCollection<Item>> tpc = MyItemService.GetRangeAsync(MyCompanyFile, pageFilter, MyCredentials, ct, null);
                Log("About to Await GetRange B");

                PagedCollection<Item> newItems = await tpc;  // fails here

                Log("Page {0} retrieved {1} items", _currentPage, newItems.Count);
                if (totalPages == 0)
                {
                    totalPages = (int)Math.Ceiling((items.Count / PageSize));
                }
                items.AddRange(newItems.Items.ToArray());
                _currentPage++;
            }
            while (_currentPage < totalPages);
            MessageBox.Show(string.Format("Fetched {0} items", items.Count));
        }
        catch (ApiCommunicationException ex)
        {
            Log(ex.ToString());
            throw;
        }
        catch (Exception exception)
        {
            Log(exception.ToString());
            throw;
        }
    }

但是我得到一个 ValidationException

{"Encountered a validation error (http://localhost:8080/AccountRight/ab5c1f96-7663-4052-8360-81004cfe8598/Inventory/Item/?$top=400&$skip=0&$orderby=Date desc)"}
    [MYOB.AccountRight.SDK.ApiValidationException]: {"Encountered a validation error (http://localhost:8080/AccountRight/ab5c1f96-7663-4052-8360-81004cfe8598/Inventory/Item/?$top=400&$skip=0&$orderby=Date desc)"}
    base: {"Encountered a validation error (http://localhost:8080/AccountRight/ab5c1f96-7663-4052-8360-81004cfe8598/Inventory/Item/?$top=400&$skip=0&$orderby=Date desc)"}
    ErrorInformation: "Warning, error messages have not been finalised in this release and may change"
    Errors: Count = 1
    RequestId: "e573dfed-ec68-4aff-ac5e-3ffde1c2f943"
    StatusCode: BadRequest
    URI: {http://localhost:8080/AccountRight/ab5c1f96-7663-4052-8360-81004cfe8598/Inventory/Item/?$top=400&$skip=0&$orderby=Date desc}

我已将此问题交叉发布到 MYOB Support

【问题讨论】:

    标签: asynchronous myob


    【解决方案1】:

    您引用的MYOB.AccountRight.API.SDK 的最新版本已经有overloads 用于支持.NET4、.NET45 和PCL 上的异步/等待。

    Sample code 是为使用 .NET 3.5 的人创建的示例(因此没有 async/await)。另一个示例 (windows phone) 显示 action using the SDK 中的 async/await

    [更新]

    您可能会遇到与 OData 相关的异常,因为 Item 实体没有可供您过滤的 Date 字段(请参阅 docs)。

    当您捕获ApiCommunicationException(其中ApiValidationException 是一个子类)时,有一个Errors 属性可以提供更多详细信息。

    还有一个RequestId(和其他一些属性)非常有用,如果您在与云托管 API 交谈时遇到问题,需要与支持人员交谈。

    【讨论】:

    • 仅供参考:我们目前正在准备一个新版本(候选版本),其中包含更新的 nuget 参考资料,应该会在接下来的几天内提供。
    • 你有如何调用 ItemService.GetAsync 的例子吗?
    • 不使用 ItemService 但这里有一个 ContactService - 语法是相同的。事后看来,我现在可能会写得稍微不同,但如果你知道 async/await 那应该是轻而易举的事。
    • 实际上我意识到 GetRangeAsync 是我需要的......但我似乎无法让它工作。我替换了问题的更新部分以显示我的最新尝试。
    • 问题是您正在发送“$orderby=Date desc”。由于 Item 没有 Date 字段,因此您不能按它订购。尝试按实体拥有的字段排序。
    【解决方案2】:

    您可以使用 TaskCompletionSource 对象,您可以使用结果或错误回调来解决该对象。我不确定错误回调的签名是什么,因此该部分可能无法正常工作。

    private Task<PagedCollection<Item>> FetchItemsAsync()
    {
        var taskSource = new TaskCompletionSource<PagedCollection<Item>>();
    
        var itemSvc = new ItemService(MyConfiguration, null, MyOAuthKeyService);
        string pageFilter = string.Format("$top={0}&$skip={1}&$orderby=Date desc", PageSize,
                                                  PageSize * (_currentPage - 1));
        itemSvc.GetRange(
            MyCompanyFile,
            pageFilter,
            MyCredentials,
            (statusCode, items) => taskSource.TrySetResult(items), 
            (error) => taskSource => taskSource.TrySetException(error) // Not sure if this is correct signature
            );
        return taskSource.Task;
    }
    

    然后,您可以返回它创建的 Task 对象,您可以将其用于异步事物。我不太确定您要实现什么逻辑,因为您的问题不是很详细,但是您可以将该方法与 await 命令一起使用,因为它返回如下所示的 Task 对象。

    private async void FetchAllItemsAsync() 
    {
        int totalPages;
        do
        {
            items = await FetchItemsAsync()
            totalPages = (int)Math.Ceiling((double)(items.Count / PageSize));
            _currentPage++;
        } while (_currentPage < totalPages)
    
    }
    

    【讨论】:

    • 我用我对你的想法的实施更新了我的问题。但是我收到一个错误的请求错误
    • 服务器返回该异常。你能看一下异常信息,看看服务器说你的请求有什么问题吗?
    • 我该怎么做?我确实将异常粘贴到我的问题中。
    • 我认为我们在正确的轨道上stackoverflow.com/questions/15316613/…
    猜你喜欢
    • 2021-10-02
    • 1970-01-01
    • 2020-06-22
    • 2014-07-09
    • 2021-11-18
    • 2020-10-11
    • 1970-01-01
    • 2019-02-04
    • 2019-01-05
    相关资源
    最近更新 更多