0

I previously had this issue where my whole application froze after calling db.CreateTableAsync in a Service.cs file after following this tutorial. After fixing this issue with this solution, the application stopped freezing but CreateTableAsync seems to do nothing and just breaks out of the Init Task and the AddPin Task. In the same runtime, if I were to try to run the AddPin task multiple times, the first time CreateTableAsync would break and consecutive times after the initial, the rest of AddPin will be called (because db != null), and it's worth noting that no matter how many times I run AddPin the id does not increment.

Restroom.cs

using SQLite;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Dook.Model
{
    public class Restroom
    {
        [SQLite.PrimaryKey, SQLite.AutoIncrement]
        [Column("Id")]
        public int Id { get; set; }
        [Column("Name")]
        public string Name { get; set; }
        [Column("Address")]
        public string Address { get; set; }
        [Column("Username")]
        public string Username { get; set; }
        [Column("Location")]
        public Location PinLocation { get; set; }
    }
}

RestroomService.cs

using Dook.Model;
using SQLite;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Dook.Services
{
    public static class RestroomService
    {
        static SQLiteAsyncConnection db;

        static async Task InitAsync()
        {
            if(db != null) 
                return;

            var databasePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "MyData.db");

            db = new SQLiteAsyncConnection(databasePath);

            await db.CreateTableAsync<Restroom>();

            Debug.WriteLine("Bruh");
        }

        public static async Task AddPinAsync(string name, string address, string username, Location location)
        {
            await InitAsync();
            var restroom = new Restroom
            {
                Name = name,
                Address = address,
                Username = username,
                PinLocation = location
            };

            var id = await db.InsertAsync(restroom);
        }

        public static async Task RemovePinAsync(int id)
        {
            await InitAsync();

            await db.DeleteAsync<Restroom>(id);
        }

        public static async Task<IEnumerable<Restroom>> GetPinAsync()
        {
            await InitAsync();

            var restroom = await db.Table<Restroom>().ToListAsync();
            return restroom;
        }
    }
}

MainPage.xaml.cs

namespace Dook;
using Microsoft.Maui.Controls.Maps;
using Microsoft.Maui.Maps;
using System.Diagnostics;
using Map = Microsoft.Maui.Controls.Maps.Map;
using Dook.ViewModel;
using Dook.Model;

public partial class MainPage : ContentPage
{
    public MainPage()
    {
        InitializeComponent();
        MoveMapLocation();
    }

    private void GoToLocation_Button(object sender, EventArgs e)
    {
        MoveMapLocation();
    }

    private void OnMapClicked(object sender, MapClickedEventArgs e)
    {
        var vm = (MainViewModel)this.BindingContext;
        if (vm.AddCommand.CanExecute(e.Location))
            vm.AddCommand.ExecuteAsync(e.Location);
    }

    private void RefreshButton_Clicked(object sender, EventArgs e)
    {
        var vm = (MainViewModel)this.BindingContext;
        vm.RefreshCommand.ExecuteAsync();
    }

    private void MoveMapLocation()
    {
        //Function to avoid boilerplate code
        MapSpan mapSpan = new MapSpan(MainViewModel.GetLocation(), 0.01, 0.01);
        mainmap.MoveToRegion(mapSpan);
    }
}

MainPage.xaml

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:viewmodel="clr-namespace:Dook.ViewModel"
             x:DataType="viewmodel:MainViewModel"
             xmlns:maps="clr-namespace:Microsoft.Maui.Controls.Maps;assembly=Microsoft.Maui.Controls.Maps"
             xmlns:sensors="clr-namespace:Microsoft.Maui.Devices.Sensors;assembly=Microsoft.Maui.Essentials"
             xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
             xmlns:model="clr-namespace:Dook.Model"
             x:Class="Dook.MainPage"
             Title="{Binding Title}"
             Shell.NavBarIsVisible="False">

    <ContentPage.BindingContext>
        <viewmodel:MainViewModel />
    </ContentPage.BindingContext>

    <Grid
        Margin="0"
        RowDefinitions="*, Auto"
        ColumnDefinitions="*">

        <maps:Map
            x:Name="mainmap"
            MapType="Street"
            IsShowingUser="True"
            x:FieldModifier="public"
            HorizontalOptions="FillAndExpand"
            VerticalOptions="FillAndExpand"
            IsVisible="True"
            IsEnabled="True"
            Grid.Row="0"
            MapClicked="OnMapClicked"
            ItemsSource="{Binding Restroom}">

            <maps:Map.ItemTemplate>
                <DataTemplate x:DataType="model:Restroom">
                    <maps:Pin
                        Label="{Binding Name}"
                        Address="{Binding Address}"
                        Type="Generic"
                        Location="{Binding PinLocation}"/>
                </DataTemplate>
            </maps:Map.ItemTemplate>
        </maps:Map>

        <Button
            Text="Refresh"
            IsEnabled="{Binding IsNotBusy}"
            Clicked="RefreshButton_Clicked"
            Margin="5"
            VerticalOptions="Center"
            HorizontalOptions="Start"
            Grid.Row="1"/>

        <ImageButton
            Source="feather_navigation_icon.png"
            Aspect="AspectFill"
            Clicked="GoToLocation_Button" 
            IsEnabled="{Binding IsNotBusy}"
            Margin="20"
            VerticalOptions="End"
            HorizontalOptions="End"
            WidthRequest="13"
            HeightRequest="13"
            BackgroundColor="Transparent"
            CornerRadius="10"
            Grid.Row="0"/>
    </Grid>
    
