Xamarin ,

Xamarin ,

Hello guys what’s up ?. When building a Xamarin Forms application, persisting data locally is done mostly with SQLite. SQLite is widely used to persist data in mobile applications. While SQLite is widely used and there are libraries which function as ORM for interactions with SQLite databases in .Net applications, you may need another solution to persist your data. For one reason or another, you may want to persist your data with a NoSQL database instead. I have been using SQLite, on every mobile app which I needed to persist data on. But I needed something different, which works better for me.

Then I asked the all mighty Google. He gave me the perfect solution to LiteDB.

What is LiteDB

As stated on the LiteDB Website, LiteDB is serverless database delivered in a single DLL (less than 350kb) fully written in .NET C# managed code (compatible with .NET 3.5, 4.x, NETStandard 1.3 and 2.0).

Lite DB was inspired from MongoDB and it is extremely easy to get started with it. And obviously, it is a NoSQL database. LiteDB stores documents in the BSON (Binary JSON) data format. Enough introduction, let’s get started with implementing LiteDB on Xamarin Forms.

LiteDB on Xamarin Forms

LiteDB is compatible with .Net Standards 2.0, Xamarin.iOX, Xamarin.Android, and UWP thus can be used to build Xamarin Forms applications. Here is how it is done.

What we will be doing

Create a new Xamarin Forms app and install LiteDB

Build a very simple Todo app

Integrate LiteDB to persist data in our Todo application.

Here is the source code for this tutorial.

Create a new Xamarin Forms app and install LiteDB

In your Visualstudio IDE, Create a new Xamarin Forms application

Open nuget package manager window at the solution level

Install the LiteDB 4.1.3 package

This Xamarin Forms application to help you manage your expenses and income was built entirely with Xamarin Forms and ReactiveUI. You can download and use it or play with it for free on Android and Windows 10 (Universal Windows Platform). You can get it on Playstore, or on the Microsoft Store.

Like this: Like Loading...

Create an interface and name it IDataBaseAccess. As follows :

Follow me on social media and stay updatedOnce you have installed the package, we need to determine where exactly our databases will be stored. And we do so on every platform. We will do this with the Dependency service provided by Xamarin Forms. (NB: I used this approach per platform to safely get a valid location for my database. And avoid any issue related to where the database file is stored on each platform).

public interface IDataBaseAccess { string DatabasePath(); } 1 2 3 4 public interface IDataBaseAccess { string DatabasePath ( ) ; }

On UWP implement this interface as follows :

[assembly: Xamarin.Forms.Dependency(typeof(DatabaseAccess))] namespace LiteDBXF.UWP { public class DatabaseAccess : IDataBaseAccess { public string DatabasePath() { var d = Path.Combine(ApplicationData.Current.LocalFolder.Path, Constants.OFFLINE_DATABASE_NAME); return Path.Combine(ApplicationData.Current.LocalFolder.Path, Constants.OFFLINE_DATABASE_NAME); } } } 1 2 3 4 5 6 7 8 9 10 11 12 [ assembly : Xamarin . Forms . Dependency ( typeof ( DatabaseAccess ) ) ] namespace LiteDBXF . UWP { public class DatabaseAccess : IDataBaseAccess { public string DatabasePath ( ) { var d = Path . Combine ( ApplicationData . Current . LocalFolder . Path , Constants . OFFLINE_DATABASE_NAME ) ; return Path . Combine ( ApplicationData . Current . LocalFolder . Path , Constants . OFFLINE_DATABASE_NAME ) ; } } }

Android

[assembly: Xamarin.Forms.Dependency(typeof(DatabaseAccess))] namespace LiteDBXF.Droid { public class DatabaseAccess : IDataBaseAccess { public string DatabasePath() { var path = Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal), Constants.OFFLINE_DATABASE_NAME); if (!File.Exists(path)) { File.Create(path).Dispose(); } return path; } } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 [ assembly : Xamarin . Forms . Dependency ( typeof ( DatabaseAccess ) ) ] namespace LiteDBXF . Droid { public class DatabaseAccess : IDataBaseAccess { public string DatabasePath ( ) { var path = Path . Combine ( System . Environment . GetFolderPath ( System . Environment . SpecialFolder . Personal ) , Constants . OFFLINE_DATABASE_NAME ) ; if ( ! File . Exists ( path ) ) { File . Create ( path ) . Dispose ( ) ; } return path ; } } }

iOS

