LiteDB on Xamarin Forms as an Alternative to SQLite

LiteDB On Xamarin Forms
LiteDB On Xamarin Forms

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

Follow me on social media and stay updated

Once 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).

  • Create an interface and name it IDataBaseAccess. As follows :
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);
        }
    }
}
  • 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;
        }
    }
}
  • 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);

        }
    }
}
office 365 16% reduction on signup.
office 365 16% reduction on signup.

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>
  • 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; }
    

    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>();

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);
        }
    }
  • 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>
  • 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);
        }
  • 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();
            }
        }

    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 };
            }

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.

LiteDB on Xamarin Forms Demo
LiteDB on Xamarin Forms Demo

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.

LiteDB on Xamarin Forms LiteDB Explorer
LiteDB on Xamarin Forms LiteDB Explorer

If you liked this post, or it was useful to you, please 👍 like it, share it on twitter, facebook or other social media… in case you want to get updated on any new useful post, follow me on twitter,  Github  and like my page on facebook. And every other social media by clicking on the  buttons at the end of this post.

Follow me on social media and stay updated

Follow me on social media and stay updated

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.