.NET Core: Introduction To Microsoft.Data.Sqlite

“Microsoft.Data.Sqlite“, is an open source library and is also available as NuGet package. Here’s the Github source code for reference.

The good thing about this library is, it’s build for .NET Core, meaning you can develop and run applications on Windows and non-Windows platform (Mac, Linux) supporting .NET Core Runtime.

Here’s the quick definition about SQLite from the official website.

“SQLite is a software library that implements a self-contained, serverless, zero-configuration, transactional SQL database engine. SQLite is the most widely deployed database engine in the world”.

SQLite is a very popular, lightweight, and open source database engine and has gained industry popularity. That’s one reason I believe Microsoft wanted to come-up with an official .NET Core based library.

At this point, the .NET Core is evolving. It’s still in pre-release mode. Especially when it comes to “Microsoft.Data.Sqlite,” it supports basic functionalities for managing data in-memory or file system. In the near future, we can expect some changes.

The primary purpose or intention of writing this article is to provide an introduction and usage about “SQLite” library that Microsoft is currently building. Since it’s at the early stage of development, we cannot expect a full documentation. However, I was able to easily understand by taking a look into the source code and unit tests.

Prerequisite

  1. Previous knowledge and understanding of ADO.NET.
  2. Make sure to install .NET Core. Please follow the link if you want install the same on your Window machine.

Using the Code

Let us see with an example to understand the usage of the Microsoft.Data.Sqlite library. We are going to build a tiny cross-platform console application to demonstrate the usage of the SQLite library.

We are going to perform “CRUD” operation on “User” table. Here’s the code snippet for our “User” entity.

  1. public class User  
  2. {  
  3.     public int Id  
  4.     {  
  5.         get;  
  6.         set;  
  7.     }  
  8.     public string Username  
  9.     {  
  10.         get;  
  11.         set;  
  12.     }  
  13.     publicstring Email  
  14.     {  
  15.         get;  
  16.         set;  
  17.     }  
  18.     public string Password  
  19.     {  
  20.         get;  
  21.         set;  
  22.     }  
  23. }  
As I said earlier, you can store all your data in-memory or in file system. We will be focusing on storing in-memory. Below is the connection string for the same.

string connectionString = "Data Source=:memory:";

Let us take a look into the ‘Main’ code to understand the things what we are going to accomplish. We are building a simple user repository so we can encapsulate the “CRUD” operation logic with it.

Below is the code snippet of our “Main” program.
  1. public staticvoid Main(string[] args)  
  2. {  
  3.     string connectionString = "Data Source=:memory:";  
  4.     DbConnetionTypedatabaseType = DbConnetionType.Sqlite;  
  5.   
  6.     using(UserRepositoryuserRepository =  
  7.         new UserRepository(connectionString, databaseType))  
  8.     {  
  9.         Console.WriteLine("Creating user table\n");  
  10.         userRepository.CreateUserTable();  
  11.   
  12.         Console.WriteLine("Inserting data to user table\n");  
  13.         userRepository.InsertIntoUserTable();  
  14.   
  15.         Console.WriteLine("Selecting data\n");  
  16.         userRepository.SelectFromUserTable();  
  17.   
  18.         Console.WriteLine("\nGet User By ID: 1\n");  
  19.         var user = userRepository.GetUserdById(1);  
  20.         if (user != null) {  
  21.             Console.WriteLine("User Name: {0}", user.Username);  
  22.             Console.WriteLine("Email: {0}", user.Email);  
  23.         }  
  24.   
  25.         Console.WriteLine("\nDeleting data\n");  
  26.         userRepository.DeleteFromUserTable();  
  27.   
  28.         Console.WriteLine("Selecting data\n");  
  29.         userRepository.SelectFromUserTable();  
  30.   
  31.         Console.WriteLine("Inserting multiple data to user table\n");  
  32.         userRepository.InsertMultipleWithTransaction();  
  33.   
  34.         Console.WriteLine("Selecting data\n");  
  35.         userRepository.SelectFromUserTable();  
  36.     }  
  37.   
  38.     Console.ReadLine();  
  39. }  
Before we dig into the repository code, there are few basic things we have to code. The below “RC1-final” has the DBConnection extensions as internal. Hence, we won’t be able to access the extension methods within our application as they are private extensions.

"Microsoft.Data.Sqlite": "1.0.0-rc1-final"


So let us reuse and build our own DBConnection Extension. Below is the code snippet for the same. The “ExecuteNonQuery” method is used for insert/update/delete. The ExecuteScalar returns a single value. The “ExecuteReader” is used when you want to fetch some data from a database. All these methods behave similaly to the ones we have in ADO.NET. The below snapshot shows the current .NET framework DbConnection classes. SqliteConnection is something new which is included as part of the “Microsoft.Data.Sqlite” assembly.

