Eva FP
Eva FP

Reputation: 775

Async Functions making code not working

First, apologize me for the title... I haven't found any suiting my single case :P

I need to download an INI file to fill a Dictionary first of all. For this, I have this class:

public class Properties : INotifyPropertyChanged
{
    private Dictionary<string, string> _properties;

    public Properties()
    {
        _properties = new Dictionary<string, string>();
    }

    public async void Load(string uri)
    { 
        Stream input = await connection(uri);

        StreamReader rStream = new StreamReader(input);
        string line;

        while((line = rStream.ReadLine()) != null)
        {
            if(line != "")
            {
                int pos = line.IndexOf('=');
                string key = line.Substring(0, pos);
                string value = line.Substring(pos + 1);
                _properties.Add(key, value);

                Debug.WriteLine("Key: " + key + ", Value: " + value);
            }
        }

        Debug.WriteLine("Properties dictionary filled with " + _properties.Count + " items.");
    }

    public async Task<Stream> connection(string uri)
    {

        var httpClient = new HttpClient();

        Stream result = Stream.Null;

        try
        {
            result = await httpClient.GetStreamAsync(uri);

        }
        catch (Exception ex)
        {
            Debug.WriteLine(ex.Message);
            Debug.WriteLine(ex.HResult);
        }
        return result;
    }

    public string getValue(string key)
    {
        string result = "";
        try
        {
            result = _properties[key];
        }
        catch(Exception ex)
        {
            Debug.WriteLine(ex.Message);
            Debug.WriteLine(ex.HResult);
            result = "Not found";
        }
        return result;
    }

    public event PropertyChangedEventHandler PropertyChanged;

    public void RaisePropertyChanged([CallerMemberName]string propertyName = "")
    {
        var Handler = PropertyChanged;
        if (Handler != null)
            Handler(this, new PropertyChangedEventArgs(propertyName));
    }
}

}

Mainly, the Dictionary contains a Key and a URL to download XML files to each page of the application.

The MainPage, which is the one that is gonna be fill has this code:

    public MainPage()
    {
        this.InitializeComponent();

        //Properties dictionary filling
        prop = new Properties();
        prop.Load("URL");

        tab = new Bars.TopAppBar();
        bab = new Bars.BottomAppBar();

        tABar = this.topAppBar;
        actvt = this.Activity;
        bABar = this.bottomAppBar;

        //Constructor of the UserControl
        act = new Home(this, prop);

    }

The constructor of the UserControl uses the MainPage as a Callback and the class Properties to look for the URL to download the XML file.

What happens is that, as Properties.Load() is an asynchronous method, is called, then the remaining lines are executed and when the program finishes, then goes back to Load() and fills the Dictionary. As the Home constructor depends on a Value of Properties I get an Exception.

I have tried to create async voids assigning different priorities to force the program to run first one thing and then the remaining, but it hasn't worked either.

So, summarizing, I need to make sure that Properties is filled in the first place, anyone knows how to do it?

Thanks in advance!

Upvotes: 0

Views: 119

Answers (1)

razor118
razor118

Reputation: 473

Eva if you want to wait until the Load method is finished you have to change this method to return a Task.

public async Task LoadAsync(string uri)...

Better if you put your code in LoadedEventHandler of your page and make this method async. After that you will be able to await Properties.Load method.

If you want to call this method in constructor you can do it like this:

Task.Run(async () =>{
var task = p.LoadAsync().ConfigureAwait(false);
await task;
}).Wait()

But you have to be aware that deadlock can appear if in LoadAsync method will be await without disabled context switching (ConfigureAwait).

Upvotes: 2

Related Questions