[assembly: Xamarin.Forms.Dependency(typeof(DatabaseAccess))] namespace LiteDBXF.iOS { public class DatabaseAccess : IDataBaseAccess { public string DatabasePath() { string docFolder = Environment.GetFolderPath(Environment.SpecialFolder.Personal); string libFolder = Path.Combine(docFolder, "..", "Library", "Databases"); if (!Directory.Exists(libFolder)) { Directory.CreateDirectory(libFolder); } return Path.Combine(libFolder, Constants.OFFLINE_DATABASE_NAME); } } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 [ assembly : Xamarin . Forms . Dependency ( typeof ( DatabaseAccess ) ) ] namespace LiteDBXF . iOS { public class DatabaseAccess : IDataBaseAccess { public string DatabasePath ( ) { string docFolder = Environment . GetFolderPath ( Environment . SpecialFolder . Personal ) ; string libFolder = Path . Combine ( docFolder , ".." , "Library" , "Databases" ) ; if ( ! Directory . Exists ( libFolder ) ) { Directory . CreateDirectory ( libFolder ) ; } return Path . Combine ( libFolder , Constants . OFFLINE_DATABASE_NAME ) ; } } }

Special Offer

Save 16% on your Microsoft Office 365 subscription Learn More >>

Lets Build a very simple Todo App

In other to really demonstrate how to use LiteDB on Xamarin Forms we need to integrate data access in a real app. So we will do so with a very simple to do application. This application is almost the same as that which we built to demostrate ReactiveUI on Xamarin.Forms. If you want a detail guide on building the sample app or to learn more about building a Xamarin.Forms app with Reactiveui, check it here.

Add a reference to Reactiveui’s nuget package on Xamarin Forms. At the Solution level.

The app’s page will be very simple. A ListView to display todo items, a textview and a button to create to do items.

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:local="clr-namespace:LiteDBXF" xmlns:vm="clr-namespace:LiteDBXF.ViewModel" x:Class="LiteDBXF.MainPage"> <ContentPage.BindingContext> <vm:TodoViewModel/> </ContentPage.BindingContext> <StackLayout> <ListView x:Name="MyListView" ItemsSource="{Binding Todos}" SelectedItem="{Binding SelectedTodo}" CachingStrategy="RecycleElement"> <!--Custom View Cells--> <ListView.ItemTemplate> <DataTemplate> <ViewCell IsEnabled="{Binding IsEnabled}"> <StackLayout Orientation="Horizontal"> <Label Text="{Binding Title}" Style="{DynamicResource ListItemTextStyle}" HorizontalOptions="Start"/> <Switch IsToggled="{Binding IsDone}" HorizontalOptions="End" IsEnabled="{Binding IsEnabled}"/> </StackLayout> </ViewCell> </DataTemplate> </ListView.ItemTemplate> </ListView> <StackLayout Orientation="Horizontal"> <Entry Text="{Binding TodoTitle}" HorizontalOptions="FillAndExpand"/> <Button Text="Add" HorizontalOptions="End" Command="{Binding AddCommand}"/> </StackLayout> </StackLayout> </ContentPage> 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 < ContentPage xmlns = "http://xamarin.com/schemas/2014/forms" xmlns : x = "http://schemas.microsoft.com/winfx/2009/xaml" xmlns : local = "clr-namespace:LiteDBXF" xmlns : vm = "clr-namespace:LiteDBXF.ViewModel" x : Class = "LiteDBXF.MainPage" > < ContentPage . BindingContext > < vm : TodoViewModel / > < / ContentPage . BindingContext > < StackLayout > < ListView x : Name = "MyListView" ItemsSource = "{Binding Todos}" SelectedItem = "{Binding SelectedTodo}" CachingStrategy = "RecycleElement" > < ! -- Custom View Cells -- > < ListView . ItemTemplate > < DataTemplate > < ViewCell IsEnabled = "{Binding IsEnabled}" > < StackLayout Orientation = "Horizontal" > < Label Text = "{Binding Title}" Style = "{DynamicResource ListItemTextStyle}" HorizontalOptions = "Start" / > < Switch IsToggled = "{Binding IsDone}" HorizontalOptions = "End" IsEnabled = "{Binding IsEnabled}" / > < / StackLayout > < / ViewCell > < / DataTemplate > < / ListView . ItemTemplate > < / ListView > < StackLayout Orientation = "Horizontal" > < Entry Text = "{Binding TodoTitle}" HorizontalOptions = "FillAndExpand" / > < Button Text = "Add" HorizontalOptions = "End" Command = "{Binding AddCommand}" / > < / StackLayout > < / StackLayout > < / ContentPage >

Create a ViewModel named TodoViewModel and here are the properties bound to the View

/// <summary> /// Reactive List https://reactiveui.net/docs/handbook/collections/reactive-list /// </summary> ReactiveList<Todo> _todos; public ReactiveList<Todo> Todos { get => _todos; set => this.RaiseAndSetIfChanged(ref _todos, value); } private Todo _selectedTodo; public Todo SelectedTodo { get => _selectedTodo; set => this.RaiseAndSetIfChanged(ref _selectedTodo, value); } private string _todoTitl; public string TodoTitle { get { return _todoTitl; } set { this.RaiseAndSetIfChanged(ref _todoTitl, value); } } public ReactiveCommand AddCommand { get; private set; } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 /// <summary> /// Reactive List https://reactiveui.net/docs/handbook/collections/reactive-list /// </summary> ReactiveList < Todo > _todos ; public ReactiveList < Todo > Todos { get = > _todos ; set = > this . RaiseAndSetIfChanged ( ref _todos , value ) ; } private Todo _selectedTodo ; public Todo SelectedTodo { get = > _selectedTodo ; set = > this . RaiseAndSetIfChanged ( ref _selectedTodo , value ) ; } private string _todoTitl ; public string TodoTitle { get { return _todoTitl ; } set { this . RaiseAndSetIfChanged ( ref _todoTitl , value ) ; } } public ReactiveCommand AddCommand { get ; private set ; } Integrate LiteDB to persist data in our Todo application. Integrating LiteDb in our Xamarin Forms app will be very easy. LiteDB use Collections to access data.

Create a new class named LiteDBService<T>. This will contain basic code to access our data.

In this class’ constructor, we instantiate a LiteDabase object by passing it the path to our database (We use the dependency service to get the path from each platform).

From the LiteDatabase object, we get the collection of data of type T. If this collection does not exist in the database, it is created. Here is how it is done in code.

var db = new LiteDatabase(DependencyService.Get<IDataBaseAccess>().DatabasePath()); _collection = db.GetCollection<T>(); 1 2 var db = new LiteDatabase ( DependencyService . Get < IDataBaseAccess > ( ) . DatabasePath ( ) ) ; _collection = db . GetCollection < T > ( ) ;

Here is the full code for this service.

public class LiteDBService<T> { protected LiteCollection<T> _collection; public LiteDBService() { var db = new LiteDatabase(DependencyService.Get<IDataBaseAccess>().DatabasePath()); _collection = db.GetCollection<T>(); } public virtual T CreateItem(T item) { var val = _collection.Insert(item); return item; } public virtual T UpdateItem(T item) { _collection.Update(item); return item; } public virtual T DeleteItemAsync(T item) { var c = _collection.Delete(i => i.Equals(item)); return item; } public virtual IEnumerable<T> ReadAllItems() { var all = _collection.FindAll(); return new List<T>(all); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 public class LiteDBService < T > { protected LiteCollection < T > _collection ; public LiteDBService ( ) { var db = new LiteDatabase ( DependencyService . Get < IDataBaseAccess > ( ) . DatabasePath ( ) ) ; _collection = db . GetCollection < T > ( ) ; } public virtual T CreateItem ( T item ) { var val = _collection . Insert ( item ) ; return item ; } public virtual T UpdateItem ( T item ) { _collection . Update ( item ) ; return item ; } public virtual T DeleteItemAsync ( T item ) { var c = _collection . Delete ( i = > i . Equals ( item ) ) ; return item ; } public virtual IEnumerable < T > ReadAllItems ( ) { var all = _collection . FindAll ( ) ; return new List < T > ( all ) ; } }

This service is just the base, now let us customize it to suit data operations on todo items. to do so, we create a class which inherits from out LiteDb service.

TodoLiteDBService : LiteDBService<Todo> 1 TodoLiteDBService : LiteDBService < Todo >

Each item saved in LiteDB has an Id property to uniquely identify it. So You need to tell LiteDB which property on our object will act as it’s ID. You can do so using attributes or a mapper. Here we will use Mappers. For our todo items, the ID property has to be considered as its ID, so let’s say it to LiteDB.

//In the constructor, we add this public TodoLiteDBService() { var mapper = BsonMapper.Global; mapper.Entity<Todo>() .Id(x => x.ID); } 1 2 3 4 5 6 7 8 //In the constructor, we add this public TodoLiteDBService ( ) { var mapper = BsonMapper . Global ; mapper . Entity < Todo > ( ) . Id ( x = > x . ID ) ; }

Before saving an Item in LiteDB, we have to assign its ID a value, this will be done using GUIDS which are randomly created in our code. And, this is done before saving the Todo Item. Here is the full code for this class.

Here are the Crud operations in LiteDB

public class TodoLiteDBService : LiteDBService<Todo> { public TodoLiteDBService() { var mapper = BsonMapper.Global; mapper.Entity<Todo>() .Id(x => x.ID); } public override Todo CreateItem(Todo item) { item.ID = Guid.NewGuid().ToString(); return base.CreateItem(item); } public override Todo DeleteItemAsync(Todo item) { var c = _collection.Delete(i => i.ID == item.ID); return c == 0 ? null : item; } public override Todo UpdateItem(Todo item) { return base.UpdateItem(item); } public override IEnumerable<Todo> ReadAllItems() { return base.ReadAllItems(); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 public class TodoLiteDBService : LiteDBService < Todo > { public TodoLiteDBService ( ) { var mapper = BsonMapper . Global ; mapper . Entity < Todo > ( ) . Id ( x = > x . ID ) ; } public override Todo CreateItem ( Todo item ) { item . ID = Guid . NewGuid ( ) . ToString ( ) ; return base . CreateItem ( item ) ; } public override Todo DeleteItemAsync ( Todo item ) { var c = _collection . Delete ( i = > i . ID == item . ID ) ; return c == 0 ? null : item ; } public override Todo UpdateItem ( Todo item ) { return base . UpdateItem ( item ) ; } public override IEnumerable < Todo > ReadAllItems ( ) { return base . ReadAllItems ( ) ; } }

Now, it is time to use this service in the ViewModel to save data.

Now, it is time to use this service in the ViewModel to save data. Create a TodoLiteDBService object to access data.

When a new Todo item is created, this object saves it. When it is set as done, the object updates it. And when the view model is created, the todo items in the database are loaded. Here is the code for these operations.

//New Todo Created AddCommand = ReactiveCommand.Create(() => { var todo = new Todo() { Title = TodoTitle }; Todos.Add(todo); TodoTitle = string.Empty; _liteDBService.CreateItem(todo); }, this.WhenAnyValue(x => x.TodoTitle, title => !String.IsNullOrEmpty(title))); //If the todo is marked as completed, it is updated Todos.ItemChanged.Where(x => x.PropertyName == "IsDone" && x.Sender.IsDone) .Select(x => x.Sender) .Subscribe(x => { if (x.IsDone) { Todos.Remove(x); Todos.Add(x); _liteDBService.UpdateItem(x); } }); //The todos are all loaded from the database _liteDBService = new TodoLiteDBService(); var todos = _liteDBService.ReadAllItems(); if (todos.Any()) { Todos = new ReactiveList<Todo>(todos) { ChangeTrackingEnabled = true }; } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 //New Todo Created AddCommand = ReactiveCommand . Create ( ( ) = > { var todo = new Todo ( ) { Title = TodoTitle } ; Todos . Add ( todo ) ; TodoTitle = string . Empty ; _liteDBService . CreateItem ( todo ) ; } , this . WhenAnyValue ( x = > x . TodoTitle , title = > ! String . IsNullOrEmpty ( title ) ) ) ; //If the todo is marked as completed, it is updated Todos . ItemChanged . Where ( x = > x . PropertyName == "IsDone" && x . Sender . IsDone ) . Select ( x = > x . Sender ) . Subscribe ( x = > { if ( x . IsDone ) { Todos . Remove ( x ) ; Todos . Add ( x ) ; _liteDBService . UpdateItem ( x ) ; } } ) ; //The todos are all loaded from the database _liteDBService = new TodoLiteDBService ( ) ; var todos = _liteDBService . ReadAllItems ( ) ; if ( todos . Any ( ) ) { Todos = new ReactiveList < Todo > ( todos ) { ChangeTrackingEnabled = true } ; }

Conclusion

With this, you have a sample application which shows how to use LiteDB on Xamarin Forms to persist data. Here is what this sample should look like on UWP.

You also have to possiblity to access the data stored in your database and manipulate it outside the app. Use the LiteDB explorer. Access the database files from the paths which we precised per platform above and open these files with LiteDB explorer.

If this post was useful to you, please share it and follow me on Twitter, Github or like my Facebook page.

Like this: Like Loading...

Like this: Like Loading...

Follow me on social media and stay updatedFollow me on social media and stay updated