VB.NET SQLite – Connect, Read & Write Data (2026)

SQLite is the easiest way to add a local database to your VB.NET application, no server, no installation, no configuration. In this guide I’ll show you step by step how to set up SQLite via NuGet, create tables, and perform all CRUD operations with parameterized queries. Full working example included. → Go to downloads.

Need database help?

Struggling with database integration in .NET?

I've been developing professionally in VB.NET and C# for over 17 years. Whether it's SQLite, SQL Server, or data architecture – I can help.

Why SQLite for your VB.NET application?

There are many database engines available, but SQLite stands out for local VB.NET applications. Here’s why:

  • Zero setup – no server process, no installation, no admin rights needed
  • Single file – the entire database lives in one .db file you can ship with your app
  • Cross-platform – works on Windows, Linux, and macOS
  • Lightweight – perfect for desktop apps, prototypes, embedded systems, and offline-first scenarios
  • Reliable – used by Firefox, Chrome, Android, and millions of other applications

If you just need to persist settings or small amounts of data, you could also write a text file in VB.NET. But as soon as you need structured data, queries, or relationships, SQLite is the better choice.

Installing the NuGet package

There are two popular SQLite packages for .NET:

  1. Microsoft.Data.Sqlite – Microsoft’s official, lightweight package (recommended for .NET 5+ / .NET Framework 4.6.1+)
  2. System.Data.SQLite – the older, full-featured package from the SQLite team (supports even .NET Framework 4.0)

In this article I’ll use Microsoft.Data.Sqlite because it’s lighter and the recommended path going forward. The API is almost identical, so switching later is straightforward.

Install via Package Manager Console

Open the Package Manager Console in Visual Studio (Tools → NuGet Package Manager → Package Manager Console) and run:

Install-Package Microsoft.Data.Sqlite

Alternatively, right-click your project in Solution Explorer → Manage NuGet Packages → search for Microsoft.Data.Sqlite → Install.

Add the Imports statement

At the top of your code file, add the following import:

Imports Microsoft.Data.Sqlite

Creating a database and connecting

A big advantage of SQLite: you don’t need to „create“ a database manually. If the file doesn’t exist yet, SQLite creates it automatically when you open a connection.

The connection string

The SQLite connection string is minimal. All you need is the path to the database file:

Dim connectionString As String = "Data Source=myapp.db"

This creates the file myapp.db in the application’s working directory. If you want to place the database next to your executable, you can use the VB.NET Application Path to build a reliable path:

Dim dbPath As String = Path.Combine(AppContext.BaseDirectory, "myapp.db")
Dim connectionString As String = $"Data Source={dbPath}"

Opening a connection

Always wrap your connection in a Using block. This ensures the connection is properly closed and disposed, even if an exception occurs:

Using connection As New SqliteConnection("Data Source=myapp.db")
    connection.Open()

    ' Your database operations here...

End Using ' connection is automatically closed

This is the basic pattern you’ll use for every database operation. The Using block is essential to avoid file locks and „database is locked“ errors (more on that later).

Creating tables

Before you can store data, you need at least one table. Use CREATE TABLE IF NOT EXISTS to safely run the command every time your app starts, without errors if the table already exists:

Using connection As New SqliteConnection("Data Source=myapp.db")
    connection.Open()

    Using command As New SqliteCommand()
        command.Connection = connection
        command.CommandText = "
            CREATE TABLE IF NOT EXISTS Contacts (
                Id INTEGER PRIMARY KEY AUTOINCREMENT,
                Name TEXT NOT NULL,
                Email TEXT,
                Phone TEXT,
                CreatedAt TEXT DEFAULT CURRENT_TIMESTAMP
            )"
        command.ExecuteNonQuery()
    End Using
End Using

SQLite data types

