0
votes

I am following this guide to add SQLite into my Xamarin.Forms Shared Asset project. It's not a PCL. I am using XAML with Code Behind.

I seem to have all the code in all the right places. I have an SQLite.cs which contains all of my DB calls I want to make and also defines the database.

I have an Interface defined inside a class I know works and is accessible as other functions in it are being consumed.

I then have a class in each platform project (I have show below the droid example) which uses the interface to get the local file path.

And finally in my Shared Asset project I define and (should) instantiate (I think) my database.

So with all that in place, In one of my ViewModels I try and execute a database call. However the inner exception reveals that I'm getting an Object Reference error. Basically my "database" object is null when it tries to execute database.MyMethod().

I can't work out why its not getting instantiated. Any ideas?

The line it executes is

var dbtimings = TechsportiseData.GetTimingsAsync().Result.ToList();

That method is...

public static Task<List<Timing>> GetTimingsAsync()
{
    return database.Table<Timing>().ToListAsync();
}

and it trips over because database is null.

SQLite.cs (Shared Asset Project)

using System;
using System.Collections.Generic;
using System.Text;
using Xamarin.Forms;
using static TechsportiseApp.Helpers.GlobalFunctions;
using TechsportiseApp.Models;
using System.Threading.Tasks;
using SQLite;

namespace TechsportiseApp.Data
{
    public class TechsportiseData
    {
        readonly SQLiteAsyncConnection database;

        public TechsportiseData(string dbPath)
        {
            database = new SQLiteAsyncConnection(dbPath);
            database.CreateTableAsync<Scan>().Wait();
            database.CreateTableAsync<Timing>().Wait();
        }


        public static Task<List<Timing>> GetTimingsAsync()
        {
            return database.Table<Timing>().ToListAsync();
        }

        public static Task<List<Timing>> GetTimingsNotUploadedAsync()
        {
            return database.QueryAsync<Timing>("SELECT * FROM [Timing] WHERE [Uploaded] = 0");
        }

        public static Task<Timing> GetTimingAsync(int id)
        {
            return database.Table<Timing>().Where(i => i.Id == id).FirstOrDefaultAsync();
        }

        public static Task<int> SaveTimingAsync(Timing timing)
        {
            if (timing.Id != 0)
            {
                return database.UpdateAsync(timing);
            }
            else
            {
                return database.InsertAsync(timing);
            }
        }

        public static Task<int> DeleteTimingAsync(Timing timing)
        {
            return database.DeleteAsync(timing);
        }

        public static Task DeleteAllTimingsAsync()
        {
            database.DropTableAsync<Timing>().Wait();          
            return database.CreateTableAsync<Timing>();
        }

        public static Task<List<Scan>> GetScansAsync()
        {
            return database.Table<Scan>().ToListAsync();
        }

        public static Task<List<Scan>> GetScansNotUploadedAsync()
        {
            return database.QueryAsync<Scan>("SELECT * FROM [Scan] WHERE [Uploaded] = 0");
        }

        public static Task<Scan> GetScanAsync(int id)
        {
            return database.Table<Scan>().Where(i => i.Id == id).FirstOrDefaultAsync();
        }

        public static Task<int> SaveScanAsync(Scan scan)
        {
            if (scan.Id != 0)
            {
                return database.UpdateAsync(scan);
            }
            else
            {
                return database.InsertAsync(scan);
            }
        }

        public static Task<int> DeleteScanAsync(Scan scan)
        {
            return database.DeleteAsync(scan);
        }

        public static Task DeleteAllScansAsync()
        {
            database.DropTableAsync<Scan>().Wait();
            return database.CreateTableAsync<Scan>();
        }



        public static Task<RaceClock> GetRaceClockAsync()
        {
            return database.Table<RaceClock>().FirstOrDefaultAsync();
        }

        public static Task<int> SaveRaceClockAsync(RaceClock clock)
        {
            return database.InsertAsync(clock);
        }

        public static Task DeleteRaceClockAsync()
        {
            database.DropTableAsync<RaceClock>().Wait();
            return database.CreateTableAsync<RaceClock>();
        }
    }
}

App.xaml.cs

using Xamarin.Forms;
using Xamarin.Forms.Xaml;
using System.Collections.Generic;
using TechsportiseApp.API;
using TechsportiseApp.Views;
using TechsportiseApp.ViewModels;
using TechsportiseApp.Models;
using TechsportiseApp.Helpers;
using TechsportiseApp.Data;
using static TechsportiseApp.Helpers.GlobalFunctions;

