How to use SQLite with Dapper (In ASP.NET Core 3.1)

SQLite is a small, fast, full-featured SQL database engine, that is written in C. And it runs in the same process as the application itself. It is highly reliable and self-contained. In this blog post, I am going to walk through how to use SQLite with Dapper (a Micro-ORM).

Just like any other database, if we want to use the SQLite database engine from an ASP.NET Core application, we will need to work with either ADO.NET or an ORM Framework.

Dapper is a high-performance micro-ORM. and it is one of the easiest to use ORM framework in my opinion. It is built with extension methods on the IDBConnection interface. And it works with most of the popular RDBMS databases out there.

To learn more about how to use Dapper in an ASP.NET Core Application, you can check out my blog post about it here.

An Application to use SQLite with Dapper

To start this example, first of all I will create a new ASP.NET Core Web API Application.

Firstly, to do that, I will open up Visual Studio 2019. Once Visual Studio opens up, I will select the menu File -> New -> Project. This will open the Create a new Project project popup window.

Secondly, in the Create a new Project popup window, I will select ASP.NET Core Web Application from the project template and click on the Next button.

Thirdly, on the next page, I will provide the name of the application as SqliteDapper.Demo and click on the Create button.

Finally, on the final page, I will select the API template option. And I will keep other values default (ASP.Net Core 3.1) and click on the Create button.

NuGet Packages needed

Once the project is ready, I will need to install the necessary NuGet packages. For this demonstration, we will need the following two NuGet packages:

  • Microsoft.Data.Sqlite – This NuGet package is required for SQLite database
  • Dapper – This NuGet package is required for Dapper

I will install both the packages using the NuGet Package manager of Visual Studio.

The version I will install for Microsoft.Data.Sqlite is 3.1.6 as of writing this blog post.

And the version I will install for Dapper is 2.0.35 as of writing this blog post.

Database Setup

Once the NuGet packages are installed, it is time to write code to set up the database with tables needed for this demonstration. Now for this project created I am going to use a File-based option for SQLite.

Tough we can use SQLite to store data only in memory as well. The in-memory version of SQLite comes very handy for unit testing.

For creating the database tables, we can use migration like FluentMigrator. But for this demonstration, I am going to use plain SQL statements along with Dapper for creating SQLite tables.

You can learn more about FluentMigrator in my blog post here.

Database Connection

I will keep the database file name (the complete connection string to the SQLite database) in the configuration file.

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "DatabaseName": "Data Source=Product.sqlite"
}

For keeping all database migration-related types, I will create a folder “Database” inside the project. And for the ease of dependency injection, I will create a class DatabaseConfig inside the “Database” folder.

The DatabaseConfig class will have a single property, Name. The Name property will contain the database connection configuration. I will set it up later in the Startup class.

namespace SqliteDapper.Demo.Database
{
    public class DatabaseConfig
    {
        public string Name { get; set; }
    }
}

DatabaseBootstrap class

For the demonstration, I will create a single table Product in the SQLite database. And use Dapper to create and read records from the database table.

To create the table Product when the application starts up, I will create a new class DatabaseBootstrap inside the “Database” folder.

The DatabaseBootstrap class will have a single method Setup. And the DatabaseBootstrap class will implement the interface IDatabaseBootstrap.

The DatabaseBootstrap class will have a constructor, and the constructor will have a dependency on the DatabaseConfig class for accessing the database connection string from the configuration.

Inside the Setup method, I will first create a connection to the SQLite database passing the connection from config.

Once the connection is established, I will check if the table already exists or not. If it exists, then I will not do anything. And if the table does not exist, then I will create the table.

For the Product table, I will keep only two columns Name and Description for simplicity.

Since I am not specifying any primary key, the SQLite will create the auto-generated primary key rowid. Which is an auto-incremented identity column created by SQLite to tables where we do not specify any primary key.

namespace SqliteDapper.Demo.Database
{
    public interface IDatabaseBootstrap
    {
        void Setup();
    }
}

using Dapper;
using Microsoft.Data.Sqlite;
using System.Linq;

namespace SqliteDapper.Demo.Database
{
    public class DatabaseBootstrap : IDatabaseBootstrap
    {
        private readonly DatabaseConfig databaseConfig;

