1

I have a page (Mainpage) and SQLite database. I need to reed SQLite database first, then Update my page controls.
This is my page code:

public MainPage()
    {           
        this.InitializeComponent();

        // Get SQLite and language setting
        SQLiteData(); 

        // Update controls in page
        GetConnectionList(); 
    }

This is the code of SQLiteData() :

public async void SQLiteData()
    {
        conn = new SQLiteAsyncConnection("Setting.db");
        await conn.CreateTableAsync<SettingTable>();
        await conn.CreateTableAsync<DataPlanTable>();

        ....
     }

because I use 'await' in SQLiteData(), debugger go to run GetConnectionList() without finishing and get data from SQLite so I get error in my app.
How Can I solve that? Is it possible to help me?
Thanks.

Chris Schaller
  • 13,704
  • 3
  • 43
  • 81
  • You should really look into design patterns. In this case MVVM and the service and repository patterns. – user9993 Sep 20 '15 at 11:50

2 Answers2

1

The key is that you declared SQLiteData as async, but in your MainPage constructor you have not 'waited' for the SQLiteData task to complete.

You should await for SQLiteData to complete, BUT you cannot do that easily inside of the constructor... and nor should you. Can constructors be async?

Please move time consuming and async tasks out of the constructor and into other methods in your class, a key reason is so that you can implement some better error handling routines and you could re-execute the connection logic if the connection fails the first time.

public async InitialiseDataConnections()
{           
    // Get SQLite and language setting
    await SQLiteData(); 

    // Update controls in page
    GetConnectionList(); 
}    

Now you have a single purpose method that you could call from the constructor... but a better solution would be to call this from the OnNavigatedTo event handler on your page class.

It is a general expectation in programming that creating an instance of an object should be relatively lightweight compared to the execution of actions against the object. Here's an interesting discussion: How much work should be done in a constructor

In universal apps it is expected that lightweight configuration is handled in the constructor and the bulk of initialisation 'logic' is delayed until the page is actually navigated to (OnNavigatedTo). Especially when we consider data access scenarios it might be important for the data to be as up-to-date as possible but there is another important event, when navigation to the page has resulted as backward navigation or after the application has been resumed. In these events you may be able to obtain some state or configuration from a previous serialised state, rather than re-initialising everything which could be a time consuming process.

So the 'best practises' here is to use OnNavigatedTo to process any page initialisation other than creation of page controls. You have access to the NavigationEventArgs that will help you to re-create the page in the state that the user is expecting. It is where other developers will look first to review the business logic behind your page, AND you can declare the OnNavigatedTo handler as async!

protected async override void OnNavigatedTo(NavigationEventArgs e)
{
    base.OnNavigatedTo(e);
    await InitialiseDataConnections();
}
Community
  • 1
  • 1
Chris Schaller
  • 13,704
  • 3
  • 43
  • 81
0

One way to do this is to move the asynchronous initialization out of your ctor in OnNavigatedTo. However, you must realize that OnNavigatedTo will return control to the framework before its asynchronous part will complete. That is probably OK but you need to consider how you handle errors.

I would structure this code a little bit differently though:

...

// The UI will use data binding to PageState to show 
// UI specific for the initialization phase.
public PageState PageState {get; private set {.../*include property change notification */...};}

// The UI will use data binding to ErrorMessage as needed.
public string ErrorMessage {get; private set {.../*include property change notification */...};}

...

protected async override void OnNavigatedTo(NavigationEventArgs e)
{
    base.OnNavigatedTo(e);

    await InitializeAsync();
}

private async Task InitializeAsync()
{
    try
    {
        // Have the UI bind to 
        PageState = PageState.InitializingInProgress;

        await SQLiteData(); 

        GetConnectionList();

        ...

        PageState = PageState.InitializedOk;
    }
    catch(...)
    {
        PageState = PageState.InitializedWithError;
        ErrorMessage = ...
    }
}

}

Ladi
  • 1,274
  • 9
  • 17
  • The code is very hard for me. I read it But I can't understand why I get error. When I get error? Is it important? – Amin Borjian Sep 20 '15 at 11:46
  • I was referring to potential errors that you could get when running your SQLite code. If the code is too hard then start with this - just move the async part of your initialization in OnNavigatedTo. Also be careful - the second time you run the app you will probably fail since you are attempting to create tables that already exist. You may need to add some more code to avoid creating the same tables at the second run. – Ladi Sep 20 '15 at 11:52
  • I get error on PageState when I copy this code? I don't use pageState until now. – Amin Borjian Sep 20 '15 at 12:01
  • Ignore the try / catch, the PageState and ErrorMessage for now. You can even remove InitializeAsync and replace the call to it with the two lines where I call "await SQLiteData();" and "GetConnectionList();". The main problem you had is now resolved since you can now call SQLiteData with await. – Ladi Sep 20 '15 at 12:05
  • In GetSqlite I ckeck for duplicate table. I make my mainpage emuty and use my code in onnavigateto. It's work perfectly but I have problem. I change my app language so I need to load GetSQLite before initialize to check language selected and change that. – Amin Borjian Sep 20 '15 at 12:16