</ContentPage>

MainViewModel.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Maui.Controls.Maps;
using Microsoft.Maui.Maps;
using Map = Microsoft.Maui.Controls.Maps.Map;
using Dook.Model;
using Dook.Services;
using MvvmHelpers;
using MvvmHelpers.Commands;
using CommunityToolkit.Maui.Core.Extensions;

namespace Dook.ViewModel
{
    public class MainViewModel : BaseViewModel
    {
        public ObservableRangeCollection<Restroom> Restroom { get; set; }
        public AsyncCommand RefreshCommand { get; }
        public AsyncCommand<Location> AddCommand { get; }
        public AsyncCommand<Restroom> RemoveCommand { get; }

        public MainViewModel()
        {
            Title = "Map Controller";

            Restroom = new ObservableRangeCollection<Restroom>();

            AddCommand = new AsyncCommand<Location>(AddAsync);
            RemoveCommand = new AsyncCommand<Restroom>(RemoveAsync);
            RefreshCommand = new AsyncCommand(RefreshAsync);
        }

        async Task AddAsync(Location currentLocation)
        {
            var name = await App.Current.MainPage.DisplayPromptAsync("Location Name", "Name of Location");
           // var address = "Latitude: {pinlocation.Latitude}, Longitude: {pinlocation.Longitude}, Altitude: {location.Altitude}";
            var address = "test";
            var username = await App.Current.MainPage.DisplayPromptAsync("Username", "Username of Toilet Adder");
            var location = currentLocation;
            if(name == null || address == null || username == null) { return; }
            await RestroomService.AddPinAsync(name, address, username, location);
            await RefreshAsync();
        }
        
        async Task RemoveAsync(Restroom restroom)
        {
            await RestroomService.RemovePinAsync(restroom.Id);
            await RefreshAsync();
        }

        async Task RefreshAsync()
        {
            IsBusy = true;
            await Task.Delay(2000);
            Restroom.Clear();
            var restrooms = await RestroomService.GetPinAsync();
            Restroom.AddRange(restrooms);
            IsBusy = false;
        }

        public static Location GetLocation()
        {
            try
            {
                Location location = new();
                location = Geolocation.Default.GetLastKnownLocationAsync().Result;
                if (location != null)
                    return location;
            }
            catch (Exception ex)
            {
                Debug.WriteLine($"Unable to get location: {ex.Message}");
                Application.Current.MainPage.DisplayAlert("Error!", ex.Message, "OK");
            }

            return null;
        }
    }
}
4
  • 1
    is the table created? Are there exceptions or log messages? Have you tried adding any exception handling?
    – Jason
    Commented Oct 25, 2023 at 16:19
  • I just tried adding exception handling, thank you for reminding me! Executing db.CreateTableAsync<Restroom>() returned this exception: "2023-10-25 23:38:24.888 Xamarin.PreBuilt.iOS[17793:6036802] Don't know about Microsoft.Maui.Devices.Sensors.Location", and executing var id = await db.InsertAsync(restroom) returned: "2023-10-25 23:44:55.524 Xamarin.PreBuilt.iOS[17798:6039260] no such table: Restroom". Seems like the whole issue is that the table isn't being created in the first place because of something related to Location. How should I go about fixing this?
    – K_Ketchup
    Commented Oct 26, 2023 at 6:47
  • Nevermind - after looking into the code behind sqlite-net-pcl, it seems like it doesn't support the type Location. Regardless, is there a way to bypass this? Or do I just have to remove the Location property from the Restroom model? If so, then how should I go about populating the map in the mainview with pins from a database that have a previously set location?
    – K_Ketchup
    Commented Oct 26, 2023 at 7:03
  • 1
    You would need to store Lat and Long as separate fields
    – Jason
    Commented Oct 26, 2023 at 7:09

1 Answer 1

0

According to Jason's comment, as an answer:

Storing Lat and Long as separate fields can fix the problem.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.