        public DatabaseBootstrap(DatabaseConfig databaseConfig)
        {
            this.databaseConfig = databaseConfig;
        }

        public void Setup()
        {
            using var connection = new SqliteConnection(databaseConfig.Name);

            var table = connection.Query<string>("SELECT name FROM sqlite_master WHERE type='table' AND name = 'Product';");
            var tableName = table.FirstOrDefault();
            if (!string.IsNullOrEmpty(tableName) && tableName == "Product")
                return;

            connection.Execute("Create Table Product (" +
                "Name VARCHAR(100) NOT NULL," +
                "Description VARCHAR(1000) NULL);");
        }
    }
}

Creating and retrieving record from SQLite

Now that the database table is ready, it is time to create and retrieving data from the database.

I will create a new folder “ProductMaster” in the project. I will keep all the files for data retrieval and creation inside of this folder.

Inside the “ProductMaster”, I will first create the Product model class. The Product class will have three properties. The Id of the Product, which is the rowid provided by the SQLite, and it is an integer. The Name of the product, which is a string. And finally the Description of the product, which is also a string.

namespace SqliteDapper.Demo.ProductMaster
{
    public class Product
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Description { get; set; }
    }
}

Creating Product data in SQLite database

For creating data in the Product table of the SQLite database, I will create a new class ProductRepository. This class will have a single method Create. The class ProductRepository will implement the interface IProductRepository.

The Create method will be an async method. And the Create method will take the Product object as an input parameter.

Inside the Create method of the ProductRepository class, I will first create a new insistence of SqliteConnection to establish a connection to the SQLite database.

Once the connection is established, I will use the ExecuteAsync extension method on the IDBConnection instance provided by Dapper, to create a new Product record in the Product table.

using System.Threading.Tasks;

namespace SqliteDapper.Demo.ProductMaster
{
    public interface IProductRepository
    {
        Task Create(Product product);
    }
}
using Dapper;
using Microsoft.Data.Sqlite;
using SqliteDapper.Demo.Database;
using System.Threading.Tasks;

namespace SqliteDapper.Demo.ProductMaster
{
    public class ProductRepository : IProductRepository
    {
        private readonly DatabaseConfig databaseConfig;

        public ProductRepository(DatabaseConfig databaseConfig)
        {
            this.databaseConfig = databaseConfig;
        }

        public async Task Create(Product product)
        {
            using var connection = new SqliteConnection(databaseConfig.Name);

            await connection.ExecuteAsync("INSERT INTO Product (Name, Description)" +
                "VALUES (@Name, @Description);", product);
        }
    }
}

Retrieving Product data from SQLite database

Once we have the class to create a Product record in the Product table, it is time to create a class to retrieve the data from the table inside the SQLite with Dapper.

For data retrieval, I will create a new class ProductProvider. The class ProductProvider will have a single method Get, which will return all the product information from the Product table. The class ProductProvider will implement the interface IProductProvider.

The Get method will be an async method and it will return IEnumerable of Product. It will not take any input parameters, since it will return all the products in the Product table in the SQLite database.

Inside the Get method, I will first establish a new connection to the SQLite database. For establishing a new connection, I will create a new instance of the SqliteConnection class.

Once the connection is established, I will use the QueryAsync extension method of Dapper on the IDBConnection instance to retrieve the Product information from the Product table in the SQLite database.

using System.Collections.Generic;
using System.Threading.Tasks;

namespace SqliteDapper.Demo.ProductMaster
{
    public interface IProductProvider
    {
        Task<IEnumerable<Product>> Get();
    }
}
using Dapper;
using Microsoft.Data.Sqlite;
using SqliteDapper.Demo.Database;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace SqliteDapper.Demo.ProductMaster
{
    public class ProductProvider : IProductProvider
    {
        private readonly DatabaseConfig databaseConfig;

        public ProductProvider(DatabaseConfig databaseConfig)
        {
            this.databaseConfig = databaseConfig;
        }

        public async Task<IEnumerable<Product>> Get()
        {
            using var connection = new SqliteConnection(databaseConfig.Name);

            return await connection.QueryAsync<Product>("SELECT rowid AS Id, Name, Description FROM Product;");
        }
    }
}