dbconnection

Reference:
  1. // Reused and Modified Code - https://github.com/aspnet/Microsoft.Data.Sqlite/blob/dev/src/Microsoft.Data.Sqlite/Utilities/DbConnectionExtensions.cs  
  2. public static class DbConnectionExtensions  
  3. {  
  4.     public static int ExecuteNonQuery(thisDbConnection connection,  
  5.         string commandText, int timeout = 30)   
  6.     {  
  7.         var command = connection.CreateCommand();  
  8.         command.CommandTimeout = timeout;  
  9.         command.CommandText = commandText;  
  10.         return command.ExecuteNonQuery();  
  11.     }  
  12.   
  13.     public static TExecuteScalar < T > (thisDbConnection connection,  
  14.             stringcommandText, int timeout = 30) =>  
  15.         (T) connection.ExecuteScalar(commandText, timeout);  
  16.   
  17.     private static objectExecuteScalar(thisDbConnection connection,  
  18.         string commandText, int timeout)  
  19.     {  
  20.         var command = connection.CreateCommand();  
  21.         command.CommandTimeout = timeout;  
  22.         command.CommandText = commandText;  
  23.         returncommand.ExecuteScalar();  
  24.     }  
  25.   
  26.     public static DbDataReader ExecuteReader(thisDbConnection connection,  
  27.         string commandText)  
  28.     {  
  29.         var command = connection.CreateCommand();  
  30.         command.CommandText = commandText;  
  31.         return command.ExecuteReader();  
  32.     }  
  33. }  
Let us code few more helper classes: one for getting the connection instance and the other for building the parameter instance. Below is the code snippet of ConnectionHelper class. We are coming up without own DbConnectionType. For now, let us have one type – “Sqlite”. The “GetDbConnection” method takes a connection string and DbConnectionType as a parameter and returns a connection instance based on the specified type.
  1. public enum DbConnetionType  
  2. {  
  3.     Sqlite  
  4. }  
  5.   
  6. public class ConnectionHelper  
  7. {  
  8.     public DbConnectionGetDbConnection(stringconnectionString,  
  9.         DbConnectionType type)   
  10.     {  
  11.         switch (type)  
  12.         {  
  13.             caseDbConnetionType.Sqlite:  
  14.                 return newSqliteConnection(connectionString);  
  15.             default:  
  16.             return null;  
  17.         }  
  18.     }  
  19. }  
Below is the code snippet of ParameterHelper class. The “GetParameter” method takes a parameter name, value and connection type as parameter. Based on a specific database type, we are building a Parameter instance, set the parameter name and value and return the same to the caller.
  1. public class ParameterHelper  
  2. {  
  3.     public DbParameter GetParameter(stringparameterName, object value,  
  4.         DbConnetionTypedatabaseType)  
  5.     {  
  6.         switch (databaseType)  
  7.         {  
  8.             case DbConnetionType.Sqlite:  
  9.                 var sqliteParameter = newSqliteParameter  
  10.                 {  
  11.                     ParameterName = parameterName,  
  12.                         Value = value  
  13.                 };  
  14.             return sqliteParameter;  
  15.             default:  
  16.             return null;  
  17.         }  
  18.     }  
  19. }  
Let us take a look into the “UserRepository” code and try to understand the logic within it. Below is the code snippet for creating a “Users” table in SQLite DB. First we need to get the DB connection so we can build the command instance and execute the command specified in the “CommandText”.
  1. public void CreateUserTable()  
  2. {  
  3.     OpenConnection();  
  4.   
  5.     using(var command = connection.CreateCommand())  
  6.     {  
  7.         command.CommandText = @ "  
  8.         CREATE TABLE IF NOT EXISTS Users  
  9.         (  
  10.             [Id] INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, [Username] NVARCHAR(64) NOT NULL, [Email] NVARCHAR(128) NOT NULL, [Password] NVARCHAR(128) NOT NULL  
  11.         )  
  12.         ";  
  13.   
  14.         // Create table if not exist  
  15.         command.ExecuteNonQuery();  
  16.     }  
  17. }  
