Search code examples
asynchronousmyob

Refactoring api calls to use await


want to update a C# Winforms application to use await. The application calls the MYOB Accountright API via an SDK. I am using Dot Net Framework 4.5.1

The old code is like this

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);
    }

I want code something like this

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?
        }
}

How do I do that?

[Update5]

I tried

    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;
        }
    }

However I get a 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}

I have cross posted this problem to MYOB Support


Solution

  • The latest version of the MYOB.AccountRight.API.SDK you are referencing already has overloads for supporting async/await on .NET4, .NET45 and PCL.

    The Sample code was created as an example for someone using .NET 3.5 (hence no async/await). Another sample (windows phone) shows async/await in action using the SDK

    [Update]

    You are probably getting an OData related exception as the Item entity does not have a Date field for which you can filter by (see docs).

    When you catch an ApiCommunicationException (of which ApiValidationException is a subclass) there is an Errors property that provides more detail.

    There is also a RequestId (and some other properties) which are very useful should you need to talk to the support guys if you have issues talking to the cloud hosted API.