SQLite.NET > async VS sync

I embarked on my software journey back in 2000, and today, I passionately craft high-quality mobile apps, delivering to thousands of users.
Search for a command to run...

I embarked on my software journey back in 2000, and today, I passionately craft high-quality mobile apps, delivering to thousands of users.
I started writing Flutter and Dart code a few weeks ago for a real production project that we are migrating from another tech stack (Xamarin). Soon I started using the power of code generation libraries like freezed, json_serializable, slang_build_ru...

Capturing network traffic between your application and your server is a handy way of checking and debugging the data you are sending and what comes back from the server. Charles Proxy shows network calls in a sequence or as a tree structure, includin...

Upgrading your target SDK to 26+ (Android 8) can break your push notifications all of a sudden. Fortunately it's very easy to fix.

Once we change the target Android version to 26 or higher, the app icon suddenly gets messed up, from a full-size icon to a tiny, hard to notice thing inside a white shape (a circle on the default Google launcher). I delayed upgrading to 26+ because ...

When Apple realised a LinearLayout could be useful for developers...

Many developers are happy using TPL (Task Parallel Library) with SQLite.Net. It´s not hard to find blog posts, StackOverflow answers and other sites recommending it. The library offers an async connection that can be handy to avoid blocking the UI thread and that´s how I implemented SQLite.Net from day one.
The reason to use SQLiteAsyncConnection is pretty simple: I want my app to stay responsive when querying the database. Big queries take time. If the connection is synchronous my app may become unresponsive for a while until the data is retrieved. We care about users and C# async/await is relatively easy, so let´s use it everywhere!, right?
That´s right. Sqlite accesses data synchronously, no matter what. Async connections are fake 💩. Simple as that. Taking a look at the source code, you´ll find out that every method call is wrapped within a Task:
public Task<T> GetAsync<T>(object pk) where T : new()
{
return Task.Factory.StartNew(() =>
{
var conn = GetConnection();
using (conn.Lock())
{
return conn.Get<T>(pk);
}
});
}
Okay… so what´s wrong with it?
You can´t choose to make something async only when it´s needed. Once you set up an async connection, you will be forced to use these wrappers everywhere unless you create a new sync connection. What if you find out that a query is fast enough to be sync?
Would you stick to async/await anyway?
Would you create a new sync connection for those cases?
If you choose 1, you are complicating things. I mean: you are awaiting a Task rather than a direct and simple method call, forcing your method to be async. I found out that in mobile development (mostly in UI initialization logic), async is harder to manage. Database queries are usually made on-screen initialization, so there you have it. You still can make it worst, by getting the result of a task in a sync fashion: var user = connection.GetAsync<User>(1).Result so you end up using a sync method, wrapped in a Task to be async, and then converted back to sync. Well played! :punch:
If you choose 2, you are complicating things. The best way to work with SQLite in most cases is by reusing a single connection during the whole life cycle.
Just take a look here or here or here. The same can happen with sync connections, but it´s easier to fix:
Connection = new SQLiteConnection(path, SQLiteOpenFlags.ReadWrite
| SQLiteOpenFlags.Create | SQLiteOpenFlags.FullMutex);
I´ve never had a single issue when creating the connection that way.
I´m pretty sure that if you measure the time it takes for most of your queries to run, you´ll realize they are surprisingly fast. Fast enough so that the user won´t notice a delay, therefore async here is useless. Go ahead and try it.
In those cases, you can/should just make the async wrapper yourself. Besides, your code will provide more information: other developers working with your code will easily figure out which queries are fast enough (sync) and which are not (async).
KISS is an acronym for "Keep it simple, stupid". The KISS principle states that most systems work best if they are kept simple rather than made complicated; therefore simplicity should be a key goal in design and unnecessary complexity should be avoided.
Wikipedia