Creating new API Controller

Now that classes for both saving and retrieving of data into and from SQLite with Dapper are ready, it is time to expose these classes through an external API. So that we can save as well as retrieve Product information through HTTP endpoint.

To achieve this, we will create a new controller class to expose these components. I will name the new controller as ProductController.

The ProductController class will contain a constructor for creating dependency on the IProductProvider and the IProductRepository interfaces. This is needed so that the ProductController class can use these interfaces for saving and retrieving data through its GET and POST HTTP methods.

The HTTP GET method of the controller will use the instance of the interface IProductProvider to return all the Products from the Product table. Hence it will return an IEnumerable of the Product type.

And the HTTP POST method of the controller will use an instance of the interface IProductRepository to save new Products into the Product table of the SQLite database. Hence it will have a Product instance as an input parameter.

using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using SqliteDapper.Demo.ProductMaster;

namespace SqliteDapper.Demo.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class ProductController : ControllerBase
    {
        private readonly IProductProvider productProvider;
        private readonly IProductRepository productRepository;

        public ProductController(IProductProvider productProvider,
            IProductRepository productRepository)
        {
            this.productProvider = productProvider;
            this.productRepository = productRepository;
        }

        // GET: api/<ProductController>
        [HttpGet]
        public async Task<IEnumerable<Product>> Get()
        {
            return await productProvider.Get();
        }


        // POST api/<ProductController>
        [HttpPost]
        public async Task Post([FromBody] Product product)
        {
            await productRepository.Create(product);
        }
    }
}

Wiring up in Startup

Now we are done with all the code needed to access SQLite with Dapper. It is now the time to wire all the types up in the dependency injection container.

To achieve this, I will update the ConfigureServices method of the Startup class to register all the new classes and interfaces created into the dependency injection container.

// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers();

    services.AddSingleton(new DatabaseConfig { Name = Configuration["DatabaseName"] });

    services.AddSingleton<IDatabaseBootstrap, DatabaseBootstrap>();
    services.AddSingleton<IProductProvider, ProductProvider>();
    services.AddSingleton<IProductRepository, ProductRepository>();
}

Finally, I will update the Configure method of the Startup class to set up the database by creating the Product table.

To do that, I will first update the Configure method to accept a new parameter of the type IServiceProvider. Once I add this new parameter, the .Net Core framework will pass an instance of IServiceProvider to this method.

And finally, at the end of the Configure method, I will get a handle to the IDatabaseBootstrap from the dependency injection container. I will use the GetService generic method of the IServiceProvider instance to achieve this.

And once I get the instance of IDatabaseBootstrap, I will call the Setup method on the instance to set up the database bt creating the Product table. That will create the Product table if it does not yet exist.

// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, 
            IWebHostEnvironment env,
            IServiceProvider serviceProvider)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }

    app.UseHttpsRedirection();

    app.UseRouting();

    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllers();
    });

    serviceProvider.GetService<IDatabaseBootstrap>().Setup();
}

Running the application

Now I will run the application to check the result. To test out if everything is working as expected, first of all, I will create a new Product in the Product table in the SQLite database. To create a new Product I will need to use the HTTP POST method of the ProductController class.

I will use Postman to fire the HTTP POST request to the https://localhost:5001/api/product endpoint. And in the request body, I will pass the Product information in the form of JSON.

sqlite with dapper

When I send the Postman request to the Product API, I can see 200 response back from the server. This confirms the Product is added successfully to the Product table.

Next, I will hit the URL https://localhost:5001/api/product in the browser to see the newly inserted Product information. This will trigger an HTTP GET call to the ProductController class.

GET response

And I can see the product information showing up as expected in the browser.

Conclusion

Working with SQLite with Dapper is a breeze. It is super simple and extremely stable. When it comes to Dapper, there is nothing special that needs to be done for it to work with SQLite.

I was pleasantly surprised at how easy it was to get Dapper to work with SQLite. We just needed to install a couple of NuGet packages, and after that, it was very straight forward implementation.

I have captured the entire coding session in a video and it is available on YouTube here.

The source code for this blog post is available on my GitHub here.