Hello I am trying to add items to a list asynchronously but I am not sure how it is done and if I am using an incorrect syntax, this is what I have at the moment:
My View:
await viewModel.getMessages();
list.ItemsSource = viewModel.Messages;
My View Model:
public List<Message> Messages { get; set; }
public async Task getMessages()
{
await GetRemoteMessages();
}
private async Task GetRemoteMessages()
{
var remoteClient = new ChiesiClient();
var messages = await remoteClient.getMessages().ConfigureAwait(false);
//App.Database.
await SaveAll(messages).ConfigureAwait(false);
}
public async Task SaveAll(IEnumerable<Message> _messages)
{
foreach (var item in _messages)
{
await SaveMessage(item);
}
}
public async Task SaveMessage(Message item)
{
await Messages.Add(new Message // Cannot await void
{
Title = item.Title,
Description = item.Description,
Date = item.Date,
Url = item.Url
});
}
I am not sure if I am getting the whole concept right but how can I await adding items to my list? how is the correct way to do this?
You don't want to add items to a list asynchronously -- there's no real benefit to it.
Your code is fine, you just need to do everything synchronously after you get your messages back from your remote service.
public List<Message> Messages { get; set; }
public async Task getMessages()
{
await GetRemoteMessages();
}
private async Task GetRemoteMessages()
{
var remoteClient = new ChiesiClient();
var messages = await remoteClient.getMessages().ConfigureAwait(false);
//App.Database.
SaveAll(messages);
}
public void SaveAll(IEnumerable<Message> _messages)
{
foreach (var item in _messages)
{
SaveMessage(item);
}
}
public void SaveMessage(Message item)
{
Messages.Add(new Message // Cannot await void
{
Title = item.Title,
Description = item.Description,
Date = item.Date,
Url = item.Url
});
}
Now, remoteClient.getMessages() might take a long time! It's an I/O bound task -- it might take a millisecond, or it might take 10 minutes. We don't know, and we can't know. If it actually does end up taking 10 minutes, we don't want the UI to be locked up that entire time. When you use the await keyword, it starts that operation, then gives control back to the UI. When the operation is done, it continues with the rest of the method.
The original example you're working off of saves stuff to a database. That's another I/O bound operation, and thus the database API creators kindly provided you with some asynchronous APIs to help out with that.
You're modifying the example to put the results into a list. Adding to a list is not an I/O bound operation. In fact, it's pretty much instantaneous. As a result, there's no asynchronous API for it. You don't need to do that part asynchronously.
Related
I'm afraid the more I read, the more baffled I get. I think I need a real world example. I'm stuck in some code in a MVVM WPF app. How do I get NetworkPlaces running asyncronously so it doesn't block the UI thread? I've tried loads of different combinations of async, task and await.
A lot of the examples seem to use 'end' methods that have asynchronous methods within it such as .ReadToEndAsync or .GetStringAsync. One advisory (and UI freeze) I kept getting was about NetworkPlaces not having an Async method.
Here's my illustration code at the moment. I'd be grateful if someone could show mw some working code:
public RelayCommand ScanForDevicesCMD { get; private set; }
public MainVM()
{
ScanForDevicesCMD = new RelayCommand(ScanForDevices, CanScanForDevices);
}
public async void ScanForDevices(object obj)
{
Debug.WriteLine("SCAN STARTED");
Task task = NetworkPlaces();
await task;
Debug.WriteLine("SCAN STOPPED");
}
public Task NetworkPlaces()
{
ShellNetworkComputers shellNetworkComputers = new ShellNetworkComputers();
foreach (string snc in shellNetworkComputers)
{
Debug.WriteLine(snc);
}
return Task.CompletedTask;
}
You can run synchronous code as a Task.
Change your method to this:
public void NetworkPlaces()
{
ShellNetworkComputers shellNetworkComputers = new ShellNetworkComputers();
foreach (string snc in shellNetworkComputers)
{
Debug.WriteLine(snc);
}
}
And call it like this:
await Task.Run(() => {
NetworkPlaces();
});
Update: Make sure that NetworkPlaces() doesn't do any UI work (e.g. user interaction) itself.
I have the following scenario:
In my WPF app using MVVM (I'm fairly new to MVVM) I have a Window that contains a DataGrid. When the Window is loaded I want to fill the DataGrid with entries from a Database using Entity Framework. Additionally a certain column should use a ComboBox in edit mode which is also filled from the DB (many-to-many relation). These items should also be loaded when the Window loads. Oh yes and, of course, this should be done async so that the database queries do not block the UI.
I've read these excellent blog posts by Stephen Cleary: https://t1p.de/c12y8 and https://t1p.de/xkdh .
I have chosen the approach that my ViewModel is constructed synchronously and an asynchronous Initialize method is called from the Window-Loaded event. The Initialize method then triggers the two queries.
The ViewModel:
public class ViewModel : ViewModelBase
{
// this uses a slightly modified class from the first blog post
private NotifyTaskCompletion databaseAction;
public NotifyTaskCompletion DatabaseAction
{
get => databaseAction;
private set
{
databaseAction = value;
NotifyPropertyChanged();
}
}
public ViewModel()
{
// nothinc asynchronous going on here
}
public void Initialize()
{
DatabaseAction = new NotifyTaskCompletion(InitializeAsync());
}
private async Task InitializeAsync()
{
List<Task> tasks = new List<Task>();
tasks.Add(FirstQueryAsync());
tasks.Add(SecondQueryAsync());
await Task.WhenAll(tasks);
}
private async Task FirstQueryAsync()
{
using (var context = new SampleContext())
{
var query = await context.Beds.ToListAsync();
if (query.Count > 0)
{
beds = new ObservableCollection<Bed>();
query.ForEach(bed => beds.Add(bed));
}
else
{
LoadBedsFromFile(ref beds);
foreach (var bed in beds)
{
context.Beds.Add(bed);
}
await context.SaveChangesAsync();
}
}
}
private void LoadBedsFromFile(ref ObservableCollection<Bed> list)
{
if (File.Exists("Beds.xml"))
{
FileStream fs = new FileStream("Beds.xml", FileMode.Open);
XmlSerializer serializer = new XmlSerializer(typeof(ObservableCollection<Bed>));
list = (ObservableCollection<Bed>)serializer.Deserialize(fs);
fs.Close();
}
}
private async Task SecondQueryAsync()
{
using (var context = new SampleContext())
{
var query = await context.Samples.Where(...)
.Include(...)
.ToListAsync();
foreach (Sample item in query)
{
// each entry is put into a ViewModel itself
SampleViewModel vm = new SampleViewModel(item);
// sampleClass.Samples is an ObservableCollection
sampleClass.Samples.Add(vm);
}
}
}
The Window:
private async void Window_Loaded(object sender, RoutedEventArgs e)
{
ViewModel vm = this.TryFindResource("viewModel") as ViewModel;
if (vm != null)
vm.Initialize();
}
}
Now here is the Issue:
The UI is unresponsive and does not update until the initialization is finished. Even if I use Task.Run(() => vm.Initialize());.
The strange thing ist this: If I put a Task.Delay() into the InitializeAsync method, the loading animation ist shown as intended und the UI is responsive. If i put the Delay into SecondQueryAsync for example the UI freezes for a few seconds and afterwards the loading animation rotates for the duration of the delay.
I suspect this might be some issue with creating the DbContext but i cannot pinpoint this.
I eventually solved this problem.
TheodorZoulias' comment and the link he posted to await Task.Run vs await gave me a hint towards the solution.
Replacing the new NotifyTaskCompletion(InitializeAsync()); with new NotifyTaskCompletion(Task.Run(() => InitializeAsync())); unfortunately raised other problems because I could not simply modify the ObservableCollection from that Task's context.
I really like writing code with async-await. The problem is when it hits code that is not async.
As I suspected the syncronous call to create the DbContext was blocking the thread.
So this is how I solved it - maybe it helps someone:
First I used a factory method to create the DbContext asynchronous:
public class SampleContext : DbContext
{
private SampleContext() : base()
{
...
}
public static async Task<SampleContext> CreateAsync()
{
return await Task.Run(() => { return new SampleContext(); }).ConfigureAwait(false);
}
}
Then I rewrote the methods that query the database in a way that they do not modify the ObservableCollection themselves but returned a list instead.
A second method takes the return value as a parameter and modifies the ObservableCollection. That way I can use ConfigureAwait to configure the context.
private async Task<List<Sample>> SecondQueryAsync()
{
var context = await SampleContext.CreateAsync().ConfigureAwait(false);
var query = await context.Samples.Where(...)
.Include(...)
.ToListAsync().ConfigureAwait(false);
context.Dispose();
return query;
}
private async Task InitializeAsync()
{
List<Task> tasks = new List<Task>();
var firstTask = FirstQueryAsync();
var secondTask = SecondQueryAsync();
tasks.Add(firstTask);
tasks.Add(secondTask);
await Task.WhenAll(tasks);
if (tasks.Any(t => t.IsFaulted))
{
Initialized = false;
}
else
{
Initialized = true;
FillFirstList(firstTask.Result);
FillSecondList(secondTask.Result);
}
}
I'm new to System.Threading.Channels. I have the following consumer code:
await foreach (var thing in this.Reader.ReadAllAsync(cancellationToken)
.ConfigureAwait(false))
{
await this.HandleThingAsync(thing, cancellationToken).ConfigureAwait(false);
}
That seems to work fine when consuming things produced by a single producer like this:
var things = await this.GetThingsAsync(cancellationToken).ConfigureAwait(false);
await foreach (var thing in things.WithCancellation(cancellationToken)
.ConfigureAwait(false))
{
await this.Writer.WriteAsync(thing, cancellationToken).ConfigureAwait(false);
}
this.Writer.Complete();
But when I try to add in a second producer of the same general form, as soon as one of the two producers is done (and calls this.Writer.Complete()), anything that the other producer still needs to add will be rejected because the channel is already closed. This is a problem because I want the reader to read everything, not merely everything up until the point that any one producer has nothing more to produce.
How does one deal with this situation? Is there some built-in or otherwise "standard" way? For example, perhaps a "condenser" channel which exposes multiple Channel.Writer objects (one for each "real" producer), and a single Channel.Reader (for the single "real" consumer)?
I don't think that there is a way that you could call "standard". A Channel<T> is a tool that can be used in many different ways, much like a Task or a SemaphoreSlim. In your case, you could propagate the completion of all producers by using a counter like this:
int producersCount = X;
//...
await foreach (var thing in things)
await channel.Writer.WriteAsync(thing);
if (Interlocked.Decrement(ref producersCount) == 0) channel.Writer.Complete();
Alternatively, if each producer is a Task, you could attach a continuation to all these tasks combined like this:
var producers = new List<Task>();
//...
_ = Task.WhenAll(producers).ContinueWith(_ => channel.Writer.Complete(),
default, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default);
The discard (_) above has been used in order to communicate that the ContinueWith continuation has been launched in a fire-and-forget fashion. If you dislike throwing unobserved tasks in the wind like me, you can handle the completion of the producers in an async void method like this:
var producers = new List<Task>();
//...
HandleProducersCompletion();
//...
async void HandleProducersCompletion()
{
try { await Task.WhenAll(producers); }
finally { channel.Writer.Complete(); }
}
This way an exception thrown by the channel.Writer.Complete(); invocation will be unhandled and will crash the process. Which is arguably a good thing, considering the alternative which is a process that has been deadlocked for no apparent reason.
I wound up making this class, based on my "channel condenser" idea that I mentioned in my original question. It may or may not be horrible and/or bug-ridden, but at least so far it seems to do the job in a way that seems fairly natural and unobtrusive to me:
using Nito.AsyncEx;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Channels;
using System.Threading.Tasks;
namespace Rwv37.System.Threading.Channels
{
public class ChannelCondenser<T>
{
private bool IsGoing { get; set; }
private AsyncLock IsGoingLock { get; init; }
private ConcurrentBag<Channel<T>> IncomingChannel { get; init; }
private Channel<T> OutgoingChannel { get; init; }
public ChannelCondenser()
{
this.IsGoingLock = new AsyncLock();
this.IncomingChannel = new();
this.OutgoingChannel = Channel.CreateUnbounded<T>();
}
public async Task GoAsync(CancellationToken cancellationToken = default)
{
using (await this.IsGoingLock.LockAsync(cancellationToken).ConfigureAwait(false))
{
if (this.IsGoing)
{
throw new System.InvalidOperationException("Cannot go - already going!");
}
this.IsGoing = true;
}
List<Task> tasks = new();
foreach (var incomingChannel in this.IncomingChannel)
{
tasks.Add(this.HandleIncomingChannelAsync(incomingChannel, cancellationToken));
}
await Task.WhenAll(tasks).ConfigureAwait(false);
this.OutgoingChannel.Writer.Complete();
}
public ChannelWriter<T> AddIncomingChannel()
{
using (this.IsGoingLock.Lock())
{
if (this.IsGoing)
{
throw new System.InvalidOperationException("New incoming channels cannot be added while going!");
}
}
Channel<T> incomingChannel = Channel.CreateUnbounded<T>();
this.IncomingChannel.Add(incomingChannel);
return incomingChannel.Writer;
}
public ChannelReader<T> GetOutgoingChannel()
{
return this.OutgoingChannel.Reader;
}
private async Task HandleIncomingChannelAsync(Channel<T> incomingChannel, CancellationToken cancellationToken)
{
await foreach (var item in incomingChannel.Reader.ReadAllAsync(cancellationToken).ConfigureAwait(false))
{
await this.OutgoingChannel.Writer.WriteAsync(item, cancellationToken).ConfigureAwait(false);
}
}
}
}
Usage within the consumer(s) and producer(s) is completely unchanged from that shown in my original question.
The only thing I had to change outside of them is how the classes using them are constructed. Consumer construction was changed from...
private Channel<Thing> WantedThingsChannel { get; init; }
(...)
this.WantedThingsChannel = Channel.CreateUnbounded<Thing>();
this.WantedThingsHandler = new(this.WantedThingsChannel.Reader);
... to...
private ChannelCondenser<Thing> WantedThingsCondenser { get; init; }
(...)
this.WantedThingsCondenser = new();
this.WantedThingsHandler = new(this.WantedThingsCondenser.GetOutgoingChannel());
Similarly, the producer's construction was changed from...
this.WantedThingsRetriever = new(this.WantedThingsChannel.Writer);
... to...
this.WantedThingsRetriever = new(this.WantedThingsCondenser.AddIncomingChannel());
Oh, no, wait, I lied. One other change outside of them: My program's main Task.WhenAll was changed so that it additionally waits on the ChannelCondenser. So, from...
List<Task> tasks = new()
{
this.WantedThingsHandler.GoAsync(cancellationToken),
this.WantedThingsRetriever.GoAsync(cancellationToken),
};
... to...
List<Task> tasks = new()
{
this.WantedThingsCondenser.GoAsync(cancellationToken),
this.WantedThingsHandler.GoAsync(cancellationToken),
this.WantedThingsRetriever.GoAsync(cancellationToken),
};
I have a list lstSubscriptionRequests on which indivisual items i am doing some sort of processing asynchronously. Then after All items are processed i have to return the updated list items. My current implementation is like
List<SubscriptionRequest> lstSubscriptionRequests = FromSomeResource();
var tsk = new List<Task>();
foreach (var oTsk in lstSubscriptionRequests.Select(objSubscriptionRequest => new Task(
() => ProcessSubscriptionForASingleRecord(objSubscriptionRequest))))
{
oTsk.Start();
lock (tsk)
{
tsk.Add(oTsk);
}
}
Task.WaitAll(tsk.ToArray());
It's looks like some of the items after all the tasks completed are not updated.
Please let me know what correction i needed
You could accomplish this much easier. Note that using the Task constructor is not typically necessary or recommended, also mutating the state of a particular object can be difficult to follow or debug. Returning a new object that represents your desired state will allow you to enforce a minimum valid state. The following code will processes all your items and return the completed items to your client code.
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Processing {
public class MyProcessor {
public async Task<IEnumerable<Subscription>> ProcessSubscriptionRequestsAsync(IEnumerable<SubscriptionRequest> subscriptionRequests) {
var subscriptionProcessingTasks = subscriptionRequests.Select(request => ProcessSubscriptionForASingleRecord(request)).ToArray();
return await Task.WhenAll(subscriptionProcessingTasks);
}
public async Task<Subscription> ProcessSubscriptionForASingleRecord(SubscriptionRequest request) {
//process the request
try {
var subscription = await Context.ProcessRequest(request);
return subscription;
} catch {
//something went wrong with the request
}
}
}
public class SubscriptionRequest {
//A subcription request
}
public class Subscription {
//A completed subscription request
}
}
Update
It could help if you could exclude new Class Subscription and add the solution in your answer. i will then try it out
Hopefully the simplified before and after view will be easier to integrate. The primary difference is replacing the Parallel.ForEach with a Select to create your collection of tasks without needing to take a lock on the task list for every SubscriptionRequest, also spooling up Tasks in parallel isn't typically necessary as each one will be executing asynchronously you only gain reaching a point where all are waiting sooner, not finishing. Next each tasks is allowed to start and all are awaited at await Task.WhenAll(tasks). It will be important for you to determine what type of processing each SubscriptionRequest undergoes. For the sake of example i"ve made the assumption that each request is somehow linked to database access, i.e. storing the request, updating a user profile of sorts etc..
public class OriginalSynchronous {
public void ProcessSubscriptionRequest() {
List<SubscriptionRequest> lstSubscriptionRequests = FromSomeResource();
List<Task> tsk = new List<Task>();
Parallel.ForEach(lstSubscriptionRequests, objSubscriptionRequest => {
var oTsk =
new Task(
() => ProcessSubscriptionForASingleRecord(objSubscriptionRequest));// update some properties after processing SubscriptionRequest
oTsk.Start();
lock (tsk) {
tsk.Add(oTsk);
}
});
Task.WaitAll(tsk.ToArray());
}
private void ProcessSubscriptionForASingleRecord(SubscriptionRequest request) {
//modify SubscriptionRequest
}
}
public class ModifiedAsync {
public async Task ProcessSubscriptionRequest() {
var subscriptionRequests = await FromSomeResourceAsync();
var tasks = subscriptionRequests.Select(request => {
return ProcessSubscriptionForASingleRecord(request);
}).ToArray();
await Task.WhenAll(tasks);
}
public async Task ProcessSubscriptionForASingleRecord(SubscriptionRequest request) {
//modify SubscriptionRequest
}
}
Seeking some input on a behaviour I'm noticing in my code below. This is my first attempt at async/await using Xamarin Forms and I have perused hundreds of posts, blogs and articles on the subject including the writings from Stephen Cleary on async from constructors and best practices to avoid locking. Although I am using a MVVM framework I assume my issue is more generic than that so I'll ignore it for the moment here.
If I am still missing something or there are ways to improve what I'm trying to do ... happy to listen and learn.
At a high level the logic is as follows:
Application starts and initialises
During initialisation verify database exist and if not - create the SQLite DB. Currently I force this every time to simulate a new application and pre-populate it with some sample data for development purposes
After initialisation completed load results set and display
This works most of the time but I have noticed 2 infrequent occurrences due to the async handling of the database initialisation and pre-populating:
Occasionally not all sample records created are displayed once the app started up - I assume this is because the pre-population phase has not completed when the results are loaded
Occasionally I get an error that one of the tables have not been created - I assume this is because the database initialisation has not completed when the results are loaded
The code - simplified to show the flow during initialisation and startup:
----------- VIEW / PAGE MODEL ----------------
public class MyListItemsPageModel
{
private ObservableRangeCollection<MyListItem> _myListItems;
private Command loadItemsCommand;
public MyListItemsPageModel()
{
_myListItems = new ObservableRangeCollection<MyListItem>();
}
public override void Init(object initData)
{
if (LoadItemsCommand.CanExecute(null))
LoadItemsCommand.Execute(null);
}
public Command LoadItemsCommand
{
get
{
return loadItemsCommand ?? (loadItemsCommand = new Command(async () => await ExecuteLoadItemsAsyncCommand(), () => { return !IsBusy; }));
}
}
public ObservableRangeCollection<MyListItem> MyListItems {
get { return _myListItems ?? (_myListItems = new ObservableRangeCollection<MyListItem>()); }
private set {
_myListItems = value;
}
}
private async Task ExecuteLoadItemsAsyncCommand() {
if (IsBusy)
return;
IsBusy = true;
loadItemsCommand.ChangeCanExecute();
var _results = await MySpecificDBServiceClass.LoadAllItemsAsync;
MyListItems = new ObservableRangeCollection<MyListItem>(_results.OrderBy(x => x.ItemName).ToList());
IsBusy = false;
loadItemsCommand.ChangeCanExecute();
}
}
----------- DB Service Class ----------------
// THERE IS A SPECIFIC SERVICE LAYER BETWEEN THIS CLASS AND THE PAGE VIEW MODEL HANDLING THE CASTING OF TO THE SPECIFIC DATA TYPE
// public class MySpecificDBServiceClass : MyGenericDBServiceClass
public class MyGenericDBServiceClass<T>: IDataAccessService<T> where T : class, IDataModel, new()
{
public SQLiteAsyncConnection _connection = FreshIOC.Container.Resolve<ISQLiteFactory>().CreateConnection();
internal static readonly AsyncLock Mutex = new AsyncLock();
public DataServiceBase()
{
// removed this from the constructor
//if (_connection != null)
//{
// IsInitialized = DatabaseManager.CreateTableAsync(_connection);
//}
}
public Task<bool> IsInitialized { get; private set; }
public virtual async Task<List<T>> LoadAllItemsAsync()
{
// Temporary async/await initialisation code. This will be moved to the start up as per Stephen's suggestion
await DBInitialiser();
var itemList = new List<T>();
using (await Mutex.LockAsync().ConfigureAwait(false))
{
itemList = await _connection.Table<T>().ToListAsync().ConfigureAwait(false);
}
return itemList;
}
}
----------- DB Manager Class ----------------
public class DatabaseManager
{
static double CURRENT_DATABASE_VERSION = 0.0;
static readonly AsyncLock Mutex = new AsyncLock();
private static bool IsDBInitialised = false;
private DatabaseManager() { }
public static async Task<bool> CreateTableAsync(SQLiteAsyncConnection CurrentConnection)
{
if (CurrentConnection == null || IsDBInitialised)
return IsDBInitialised;
await ProcessDBScripts(CurrentConnection);
return IsDBInitialised;
}
private static async Task ProcessDBScripts(SQLiteAsyncConnection CurrentConnection)
{
using (await Mutex.LockAsync().ConfigureAwait(false))
{
var _tasks = new List<Task>();
if (CURRENT_DATABASE_VERSION <= 0.1) // Dev DB - recreate everytime
{
_tasks.Add(CurrentConnection.DropTableAsync<Table1>());
_tasks.Add(CurrentConnection.DropTableAsync<Table2>());
await Task.WhenAll(_tasks).ConfigureAwait(false);
}
_tasks.Clear();
_tasks.Add(CurrentConnection.CreateTableAsync<Table1>());
_tasks.Add(CurrentConnection.CreateTableAsync<Table2>());
await Task.WhenAll(_tasks).ConfigureAwait(false);
_tasks.Clear();
_tasks.Add(UpgradeDBIfRequired(CurrentConnection));
await Task.WhenAll(_tasks).ConfigureAwait(false);
}
IsDBInitialised = true;
}
private static async Task UpgradeDBIfRequired(SQLiteAsyncConnection _connection)
{
await CreateSampleData();
return;
// ... rest of code not relevant at the moment
}
private static async Task CreateSampleData()
{
IDataAccessService<MyListItem> _dataService = FreshIOC.Container.Resolve<IDataAccessService<MyListItem>>();
ObservableRangeCollection<MyListItem> _items = new ObservableRangeCollection<MyListItem>(); ;
_items.Add(new MyListItem() { ItemName = "Test 1", ItemCount = 14 });
_items.Add(new MyListItem() { ItemName = "Test 2", ItemCount = 9 });
_items.Add(new MyListItem() { ItemName = "Test 3", ItemCount = 5 });
await _dataService.SaveAllItemsAsync(_items).ConfigureAwait(false);
_items = null;
_dataService = null;
IDataAccessService<Sample> _dataService2 = FreshIOC.Container.Resolve<IDataAccessService<AnotherSampleTable>>();
ObservableRangeCollection<Sample> _sampleList = new ObservableRangeCollection<Sample>(); ;
_sampleList.Add(new GuestGroup() { SampleName = "ABC" });
_sampleList.Add(new GuestGroup() { SampleName = "DEF" });
await _dataService2.SaveAllItemsAsync(_sampleList).ConfigureAwait(false);
_sampleList = null;
_dataService2 = null;
}
}
In your DataServiceBase constructor, you're calling DatabaseManager.CreateTableAsync() but not awaiting it, so by the time your constructor exits, that method has not yet completed running, and given that it does very little before awaiting, it's probably barely started at that point. As you can't effectively use await in a constructor, you need to remodel things so you do that initialisation at some other point; e.g. perhaps lazily when needed.
Then you also want to not use .Result/.Wait() whenever possible, especially as you're in an async method anyway (e.g. ProcessDBScripts()), so instead of doing
var _test = CurrentConnection.DropTableAsync<MyListItem>().Result;
rather do
var _test = await CurrentConnection.DropTableAsync<MyListItem>();
You also don't need to use Task.Run() for methods that return Task types anyway. So instead of
_tasks.Add(Task.Run(() => CurrentConnection.CreateTableAsync<MyListItem>().ConfigureAwait(false)));
_tasks.Add(Task.Run(() => CurrentConnection.CreateTableAsync<AnotherSampleTable>().ConfigureAwait(false)));
just do
_tasks.Add(CurrentConnection.CreateTableAsync<MyListItem>()));
_tasks.Add(CurrentConnection.CreateTableAsync<AnotherSampleTable>()));
sellotape has correctly diagnosed the code problem: the constructor is starting an asynchronous method but nothing is (a)waiting for it to complete. A simple fix would be to add await IsInitialized; to the beginning of LoadAllItemsAsync.
However, there's also a design problem:
After initialisation completed load results set and display
That's not possible on Xamarin, or any other modern UI platform. You must load your UI immediately and synchronously. What you should do is immediately display a splash/loading page and start the asynchronous initialization work. Then, when the async init is completed, update your VM/UI with your "real" page. If you just have LoadAllItemsAsync await IsInitialized, then your app will sit there for some time showing the user zero data before it "fills in".
You may find my NotifyTask<T> type (available on NuGet) useful here if you want to show a splash/spinner instead of zero data.