Now let us see how to insert into our “Users” table. Below is the code snippet demonstrates how one can insert an entry to “Users” table by making a call to “ExecuteNonQuery”, passing in the “INSERT INTO” DML Query.
  1. public void InsertIntoUserTable()  
  2. {  
  3.         OpenConnection();  
  4.   
  5.         using(var command = connection.CreateCommand())  
  6.         {  
  7.             // Insert a record  
  8.             connection.ExecuteNonQuery  
  9.             (@ "INSERT INTO Users(Username, Email, Password) VALUES('admin''testing@gmail.com''test')  
  10.                 ");  
  11.             }  
  12.         }  
Let us take a look into how to query the “Users” table and print the result to Console. Below is the code snippet for the same.
  1. Open a DB connection if we have not already opened a connection.
  2. Based on the connection instance, Create a Command instance.
  3. Set the command text to select from Users table. Make sure to specify only the columns you are interested in.
  4. Execute the command by making a call to “ExecuteReader”. Which will return a DataReader.
  5. Loop through until we have records by making a call to “Read” method of DataReader.
  6. Get the “UserName” and “Email” values from the DataReader by specifying the appropriate ordinal value.
  1. public void SelectFromUserTable()  
  2. {  
  3.     OpenConnection();  
  4.   
  5.     using(var command = connection.CreateCommand())   
  6.     {  
  7.         command.CommandText = "SELECT UserName,Email from Users;";  
  8.         var result = command.ExecuteReader();  
  9.         while (result.Read())  
  10.         {  
  11.             Console.WriteLine(string.Format("UserName: {0}",  
  12.                 result.GetString(0)));  
  13.             Console.WriteLine(string.Format("Email: {0}",  
  14.                 result.GetString(1)));  
  15.         }  
  16.     }  
  17. }  
Let us take a look into the “Delete” functionality. Below is the code snippet for the same. The code is self-explanatory. We are going to execute a DML command to delete all entries from Users table.
  1. public void DeleteFromUserTable()  
  2. {  
  3.     OpenConnection();  
  4.   
  5.     using(var command = connection.CreateCommand())  
  6.     {  
  7.         connection.ExecuteNonQuery("DELETE FROM Users");  
  8.     }  
  9. }  
Now let us take a look into the code for fetching a specific user by “Id.” Below is the code snippet for the same. We are going to build a parameterized query with the parameter name as ‘UserId’ and value as the user id specified in the function parameters. Here comes the role of our “ParameterHelper” helper class for building the parameter. Ones we build the parameter, we are going to add the same to “Command” instance. Finally, the command is executed by making a call to “ExecuteReader” method which returns a single record (if we have one for the specified user id).
  1. public UserGetUserdById(intuserId)  
  2. {  
  3.     try   
  4.     {  
  5.         var command = connection.CreateCommand();  
  6.         command.CommandText = "SELECT * From Users WHERE Id = @UserId;";  
  7.   
  8.         var sqliteParameter = parameterHelper.GetParameter("@UserId",  
  9.             userId, databaseType);  
  10.   
  11.         command.Parameters.Add(sqliteParameter);  
  12.   
  13.         var result = command.ExecuteReader();  
  14.         if (result.Read())  
  15.         {  
  16.             var user = newUser  
  17.             {  
  18.                 Id = result.GetInt32(0),  
  19.                     Username = result.GetString(1),  
  20.                     Email = result.GetString(2),  
  21.                     Password = result.GetString(3)  
  22.             };  
  23.         }  
  24.     } catch (Exception ex)  
  25.     {  
  26.         Console.WriteLine(ex.ToString());  
  27.     }  
  28.   
  29.     returnnull;  
  30. }  
As part of our “UserRepository” implementation, there’s one key thing we have to take a look into. That’s how are we dealing with the “Transaction”. Below is the code snippet for the same. The concept of transaction is same as that of ADO.NET.

From the below code you can see, we are executing the DML query little bit differently, that is directly making a call to “ExecuteNonQuery” of connection instance. That’s possible because of our custom extension class – “DbConnectionExtensions”.

In the below case, we are performing multiple data insert into “Users” table. If everything goes well, we are going to commit the transaction. Say if something goes wrong, the ‘catch’ gets executed so we can rollback the transaction.
  1. public void InsertMultipleWithTransaction()  
  2. {  
  3.         OpenConnection();  
  4.         var transaction = connection.BeginTransaction();  
  5.   
  6.         try  
  7.         {  
  8.             connection.ExecuteNonQuery(@ "INSERT INTO Users(Username, Email, Password)  
  9.                 VALUES('admin1''testing1@gmail.com''test1')  
  10.                 ");  
  11.                 connection.ExecuteNonQuery(@ "INSERT INTO Users(Username, Email, Password)  
  12.                     VALUES('admin2''testing2@gmail.com''test2')  
  13.                     ");  
  14.                     transaction.Commit();  
  15.                 }  
  16.                 catch (Exception ex)  
  17.                 {  
  18.                     Console.WriteLine(ex.ToString());  
  19.                     transaction.Rollback();  
  20.                 } finally  
  21.                 {  
  22.                     transaction.Dispose();  
  23.                 }  
  24.             }  
Application Screenshot

Here’s the console application screenshot.

application

Read more articles on .NET Core: