Unable to Access SQLite Data in MvvmCross ViewModel - sqlite

Hello StackOverflow community,
I know there's a lot of code in this post, but I wanted to give you guys, the community as good of a picture as possible as to what is going on here so that maybe someone can help me figure out what my issue is.
Recently for a project I'm working on we've decided to upgrade from MvvmCross 5.7.0 to 6.2.2. I've managed to get our UWP app to successfully complete the initialization and setup process. The first viewmodel for which we register the app start also starts initializing. However, I'm finding that my vm initialization hangs at a particular line of code (shown in the code below). The weirdest part though is similar methods called in the app initialization code run perfectly fine without hanging/deadlock, so I'm not sure what's different Here's a simplified version of my viewmodel code to illustrate:
public class MyViewModel : BaseAuthenticatedTabBarViewModel, IMvxViewModel<int>
{
private int? _settingValue;
public override async Task Initialize()
{
//Some irrelevant initialization code
Exception e = null;
try
{
//This line of code never returns
_settingValue = _settingValue ?? await AppSettingService.GetSettingValue();
}
catch (Exception ex)
{
e = ex;
}
if (e != null)
{
await HandleCatastrophicError(e);
}
}
}
The AppSettingService.GetSettingValue() method looks like this:
public async Task<int?> GetCurrentEventId()
{
return await GetNullableIntSetting("SettingValue");
}
private static async Task<int?> GetNullableIntSetting(string key)
{
try
{
var setting = await SettingDataService.SettingByName(key);
if (setting != null)
{
return string.IsNullOrEmpty(setting.Value) ? (int?)null : Convert.ToInt32(setting.Value);
}
}
catch (Exception ex)
{
//Handle the exception
}
return null;
}
All the code for SettingDataService:
public class SettingDataService : DataService<SettingDataModel>, ISettingDataService
{
public async Task<SettingDataModel> SettingByName(string name)
{
try
{
var values = (await WhereAsync(e => e.Name == name));
return values.FirstOrDefault();
}
catch(Exception ex)
{
//Handle the exception
}
return null;
}
}
Finally, the implementation for WhereAsync() is in a class called DataService and is as follows:
public virtual async Task<IEnumerable<T>> WhereAsync(System.Linq.Expressions.Expression<Func<T, bool>> condition, SQLiteAsyncConnection connection = null)
{
return await (connection ?? await GetConnectionAsync())
.Table<T>()
.Where(condition)
.ToListAsync();
}
Thank you very much for your help in advance
Edit: Forgot to also add this crucial bit of code to help you guys even further:
protected async Task<SQLiteAsyncConnection> GetConnectionAsync()
{
SQLiteAsyncConnection connection = null;
while (true)
{
try
{
connection = Factory.Create(App.DatabaseName);
// This line of code is the culprit. For some reason this hangs and I can't figure out why.
await connection.CreateTableAsync<T>();
break;
}
catch (SQLiteException ex)
{
if (ex.Result != Result.CannotOpen && ex.Result != Result.Busy && ex.Result != Result.Locked)
{
throw;
}
}
await Task.Delay(20);
}
return connection;
}

I'm suspecting that you are calling Task.Wait or Task<T>.Result somewhere further up your call stack. Or if you're not doing it, MvvmCross is probably doing it for you. This will cause a deadlock when called from a UI context.
Personally, I prefer the approach that ViewModels should always be constructed synchronously, and cannot have an asynchronous "initialization". That is, they must construct themselves (synchronously) into a "loading" state, and this construction can kick off an asynchronous operation that will later update them into a "loaded" state. The synchronous-initialization pattern means there's never an unnecessary delay when changing views; your users may only see a spinner or a loading message, but at least they'll see something. See my article on async MVVM data binding for a pattern that helps with this, and note that there's a newer version of the helper types in that article.

Related

is doing a commit at the end of a line of code a good thing?