SQLite uses a dynamic type system that’s different from SQL Server. The most common types are:

  • INTEGER – whole numbers (equivalent to Integer / Long in VB.NET)
  • TEXT – strings (equivalent to String)
  • REAL – floating point numbers (equivalent to Double)
  • BLOB – binary data (equivalent to Byte())
  • NULL – no value

A tip: SQLite doesn’t have a native DATETIME type. Store dates as TEXT in ISO 8601 format (e.g. 2026-04-07T14:30:00) or as INTEGER (Unix timestamp).

Inserting data

Now let’s insert a record into our Contacts table. Always use parameterized queries instead of string concatenation. This protects your app from SQL injection and handles special characters correctly:

Using connection As New SqliteConnection("Data Source=myapp.db")
    connection.Open()

    Using command As New SqliteCommand()
        command.Connection = connection
        command.CommandText = "INSERT INTO Contacts (Name, Email, Phone) VALUES (@name, @email, @phone)"
        command.Parameters.AddWithValue("@name", "John Doe")
        command.Parameters.AddWithValue("@email", "john@example.com")
        command.Parameters.AddWithValue("@phone", "+49 123 456789")
        command.ExecuteNonQuery()
    End Using
End Using

Why parameterized queries matter

Never build SQL strings by concatenating user input directly. This is the #1 security mistake that leads to SQL injection:

' ❌ NEVER do this - SQL injection vulnerability!
command.CommandText = "INSERT INTO Contacts (Name) VALUES ('" & txtName.Text & "')"

' ✅ Always use parameters instead
command.CommandText = "INSERT INTO Contacts (Name) VALUES (@name)"
command.Parameters.AddWithValue("@name", txtName.Text)

Getting the last inserted ID

After inserting a row, you often want to know the auto-generated ID. Use last_insert_rowid() for that:

command.CommandText = "INSERT INTO Contacts (Name, Email) VALUES (@name, @email)"
command.Parameters.AddWithValue("@name", "Jane Doe")
command.Parameters.AddWithValue("@email", "jane@example.com")
command.ExecuteNonQuery()

command.CommandText = "SELECT last_insert_rowid()"
Dim newId As Long = CLng(command.ExecuteScalar())
Console.WriteLine($"New contact ID: {newId}")

Reading data (SELECT)

Use ExecuteReader() to query data and iterate over the results. Again, wrap everything in Using blocks:

Using connection As New SqliteConnection("Data Source=myapp.db")
    connection.Open()

    Using command As New SqliteCommand("SELECT Id, Name, Email FROM Contacts", connection)
        Using reader As SqliteDataReader = command.ExecuteReader()
            While reader.Read()
                Dim id As Long = reader.GetInt64(0)
                Dim name As String = reader.GetString(1)
                Dim email As String = If(reader.IsDBNull(2), "", reader.GetString(2))

                Console.WriteLine($"[{id}] {name} - {email}")
            End While
        End Using
    End Using
End Using

Filtering with WHERE

To search for specific records, use a parameterized WHERE clause:

Using command As New SqliteCommand(
    "SELECT Id, Name, Email FROM Contacts WHERE Name LIKE @search",
    connection)
    command.Parameters.AddWithValue("@search", $"%{searchTerm}%")

    Using reader As SqliteDataReader = command.ExecuteReader()
        While reader.Read()
            ' Process results...
        End While
    End Using
End Using

If you’re building a search-as-you-type feature, consider debouncing the input so you don’t fire a query on every keystroke.

Planning a project?

Need a .NET desktop application with database?

From data models to finished WinForms or WPF app, I deliver your project. Let's talk about your requirements.

Updating and deleting data

UPDATE

Using connection As New SqliteConnection("Data Source=myapp.db")
    connection.Open()

    Using command As New SqliteCommand()
        command.Connection = connection
        command.CommandText = "UPDATE Contacts SET Email = @email WHERE Id = @id"
        command.Parameters.AddWithValue("@email", "new-email@example.com")
        command.Parameters.AddWithValue("@id", 1)

        Dim rowsAffected As Integer = command.ExecuteNonQuery()
        Console.WriteLine($"{rowsAffected} row(s) updated.")
    End Using
