The application called an interface that was marshalled for a different thread - Xamarin Forms

2

I am loading some data via WCF on the loading of a given page. I then want to populate a Picker with the values once the data has loaded. However, I'm getting a cross threading problem. Here is the code:

protected async override void OnAppearing()
{
    base.OnAppearing();
    await Task.Factory.StartNew(async () => { await Initialise(); });
}

private async Task Initialise()
{
    var activities = await App.XivicServicesClient.DataAccessCalls.GetRecordsAsync<ActivityDef>(null, new ConnectionInfo(null, App.XivicServicesClient.AuthToken));

    var activityCodes = activities.Select(a => a.Code);
    foreach (var activityCode in activityCodes)
    {
        ActivityPicker.Items.Add(activityCode);
    }
}

In Windows UWP, the error I get is

Additional information: The application called an interface that was marshalled for a different thread. (Exception from HRESULT: 0x8001010E (RPC_E_WRONG_THREAD))

I guess that I am using the OnAppearing call in an unintended way, so how should I get around this? I need to make a Task Based Async WCF call when the page starts loading.

On iOS, I don't get the error.

c#
multithreading
wcf
xamarin
xamarin.forms

2 Answers

3

Your problem comes from

ActivityPicker.Item.Add(activityCode);

That line may ONLY be run on the UI thread.

You can easily fix this code with the following

protected async override void OnAppearing()
{
    base.OnAppearing();
    var activityCodes = await Task.Factory.StartNew(async () => { await Initialise(); });

    foreach (var activityCode in activityCodes)
    {
        ActivityPicker.Items.Add(activityCode);
    }
}

private async Task<List<string>> Initialise()
{
    var activities = await App.XivicServicesClient
                       .DataAccessCalls
                       .GetRecordsAsync<ActivityDef>(null, new ConnectionInfo(null, App.XivicServicesClient.AuthToken));

    return activities.Select(a => a.Code).ToList();

}

However, I really doubt that you "need" to use Task.Factory.StartNew. GetRecordsAsync already is an async method. Chances are, most of the delay in Initialise(sic) comes from the I/O latency.

Running Initialise from another thread will only INCREASE the run time, as the data needs to be marshaled across threads, along with synchronization.

Rather, try.

protected async override void OnAppearing()
{
    base.OnAppearing();

    var activities = await App.XivicServicesClient
                          .DataAccessCalls
                          .GetRecordsAsync<ActivityDef>(null, new ConnectionInfo(null, App.XivicServicesClient.AuthToken));

    foreach (var activityCode in activities.Select(a => a.Code))
    {
        ActivityPicker.Items.Add(activityCode);
    }
}
answered on Stack Overflow Feb 2, 2017 by Aron
0

The problem was simple. I'd added code that wasn't necessary, and that code was causing the bug. Here is the fixed method:

protected async override void OnAppearing()
{
    base.OnAppearing();
    await Initialise();
}
answered on Stack Overflow Feb 2, 2017 by Christian Findlay

User contributions licensed under CC BY-SA 3.0