//[assembly: XamlCompilation(XamlCompilationOptions.Compile)]
namespace TechsportiseApp
{
    public partial class App : Application
    {
        static TechsportiseData database;



        public App()
        {

            InitializeComponent();


            Application.Current.Properties["APIServer"] = "https://www.techsportise.com/";
            //Application.Current.Properties["APIServer"] = "http://localhost:52693/";

            if (Application.Current.Properties.ContainsKey("ShowHelpOnStartup") == false)
            {
                Application.Current.Properties["ShowHelpOnStartup"] = true;
            }
            if (Application.Current.Properties.ContainsKey("ShowCompletedRaces") == false)
            {
                Application.Current.Properties["ShowCompletedRaces"] = false;
            }
            //MainPage = new NavigationPage(new ResultsProcess());

            //MainPage = new NavigationPage(new Scanning());
            //If the token is present and they are remembered
            if ((Application.Current.Properties.ContainsKey("Token")) && (Application.Current.Properties.ContainsKey("IsRemembered")))
            {
                //And is blank/null
                if (((string)Application.Current.Properties["Token"] == "") || ((GlobalFunctions.PropertyToBool(Application.Current.Properties["IsRemembered"].ToString()) != true)))
                {
                    //They need to login
                    MainPage = new NavigationPage(new Login());
                }
                //Otherwise they can go straight to their page
                else
                {
                    if (GlobalFunctions.PropertyToBool(Application.Current.Properties["ShowHelpOnStartup"].ToString()) == true)
                    {
                        MainPage = new StartupHelp(true);
                    }
                    else
                    {
                        MainPage = new MainMenuMasterDetail();
                    }

                }
            }
            else
            {
                MainPage = new NavigationPage(new Login());
            }
        }

        public static TechsportiseData Database
        {
            get
            {
                if (database == null)
                {
                    database = new TechsportiseData(DependencyService.Get<IFileHelper>().GetLocalFilePath("Techsportise.db3"));
                }
                return database;
            }
        }


        protected override void OnStart()
        {
            // Handle when your app starts
        }

        protected override void OnSleep()
        {
            // Handle when your app sleeps
        }

        protected override void OnResume()
        {
            // Handle when your app resumes
        }
    }
}

Interface (Shared Assets project)

using System;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Net;
using Xamarin.Forms;

namespace TechsportiseApp.Helpers
{
    public class GlobalFunctions
    {
        public GlobalFunctions()
        {
        }

        public interface IFileHelper
        {
            string GetLocalFilePath(string filename);
        }

        public static bool CheckForInternetConnection()
        {
            try
            {
                HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://www.google.com");
                request.Timeout = 5000;
                request.Credentials = CredentialCache.DefaultNetworkCredentials;
                HttpWebResponse response = (HttpWebResponse)request.GetResponse();

                if (response.StatusCode == HttpStatusCode.OK)
                    return true;
                else
                    //return false;
                    return true;
            }
            catch
            {
                //return false;
                return true;
            }

        }

        public static bool HasPremium()
        {
            var handler = new JwtSecurityTokenHandler();
            var token = handler.ReadToken(Application.Current.Properties["Token"].ToString()) as JwtSecurityToken;
            var premium = token.Claims.FirstOrDefault(claim => claim.Type == "premium").Value.ToString();
            var premiumbool = PropertyToBool(premium);

            return premiumbool;

        }
    }
}

Platform Class (Droid in this example)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Xamarin.Forms;
using TechsportiseApp.Droid;
using static TechsportiseApp.Helpers.GlobalFunctions;
using System.IO;

[assembly: Dependency(typeof(FileHelper))]
namespace TechsportiseApp.Droid
{
    public class FileHelper : IFileHelper
    {
        public string GetLocalFilePath(string filename)
        {
            string path = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal);
            return Path.Combine(path, filename);
        }
    }
}
1
have you used the debugger to step through your code to verify that it's actually doing what you think it is? - Jason

1 Answers

2
votes

You have an inconsistency on your database class. The initialization is done in an instance constructor while the database calls are static, that will lead you to call the static functions even if the instance hasn't been initialized.

Change the functions to instance ones (remove the static qualifier from the functions) or initialize the database class on a static constructor.