End Using

DELETE

Using connection As New SqliteConnection("Data Source=myapp.db")
    connection.Open()

    Using command As New SqliteCommand("DELETE FROM Contacts WHERE Id = @id", connection)
        command.Parameters.AddWithValue("@id", 5)

        Dim rowsAffected As Integer = command.ExecuteNonQuery()
        Console.WriteLine($"{rowsAffected} row(s) deleted.")
    End Using
End Using

Complete example: Contact manager

Let’s tie it all together. Here’s a complete helper class that wraps all CRUD operations for a Contacts table. You can use this as a starting point for your own projects:

Imports Microsoft.Data.Sqlite

Public Class ContactRepository

    Private ReadOnly _connectionString As String

    Public Sub New(dbPath As String)
        _connectionString = $"Data Source={dbPath}"
    End Sub

    ''' Ensures the Contacts table exists.
    Public Sub InitializeDatabase()
        Using connection As New SqliteConnection(_connectionString)
            connection.Open()

            Using command As New SqliteCommand()
                command.Connection = connection
                command.CommandText = "
                    CREATE TABLE IF NOT EXISTS Contacts (
                        Id INTEGER PRIMARY KEY AUTOINCREMENT,
                        Name TEXT NOT NULL,
                        Email TEXT,
                        Phone TEXT,
                        CreatedAt TEXT DEFAULT CURRENT_TIMESTAMP
                    )"
                command.ExecuteNonQuery()
            End Using
        End Using
    End Sub

    ''' Adds a new contact and returns the generated Id.
    Public Function AddContact(name As String, email As String, phone As String) As Long
        Using connection As New SqliteConnection(_connectionString)
            connection.Open()

            Using command As New SqliteCommand()
                command.Connection = connection
                command.CommandText = "
                    INSERT INTO Contacts (Name, Email, Phone)
                    VALUES (@name, @email, @phone)"
                command.Parameters.AddWithValue("@name", name)
                command.Parameters.AddWithValue("@email", CObj(email) ?? DBNull.Value)
                command.Parameters.AddWithValue("@phone", CObj(phone) ?? DBNull.Value)
                command.ExecuteNonQuery()
            End Using

            Using command As New SqliteCommand("SELECT last_insert_rowid()", connection)
                Return CLng(command.ExecuteScalar())
            End Using
        End Using
    End Function

    ''' Returns all contacts as a list of dictionaries.
    Public Function GetAllContacts() As List(Of Dictionary(Of String, Object))
        Dim contacts As New List(Of Dictionary(Of String, Object))

        Using connection As New SqliteConnection(_connectionString)
            connection.Open()

            Using command As New SqliteCommand("SELECT Id, Name, Email, Phone FROM Contacts ORDER BY Name", connection)
                Using reader As SqliteDataReader = command.ExecuteReader()
                    While reader.Read()
                        Dim contact As New Dictionary(Of String, Object) From {
                            {"Id", reader.GetInt64(0)},
                            {"Name", reader.GetString(1)},
                            {"Email", If(reader.IsDBNull(2), "", reader.GetString(2))},
                            {"Phone", If(reader.IsDBNull(3), "", reader.GetString(3))}
                        }
                        contacts.Add(contact)
                    End While
                End Using
            End Using
        End Using

        Return contacts
    End Function

    ''' Updates a contact by Id.
    Public Function UpdateContact(id As Long, name As String, email As String, phone As String) As Boolean
        Using connection As New SqliteConnection(_connectionString)
            connection.Open()

            Using command As New SqliteCommand()
                command.Connection = connection
                command.CommandText = "UPDATE Contacts SET Name = @name, Email = @email, Phone = @phone WHERE Id = @id"
                command.Parameters.AddWithValue("@name", name)
                command.Parameters.AddWithValue("@email", CObj(email) ?? DBNull.Value)
                command.Parameters.AddWithValue("@phone", CObj(phone) ?? DBNull.Value)
                command.Parameters.AddWithValue("@id", id)

                Return command.ExecuteNonQuery() > 0
            End Using
        End Using
    End Function

    ''' Deletes a contact by Id.
    Public Function DeleteContact(id As Long) As Boolean
        Using connection As New SqliteConnection(_connectionString)
            connection.Open()

            Using command As New SqliteCommand("DELETE FROM Contacts WHERE Id = @id", connection)
                command.Parameters.AddWithValue("@id", id)

                Return command.ExecuteNonQuery() > 0
            End Using
        End Using
    End Function

End Class

Using the ContactRepository

Here’s how you’d use the class above, for example in a WinForms Form_Load event:

Dim dbPath As String = Path.Combine(AppContext.BaseDirectory, "contacts.db")
Dim repo As New ContactRepository(dbPath)

' Create the table (safe to call every time)
repo.InitializeDatabase()

' Add a contact
Dim newId As Long = repo.AddContact("Robert Skibbe", "mail@robbelroot.de", "+49 123 456")
Console.WriteLine($"Added contact with ID {newId}")

' Read all contacts
For Each contact In repo.GetAllContacts()
    Console.WriteLine($"[{contact("Id")}] {contact("Name")} - {contact("Email")}")
Next

' Update a contact
repo.UpdateContact(newId, "Robert S.", "new@robbelroot.de", "+49 999 999")

' Delete a contact
repo.DeleteContact(newId)

If you want to display the results in a grid, have a look at the VB.NET DataGridView with filter functionality. For storing results by key, the VB.NET Dictionary is a great data structure.

Using transactions

When you need to insert or update multiple rows at once, wrap them in a transaction. This is both faster and safer: either all operations succeed or none of them do.

Using connection As New SqliteConnection("Data Source=myapp.db")
    connection.Open()

    Using transaction = connection.BeginTransaction()
        Using command As New SqliteCommand()
            command.Connection = connection
            command.Transaction = transaction
            command.CommandText = "INSERT INTO Contacts (Name, Email) VALUES (@name, @email)"

            For i As Integer = 1 To 1000
                command.Parameters.Clear()
                command.Parameters.AddWithValue("@name", $"Contact {i}")
                command.Parameters.AddWithValue("@email", $"contact{i}@example.com")
                command.ExecuteNonQuery()
            Next
        End Using

        transaction.Commit()
    End Using
End Using

Without a transaction, inserting 1,000 rows can take several seconds because SQLite commits after each individual INSERT. With a transaction, the same operation finishes in milliseconds.

Async/Await: keeping the UI responsive

If a database query runs on the UI thread, the entire interface freezes until it completes. Using Async/Await moves the work to the background while the UI stays responsive. For a deeper dive, check out the article on asynchronous programming in VB.NET.

Public Async Function GetAllContactsAsync() As Task(Of List(Of Dictionary(Of String, Object)))
    Dim contacts As New List(Of Dictionary(Of String, Object))

    Using connection As New SqliteConnection(_connectionString)
        Await connection.OpenAsync()

        Using command As New SqliteCommand("SELECT Id, Name, Email FROM Contacts ORDER BY Name", connection)
            Using reader As SqliteDataReader = Await command.ExecuteReaderAsync()
                While Await reader.ReadAsync()
                    Dim contact As New Dictionary(Of String, Object) From {
                        {"Id", reader.GetInt64(0)},
                        {"Name", reader.GetString(1)},
                        {"Email", If(reader.IsDBNull(2), "", reader.GetString(2))}
                    }
                    contacts.Add(contact)
                End While
            End Using
        End Using
    End Using

    Return contacts
End Function

The pattern applies to all operations: OpenAsync() instead of Open(), ExecuteNonQueryAsync() instead of ExecuteNonQuery(), ReadAsync() instead of Read(). The calling method must be declared as Async Function.

Common pitfalls and solutions

„Database is locked“

This is the most common SQLite error. It occurs when multiple threads or connections try to write to the database simultaneously. Here’s how to avoid it:

  • Always dispose connections – use Using blocks consistently. A forgotten open connection locks the file.
  • Enable WAL mode – Write-Ahead Logging allows readers and one writer to work concurrently:
Using connection As New SqliteConnection("Data Source=myapp.db")
    connection.Open()

    Using walCommand As New SqliteCommand("PRAGMA journal_mode=WAL;", connection)
        walCommand.ExecuteNonQuery()
    End Using
End Using
  • Set a busy timeout – instead of failing immediately, let SQLite retry for a few seconds:
Using walCommand As New SqliteCommand("PRAGMA busy_timeout=5000;", connection)
    walCommand.ExecuteNonQuery()
End Using
  • Use transactions for bulk operations instead of opening/closing connections in a tight loop.
  • Avoid long-running reads while writing. If you need complex background work, look into asynchronous programming in VB.NET.

File path issues

A relative path like Data Source=myapp.db resolves against the current working directory, which may differ depending on how your application was started (e.g. double-click vs. command line). Use an absolute path built from AppContext.BaseDirectory to be safe, as shown in the Application Path article.

NULL handling

When reading nullable columns, always check with reader.IsDBNull() before calling GetString() or GetInt64(). Otherwise you’ll get an InvalidCastException at runtime. When inserting NULL values, pass DBNull.Value instead of Nothing.

Need expert support?

Looking for an experienced .NET developer?

I'll take on your project, from database design to finished application. Just drop me a message.

FAQ

How do I create an SQLite database in VB.NET?

Just open a connection with a file path that doesn’t exist yet: New SqliteConnection("Data Source=myapp.db"). SQLite creates the file automatically when you call Open().

What is the SQLite connection string for VB.NET?

The minimal connection string is Data Source=path\to\database.db. You can add options like Mode=ReadWrite or Cache=Shared depending on your needs.

Why do I get „database is locked“ in SQLite?

This happens when multiple connections try to write simultaneously, or a connection wasn’t properly closed. Use Using blocks to dispose connections, enable WAL mode with PRAGMA journal_mode=WAL, and set a busy timeout.

Should I use Microsoft.Data.Sqlite or System.Data.SQLite?

Microsoft.Data.Sqlite is lighter, modern, and maintained by Microsoft. System.Data.SQLite is older and supports .NET Framework 4.0+. For new projects on .NET 5+ or .NET Framework 4.6.1+, Microsoft.Data.Sqlite is the recommended choice.

Can I use SQLite with async/await in VB.NET?

Yes. Microsoft.Data.Sqlite supports async methods like ExecuteNonQueryAsync(), ExecuteReaderAsync(), and ExecuteScalarAsync(). Use them with Await to keep the UI responsive during database operations.

Wrapping up

SQLite is the fastest way to add a local database to your VB.NET application. Install Microsoft.Data.Sqlite via NuGet, use Using blocks for every connection, and always write parameterized queries. For multi-threaded scenarios, enable WAL mode and set a busy timeout to avoid „database is locked“ errors.

Downloads

VB.NET SQLite example project – WinForms app with DataGridView and CRUD buttons

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

Robert Skibbe
Datenschutz-Übersicht

Diese Website verwendet Cookies, damit wir dir die bestmögliche Benutzererfahrung bieten können. Cookie-Informationen werden in deinem Browser gespeichert und führen Funktionen aus, wie das Wiedererkennen von dir, wenn du auf unsere Website zurückkehrst, und hilft unserem Team zu verstehen, welche Abschnitte der Website für dich am interessantesten und nützlichsten sind.