This topic is going to be sort of continuation of my previous post about SQLiteWrapperUWP-PCL – an SQLite Wrapper for Windows 10 UWP applications. I'm writing “sort of” because it’s actually an update to that post. The project part – setting up the solution – hasn’t changed but the code part has. Please understand that the SQLiteWrapperUWP-PCL library is being constantly developed and improved, hence I need to explain how things has changed and how one should implement their database logic with latest release of SQLiteWrapperUWP-PCL NuGet package BuildIt SQLite.
For those who are not very familiar with Windows 10 and UWP application development please, before you start going through what’s written in here, refer to my previous post about SQLite Wrapper for Windows 10 UWP applications, where you can find guidelines how to set up your solution / project.
For those who can’t be bothered reading and/or want to figure things out by themselves I recommend looking at the GitHub repository GitHub repository where you can find some short guidance / information about how things work with SQLiteWrapperUWP-PCL BuildIt (Sqlite). Also there’s a CRUD database operations example CRUD database operations example which could be a good place to start your explorations.
EDIT: Apologies to all that wanted to try out the SQLiteWrapperUWP-PCL and some of the links weren’t working, but SQLiteWrapperUWP-PCL evolved from its own existence, to become a part of a bigger set of libraries, called BuildIt. Those libraries are a foundation that we rely on, in almost every project that we create @BuiltToRoam, so besides working with SQLite check out others, I can bet you will like it and use it.
The Code
Let’s start from the Core project and setting up the DatabaseService.
public class DatabaseService : BasicDatabaseService, IDatabaseService
{
public DatabaseService(ISqlitePlatformProvider sqlitePlatformProvider, IDatabaseNameProvider databaseNameProvider, ILocalFileService localFileService)
: base(sqlitePlatformProvider, databaseNameProvider, localFileService)
{
}
protected override void CreateDatabaseTables(SQLiteConnection dbConnection)
{
dbConnection?.CreateTable<PersonEntity>();
}
}
The base class, that DatabaseService
inherits from, is an abstract class BasicDatabaseService
which is a exemplary implementation of BaseDatabaseService
. Both of which sit in the SQLiteWrapperUWP-PCL library and expose the IBasicDatabaseService
and IBaseDatabaseService
interfaces. In this case, deriving from Basic service, one have to provide only the implementation of CreateDatabaseTables
method, which is responsible for creating database tables, and provide some constructor parameters, about which in a second.
I think it’s worth noting that one is left with an option to derive directly from BaseDatabaseService
and in that case it opens a path where one could provide their own SQLite database connection creation (implement CreateSQLiteConnection
method) handling.
Let’s explain a bit about DatabaseService
constructor parameters and its purpose.
ISqlitePlatformProvider
consists of one child, and its purpose is to provide core SQLite platform functionality, basically it's the "heart" or "spine" to the SQLite database.IDatabaseNameProvider
is nothing more but a “fancy” way of saying “if you want a database you need to give it a name it”. With this interface implementation you need to provide a database name, and that’s it – one string- Last but not least is the
ILocalFileService
which is necessary to provide a way of getting physical path –RetrieveNativePath
method – where the database file will be saved (on a hard drive)
You can find exemplary implementation of all of those in the GitHub repository
NOTE: In almost all of my projects I use mvvm cross-platform framework MvvmCross, which I highly recommend, and with it, out of the box, comes Dependency Injection (IoC) mechanisms. Those mechanism under the hood, create and inject automatically singletons for mentioned above interfaces (constructor parameters). You can find out more about it in the N+1 days of MvvmCross blog posts and videos. For those who are familiar with it and to spare you some time looking for how it could be initialized, here’s a sample code from Setup.cs file
protected override void InitializeIoC()
{
base.InitializeIoC();
Mvx.LazyConstructAndRegisterSingleton<ISqlitePlatformProvider, SqlitePlatformProvider>();
Mvx.LazyConstructAndRegisterSingleton<ILocalFileService, LocalFileService>();
Mvx.LazyConstructAndRegisterSingleton<IDatabaseNameProvider, DatabaseNameProvider>();
}
Important thing, that has been changed in the latest release, is BaseEntity
class. It was redesign to consists the update of representation of the entity logic (update of the database record). For example:
public class PersonEntity : BaseEntity<PersonEntity>
{
public string Name { get; set; }
public string Surname { get; set; }
public override bool UpdateFromEntity(PersonEntity entityToBeUpdated, PersonEntity entitySource)
{
// Checks if the ID's of those two entites are the same
if (base.UpdateFromEntity(entityToBeUpdated, entitySource))
{
entityToBeUpdated.Name = entitySource.Name;
entityToBeUpdated.Surname = entitySource.Surname; // Update changed valuess
return true;
}
// Do not update
return false;
}
}
There’re two things to note in the above code. First is to do with self-reference generic class in the class declaration. It was purely designed this way so we could handle the update logic inside of the Entity
class, which is called from the BaseRepository
every time the Update
logic on the entity is executed. UpdateFromEntity
method – second thing that one should be aware of – should be a place where entity update is being handled.
Assuming you have an instance of DatabaseService
handy (e.g. created with MvvmCross DI mechanisms) and with all this setup / knowledge we can now start working with our database, and it couldn’t be any easier. To create new database record (entity) one could write something as follows:
var newPerson = new PersonEntity()
{
Name = Constants.Nick,
Surname = Constants.Cage
};
var createResult = await databaseService.InsertOrUpdate(newPerson);
To retrieve all the table records or just one:
var persons = await databaseService.Get<PersonEntity>(); // all
var person = await databaseService.Get<PersonEntity>(<entity_id>); // one
To delete record:
var delRes = await databaseService.Delete<PersonEntity>(<entity_id>);
Those are Base CRUD methods, but if you’d like to write more sophisticated queries you should look into BaseRepository
and Table
property. It will allow you to perform some LINQ queries on it. For example:
var database = await CreateSQLiteConnection();
if (database == null)
{
return null;
}
using (var repo = new BaseRepository<PersonEntity>(database))
{
return repo.Table.Where(p => p.Surname.Contains(<something>)).ToList();
}
NOTE: As it’s not perfectly obvious, but the CreateSQLiteConnection
method won’t create a newconnection every time the method has been invokde, instead it will use previously cached one.
NOTE: Another thing is, that not all LINQ queries will be valid in terms of translating them to what database understands, hence sometimes it’s better to grab all table records (entities) and perform some queries on the values stored in memory.
I would highly recommend checking out the GitHub repository and the sample that is in it, as it consists of all of what I’ve just wrote about, and you can easily compile and debug that code.
NOTE: For inspecting SQLite database file(s) I recommend using SQLiteBrowser