I have code like this, where the commit is done at the end of the line. the goal is that when an error occurs while sending an email, then the commit will not be performed but will rollback. is something like this a good thing? or is there a better way than the one I'm currently implementing?
public async Task<IActionResult> Register(RegisterAccount register)
{
MyAccount myAccount = new();
using var transaction = _dbContext.Database.BeginTransaction();
bool afterRegister = false;
try
{
//code for check account
//code for set value register account
afterRegister = true;
_dbContext.Database.OpenConnection();
transaction.CreateSavepoint("register");
_dbContext.MyAccounts.Add(myAccount);
await _dbContext.SaveChangesAsync();
_dbContext.Database.CloseConnection();
//code for send email
transaction.Commit();
return RedirectToAction("RegisterConfirm", "Account", new { emailConfirm = myAccount.Email });
}
catch(Exception e)
{
if (afterRegister)
{
transaction.RollbackToSavepoint("register");
}
return View();
}
}
thank you for answering my question. good luck always

Cosmos always gets 404 when doing container.ReadItemAsync even though the item is there

I am pulling my hair out for this weird error. I have created an azure cosmosdb and manually inserted an item in the container and then query it from C# code:
public async Task<T> ReadAsync<T>(string documentId, string collectionName, string partitionKey)
where T : class
{
Requires.NotNullOrWhiteSpace(documentId, nameof(documentId));
Requires.NotNullOrWhiteSpace(collectionName, nameof(collectionName));
return await this.PerformActionAsync(
$"ReadAsync {documentId}. Collection: {collectionName}.",
async client =>
{
try
{
Container container = client.GetDatabase(this.cosmosDbDatabaseName).GetContainer(collectionName);
ItemResponse<T> response = await container.ReadItemAsync<T>(id: documentId, partitionKey: new PartitionKey(partitionKey)).ConfigureAwait(true);
return response.Resource;
}
catch (CosmosException ex)
{
if (ex.StatusCode == HttpStatusCode.NotFound)
{
resultStatus = NotFound;
return null;
}
resultStatus = Exception;
throw new Exception("The server encountered an internal error. Please retry the request.");
}
The code always returns 404 NotFound! But I can see the item in the container from DataExplorer!
Can anyone shed some light on me? Much appreciated.
Thanks!
The reason was that I got confused by partitionKey, which should be the value of the partitionKey!

Can't return completedsuccesfull task in .net core

Hi I wrote the following code:
private bool GetIsCompleted()
{
return Email.SendMessageAsync().IsCompletedSuccessfully;
}
[HttpPost]
public ViewResult CheckOut(Order order)
{
if (Cart.Lines.Count() == 0)
{
ModelState.AddModelError("","Your Cart is empty!");
}
if (ModelState.IsValid)
{
order.CartLines = Cart.Lines;
order.DateTime = DateTime.Now;
order.TotalPrice = Cart.ComputeTotalValue();
if (Repository.SaveOrder(order))
{
if (User.Identity.Name != null)
{
Email.SetMessageBody(order.OrderID);
if (GetIsCompleted())
{
Cart.Clear();
return View("Completed");
}
}
}
ViewBag.Error = "An error Occured while sending you an email with the order details.";
return View(new Order());
}
else
{
ViewBag.Error = "An error Occured while trying to save your order. Please try again!";
return View(new Order());
}
}
public async Task SendMessageAsync()
{
this.Message = new MailMessage(this.MailFrom.ToString(), this.MailTo.ToString(), this.GetSubject(), this.GetMessageBody());
//Message.Dispose();
try
{
await this.Client.SendMailAsync(this.Message);
}
catch (Exception ex)
{
Logger.LogInformation("The Email couldn't send to the recipient");
}
}
I get
An error Occured while sending you an email with the order details.
in the View. I want GetIsCompleted() to return true to proceed the code. It is developed under .net core. I do not understand why IsCompletedSuccessfully() does not return true; Any suggestion?
The current flow of your code is this:
Start sending the e-mail.
Check if it is completed successfully, decide that it hasn't and return failure.
The e-mail completes sending.
You're awaiting the actual SendMailAsync(..) method, and that's great, but nothing awaits SendMessageAsync(...) so it immediately returns the incomplete task to the caller. Because there isn't enough time between starting to send the e-mail and checking if the task completed, the status will be false.
You need to use async all the way up. Change your method definition to be async:
public async Task<ViewResult> CheckOut(Order order)
Replace this code:
if (GetIsCompleted())
{
Cart.Clear();
return View("Completed");
}
with this:
try
{
await Email.SendMessageAsync();
Cart.Clear();
return View("Completed");
}
catch (Exception e)
{
// handle exception
}
It's worth noting that you'll only ever get an exception if the call to new MailMessage(...) fails because your try/catch block in SendMessageAsync is swallowing all other exceptions.

How do I resolve an ImageSource IRandomAccessStream seeking not supported error?

I am receiving the following runtime-error when attempting to load an image via ImageSource assignment.
Cannot use the specified Stream as a Windows Runtime IRandomAccessStream because this Stream does not support seeking.
Specifically, I am passing an ImageSource object from one page to another page.
Thus, I am surprised that I am receiving this error.
I have even tried extracting the stream object from the ImageSource object.
However, this does not appear to be supported.
Can anyone provide guidance on how I can resolve this issue?
Same problem i was facing,
Please follow following step.
1.In your xaml page add like this a img tag
Image x:Name="productImage" Source="{Binding SelectedSalesItem.Source}"
2. on any grid call ItemSelected="OnSalesItemSelected" event called bellow method.
Public async void OnSalesItemSelected(object sender, EventArgs args) {
try
{
if (SaleVm.SelectedSalesItem != null)
{
OnImages();//Call point no 3 method "OnImages"
}
}
catch (Exception ex)
{
throw ex;
}
}
3.Create a method in your xaml.cs page.
async void OnImages() {
try
{
if (SaleVm.SelectedSalesItem != null)
{
IFolder rootFolder = FileSystem.Current.LocalStorage;
IFolder imageFolder = await rootFolder.GetFolderAsync("ItemsImg");
IFile imageFile = await imageFolder.GetFileAsync(SaleVm.SelectedSalesItem.ItemCode + ".png");
var stream = await imageFile.OpenAsync(FileAccess.Read);
productImage.Source = ImageSource.FromStream(() => stream);
}
}
catch (Exception ex)
{
throw ex;
}
}
4. Call "OnImages()" method from other class like ViewModel.
and change image at run time.

Error for GetStringAsync if triggered by ScheduledAgent but no error during WP8 App usage

I have a wrapper for the webclient that I am using to retrieve some data. This same function is being used by the WP8 App and also used by the WP8 ScheduledAgent.
Somehow, when the function is used by the WP8 App, there is no error and it returns correctly.
However, when the ScheduledAgent uses the function, it erred out at the bold code below. I tried a try catch but it is not catching. Via Debugger, the GetSTringAsync(uri) had completed without any exception. The error seemed to be only happening when it is assigning the return Task to the result string.
The error I received is:
An unhandled exception of type 'System.UnauthorizedAccessException' occurred in System.Windows.ni.dll
public class HttpClient : WebClient
..
private async Task GetStringAsync(string strUri)
{
Uri uri = new Uri(strUri);
string result = string.Empty;
try
{
result = await GetStringAsync(uri);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
return result;
}
...
private Task GetStringAsync(Uri requestUri)
{
TaskCompletionSource tcs = new TaskCompletionSource();
try
{
this.DownloadStringCompleted += (s, e) =>
{
if (e.Error == null)
{
tcs.TrySetResult(e.Result);
}
else
{
tcs.TrySetException(e.Error);
}
};
this.DownloadStringAsync(requestUri);
}
catch (Exception ex)
{
tcs.TrySetException(ex);
}
if (tcs.Task.Exception != null)
{
throw tcs.Task.Exception;
}
return tcs.Task;
}
Please advise if I am missing something.
My problem is because I am using pushpin as one of my object types within my Model. Apparently, in the scheduled agent, it is not able to access that object type and thus threw the above error.

Resources