VB.NET Dapper – Micro ORM für sauberen Datenzugriff (2026)
Dapper ist der schnellste Weg, um das mühsame SqliteCommand-Boilerplate in deiner VB.NET-Anwendung durch sauberen, typsicheren Datenzugriff zu ersetzen. In diesem Guide zeige ich dir, wie du Dapper per NuGet installierst, Abfrageergebnisse direkt auf Klassen mappst und alle CRUD-Operationen mit minimalem Code ausführst. Wenn du bisher AddWithValue und ExecuteReader von Hand geschrieben hast, ist das hier dein nächster Schritt.
Inhaltsverzeichnis
- 1 Was ist Dapper und warum lohnt es sich?
- 2 Dapper per NuGet installieren
- 3 Die Model-Klasse anlegen
- 4 Verbindung herstellen und Tabellen erstellen
- 5 Daten einfügen (INSERT)
- 6 Daten abfragen (SELECT)
- 7 Daten aktualisieren und löschen
- 8 Komplettes Beispiel: ContactRepository mit Dapper
- 9 Transaktionen mit Dapper
- 10 Async-Abfragen: GUI nicht einfrieren
- 11 Dapper vs. ADO.NET: Direktvergleich
- 12 Häufige Stolperfallen und Tipps
- 13 FAQ
- 14 Fazit
Was ist Dapper und warum lohnt es sich?
Dapper ist ein leichtgewichtiger „Micro ORM“ für .NET, der ursprünglich vom Stack Overflow-Team entwickelt wurde, um deren eigene Performance-Probleme zu lösen. Er setzt auf IDbConnection auf und erweitert es mit praktischen Methoden wie Query(Of T) und Execute(). Hier ist, warum das relevant ist:
- Kein Boilerplate – kein manuelles
SqliteCommand,AddWithValue,ExecuteReaderund spaltenweises Mapping mehr - Stark typisiert – Abfrageergebnisse werden direkt auf deine VB.NET-Klassen gemappt, anhand übereinstimmender Property-Namen
- Schnell – Dapper ist fast so schnell wie reines ADO.NET, deutlich schneller als Entity Framework bei einfachen Abfragen
- Keine Magie – du schreibst echtes SQL, also weißt du immer genau, was an die Datenbank geht
- Funktioniert mit jeder Datenbank – SQLite, SQL Server, PostgreSQL, MySQL, alles was eine
IDbConnectionhat
Wenn du den VB.NET SQLite Artikel gelesen hast, kennst du bereits die Grundlagen: Datenbank erstellen und Abfragen mit SqliteCommand ausführen. Dapper ersetzt diese manuelle Arbeit, während du die volle Kontrolle über dein SQL behältst.
Dapper per NuGet installieren
Öffne die Package Manager Console in Visual Studio (Tools → NuGet Package Manager → Package Manager Console) und führe aus:
Install-Package Dapper Install-Package Microsoft.Data.Sqlite
Du brauchst beide Pakete: Dapper liefert die Extension Methods, Microsoft.Data.Sqlite die Datenbank-Verbindung. Wenn du Microsoft.Data.Sqlite aus einem früheren Projekt bereits installiert hast, reicht es, nur Dapper hinzuzufügen.
Alternativ: Rechtsklick auf das Projekt im Solution Explorer → NuGet-Pakete verwalten → nach Dapper suchen → Installieren. Dann für Microsoft.Data.Sqlite wiederholen, falls noch nicht vorhanden.
Die Model-Klasse anlegen
Bevor du Abfragen schreibst, definiere eine Klasse, deren Properties den Spaltennamen in deiner Tabelle entsprechen. Dapper mappt Spalten auf Properties anhand des Namens (Groß-/Kleinschreibung egal):
Public Class Contact
Public Property Id As Long
Public Property Name As String
Public Property Email As String
Public Property Phone As String
Public Property CreatedAt As String
End Class
Das ist alles, was Dapper braucht. Keine Attribute, keine Basisklassen, keine Konfigurationsdateien. Wenn ein Spaltenname nicht zum Property-Namen passt, verwende einen SQL-Alias: SELECT first_name AS Name.
Verbindung herstellen und Tabellen erstellen
Dapper arbeitet auf Basis von IDbConnection. Die Verbindung erstellst du weiterhin selbst. Verwende AppContext.BaseDirectory für einen zuverlässigen Pfad, wie im Application Path Artikel beschrieben:
Imports Dapper
Imports Microsoft.Data.Sqlite
Dim dbPath As String = Path.Combine(AppContext.BaseDirectory, "myapp.db")
Dim connectionString As String = $"Data Source={dbPath}"
Using connection As New SqliteConnection(connectionString)
connection.Open()
connection.Execute("
CREATE TABLE IF NOT EXISTS Contacts (
Id INTEGER PRIMARY KEY AUTOINCREMENT,
Name TEXT NOT NULL,
Email TEXT,
Phone TEXT,
CreatedAt TEXT DEFAULT CURRENT_TIMESTAMP
)")
End Using
Beachte, wie connection.Execute() das gesamte SqliteCommand– / ExecuteNonQuery()-Pattern ersetzt. Dapper öffnet die Verbindung automatisch, wenn sie geschlossen ist, aber ich bevorzuge es, sie explizit zu öffnen.
Daten einfügen (INSERT)
Mit rohem ADO.NET würdest du einen Command erstellen, Parameter einzeln hinzufügen und ExecuteNonQuery() aufrufen. Mit Dapper übergibst du ein anonymes Objekt und die Properties werden automatisch auf die Parameter gemappt:
Using connection As New SqliteConnection(connectionString)
connection.Open()
connection.Execute(
"INSERT INTO Contacts (Name, Email, Phone) VALUES (@Name, @Email, @Phone)",
New With {
.Name = "Max Mustermann",
.Email = "max@example.com",
.Phone = "+49 123 456789"
})
End Using
Dapper erzeugt automatisch parametrisierte Abfragen aus dem anonymen Objekt. Es besteht kein Risiko für SQL Injection, da die Werte nie in den SQL-String konkateniert werden.
Mehrere Zeilen auf einmal einfügen
Übergib eine Liste statt eines einzelnen Objekts und Dapper führt das Statement einmal pro Element aus:
Dim contacts As New List(Of Contact) From {
New Contact With {.Name = "Alice", .Email = "alice@example.com"},
New Contact With {.Name = "Bob", .Email = "bob@example.com"},
New Contact With {.Name = "Clara", .Email = "clara@example.com"}
}
Using connection As New SqliteConnection(connectionString)
connection.Open()
connection.Execute(
"INSERT INTO Contacts (Name, Email) VALUES (@Name, @Email)",
contacts)
End Using
Bei großen Datenmengen (Hunderte oder Tausende Zeilen) solltest du das in einer Transaktion kapseln, um die Performance deutlich zu verbessern. Dazu weiter unten mehr.
Daten abfragen (SELECT)
Hier zeigt Dapper seine wahre Stärke. Statt einen DataReader zu durchlaufen und jede Spalte manuell zu mappen, rufst du Query(Of T) auf und bekommst eine typisierte Collection zurück:
Using connection As New SqliteConnection(connectionString)
connection.Open()
Dim contacts = connection.Query(Of Contact)(
"SELECT Id, Name, Email, Phone FROM Contacts ORDER BY Name")
For Each contact In contacts
Console.WriteLine($"[{contact.Id}] {contact.Name} - {contact.Email}")
Next
End Using
Das Ergebnis ist ein IEnumerable(Of Contact), das du iterieren, mit LINQ filtern oder direkt an ein DataGridView binden kannst.
Abfragen mit Parametern
Parameter übergibst du wie bei Execute() als anonymes Objekt:
Using connection As New SqliteConnection(connectionString)
connection.Open()
Dim results = connection.Query(Of Contact)(
"SELECT Id, Name, Email FROM Contacts WHERE Name LIKE @Search",
New With {.Search = $"%{searchTerm}%"})
For Each contact In results
Console.WriteLine($"[{contact.Id}] {contact.Name}")
Next
End Using
Einzelne Zeile abfragen
Verwende QueryFirstOrDefault(Of T), wenn du genau ein Ergebnis erwartest (oder keines):
Using connection As New SqliteConnection(connectionString)
connection.Open()
Dim contact = connection.QueryFirstOrDefault(Of Contact)(
"SELECT Id, Name, Email, Phone FROM Contacts WHERE Id = @Id",
New With {.Id = 1})
If contact IsNot Nothing Then
Console.WriteLine($"{contact.Name} ({contact.Email})")
End If
End Using
Weitere Varianten: QueryFirst (Exception wenn keine Zeilen), QuerySingle (Exception wenn nicht genau eine Zeile), QuerySingleOrDefault (Exception wenn mehr als eine Zeile).
Skalare Abfragen
Für Abfragen, die einen einzelnen Wert zurückgeben, verwende ExecuteScalar(Of T):
Using connection As New SqliteConnection(connectionString)
connection.Open()
Dim count = connection.ExecuteScalar(Of Long)(
"SELECT COUNT(*) FROM Contacts")
Console.WriteLine($"{count} Kontakte in der Datenbank")
End Using
Daten aktualisieren und löschen
Updates und Deletes folgen dem gleichen Muster wie Inserts. Execute() gibt die Anzahl betroffener Zeilen zurück:
UPDATE
Using connection As New SqliteConnection(connectionString)
connection.Open()
Dim rowsAffected = connection.Execute(
"UPDATE Contacts SET Email = @Email WHERE Id = @Id",
New With {.Email = "neue-email@example.com", .Id = 1})
Console.WriteLine($"{rowsAffected} Zeile(n) aktualisiert.")
End Using
DELETE
Using connection As New SqliteConnection(connectionString)
connection.Open()
Dim rowsAffected = connection.Execute(
"DELETE FROM Contacts WHERE Id = @Id",
New With {.Id = 5})
Console.WriteLine($"{rowsAffected} Zeile(n) gelöscht.")
End Using
Komplettes Beispiel: ContactRepository mit Dapper
Hier ist eine vollständige Repository-Klasse für alle CRUD-Operationen. Vergleiche sie mit der manuellen ADO.NET-Version im SQLite-Artikel, um zu sehen, wie viel Code Dapper einspart:
Imports Dapper
Imports Microsoft.Data.Sqlite
Public Class ContactRepository
Private ReadOnly _connectionString As String
Public Sub New(connectionString As String)
_connectionString = connectionString
End Sub
Public Sub InitializeDatabase()
Using connection As New SqliteConnection(_connectionString)
connection.Open()
connection.Execute("
CREATE TABLE IF NOT EXISTS Contacts (
Id INTEGER PRIMARY KEY AUTOINCREMENT,
Name TEXT NOT NULL,
Email TEXT,
Phone TEXT,
CreatedAt TEXT DEFAULT CURRENT_TIMESTAMP
)")
End Using
End Sub
Public Function AddContact(name As String, email As String, phone As String) As Long
Using connection As New SqliteConnection(_connectionString)
connection.Open()
connection.Execute(
"INSERT INTO Contacts (Name, Email, Phone) VALUES (@Name, @Email, @Phone)",
New With {.Name = name, .Email = email, .Phone = phone})
Return connection.ExecuteScalar(Of Long)("SELECT last_insert_rowid()")
End Using
End Function
Public Function GetAllContacts() As List(Of Contact)
Using connection As New SqliteConnection(_connectionString)
connection.Open()
Return connection.Query(Of Contact)(
"SELECT Id, Name, Email, Phone FROM Contacts ORDER BY Name").AsList()
End Using
End Function
Public Function GetContactById(id As Long) As Contact
Using connection As New SqliteConnection(_connectionString)
connection.Open()
Return connection.QueryFirstOrDefault(Of Contact)(
"SELECT Id, Name, Email, Phone FROM Contacts WHERE Id = @Id",
New With {.Id = id})
End Using
End Function
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()
Dim rows = connection.Execute(
"UPDATE Contacts SET Name = @Name, Email = @Email, Phone = @Phone WHERE Id = @Id",
New With {.Name = name, .Email = email, .Phone = phone, .Id = id})
Return rows > 0
End Using
End Function
Public Function DeleteContact(id As Long) As Boolean
Using connection As New SqliteConnection(_connectionString)
connection.Open()
Dim rows = connection.Execute(
"DELETE FROM Contacts WHERE Id = @Id",
New With {.Id = id})
Return rows > 0
End Using
End Function
End Class
Die Verwendung ist unkompliziert:
Dim repo As New ContactRepository($"Data Source={dbPath}")
repo.InitializeDatabase()
Dim newId = repo.AddContact("Max Mustermann", "max@example.com", "+49 123 456789")
Console.WriteLine($"Kontakt mit ID {newId} erstellt")
Dim allContacts = repo.GetAllContacts()
For Each c In allContacts
Console.WriteLine($"[{c.Id}] {c.Name} - {c.Email}")
Next
Transaktionen mit Dapper
Bei Bulk-Operationen solltest du eine Transaktion verwenden. Ohne Transaktion führt SQLite nach jedem einzelnen Statement einen Commit durch, was bei Hunderten von Inserts extrem langsam wird:
Using connection As New SqliteConnection(connectionString)
connection.Open()
Using transaction = connection.BeginTransaction()
Dim contacts As New List(Of Object)
For i As Integer = 1 To 1000
contacts.Add(New With {
.Name = $"Kontakt {i}",
.Email = $"kontakt{i}@example.com"
})
Next
connection.Execute(
"INSERT INTO Contacts (Name, Email) VALUES (@Name, @Email)",
contacts,
transaction)
transaction.Commit()
End Using
End Using
Der entscheidende Unterschied zu rohem ADO.NET: Du übergibst das transaction-Objekt als Parameter an Execute(). Kein manuelles command.Transaction setzen. Und weil Dapper die Liste intern durchläuft, bekommst du Transaktionssicherheit für alle 1000 Inserts mit minimalem Code.
Async-Abfragen: GUI nicht einfrieren
Jede Dapper-Methode hat ein asynchrones Pendant. Wenn du eine WinForms- oder WPF-Anwendung baust, verwende sie, damit die Oberfläche während der Datenbankaufrufe nicht einfriert. Die Grundlagen findest du im VB.NET Async/Await Guide.
Private Async Function LoadContactsAsync() As Task(Of List(Of Contact))
Using connection As New SqliteConnection(connectionString)
Await connection.OpenAsync()
Dim contacts = Await connection.QueryAsync(Of Contact)(
"SELECT Id, Name, Email, Phone FROM Contacts ORDER BY Name")
Return contacts.AsList()
End Using
End Function
Verfügbare Async-Methoden: QueryAsync, QueryFirstOrDefaultAsync, ExecuteAsync, ExecuteScalarAsync. Sie funktionieren genau wie ihre synchronen Gegenstücke, nur mit Await.
Dapper vs. ADO.NET: Direktvergleich
Hier die gleiche „alle Kontakte laden“-Operation auf beide Arten. Zuerst reines ADO.NET, wie im SQLite-Artikel gezeigt:
' Reines ADO.NET - 13 Zeilen
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 = command.ExecuteReader()
While reader.Read()
Dim contact As New Contact With {
.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
Und jetzt das gleiche mit Dapper:
' Dapper - 4 Zeilen
Using connection As New SqliteConnection(connectionString)
connection.Open()
Dim contacts = connection.Query(Of Contact)(
"SELECT Id, Name, Email, Phone FROM Contacts ORDER BY Name").AsList()
End Using
Gleiches Ergebnis, gleiches SQL, gleiche Performance. Dapper übernimmt die Reader-Schleife, die Null-Checks und das Property-Mapping automatisch. Je komplexer das Datenmodell, desto größer der Unterschied.
Häufige Stolperfallen und Tipps
Property-Namen müssen zu Spaltennamen passen
Dapper mappt nach Name, Groß-/Kleinschreibung wird ignoriert. Wenn deine Tabelle first_name hat, dein Property aber FirstName heißt, verwende einen SQL-Alias:
Dim contacts = connection.Query(Of Contact)(
"SELECT first_name AS FirstName, last_name AS LastName FROM Contacts")
NULL-Werte
Dapper behandelt NULL-Spalten automatisch. Wenn eine Spalte NULL ist und das Ziel-Property ein String, wird es zu Nothing. Wenn du stattdessen einen leeren String willst, initialisiere deine Properties mit Standardwerten:
Public Class Contact
Public Property Id As Long
Public Property Name As String = ""
Public Property Email As String = ""
Public Property Phone As String = ""
End Class
Using-Blöcke nicht vergessen
Dapper verwaltet keine Verbindungs-Lebenszeiten. Du brauchst weiterhin Using-Blöcke für jede Verbindung, genau wie bei rohem ADO.NET. Eine offene Verbindung sperrt die SQLite-Datei.
Dapper und Dependency Injection
In größeren Anwendungen solltest du den Connection String per Injection übergeben statt ihn hardzucoden. Die ContactRepository-Klasse oben folgt bereits diesem Muster. Wenn du einen DI-Container wie Autofac einsetzt, schau dir den Dependency Injection Guide an.
FAQ
Dapper ist ein leichtgewichtiger Micro ORM, der IDbConnection um Methoden wie Query(Of T) und Execute() erweitert. Er mappt SQL-Ergebnisse direkt auf VB.NET-Klassen, ohne den Overhead eines vollen ORM wie Entity Framework.
Ja. Dapper ist fast so schnell wie reines ADO.NET, weil es minimalen Overhead erzeugt. Entity Framework fügt Change Tracking, Proxy-Generierung und Query-Translation hinzu, was es bei einfachen Operationen langsamer macht.
Ja. Installiere Dapper und Microsoft.Data.Sqlite per NuGet. Dapper arbeitet mit jeder IDbConnection, also funktioniert SqliteConnection ohne weitere Konfiguration.
Dapper mappt NULL-Spalten auf Nothing bei Referenztypen. Wenn du leere Strings bevorzugst, initialisiere deine Properties mit Standardwerten wie Public Property Email As String = "".
Ja. Jede Dapper-Methode hat ein asynchrones Gegenstück: QueryAsync, ExecuteAsync, ExecuteScalarAsync usw. Verwende sie mit Await in WinForms- oder WPF-Anwendungen, um die Oberfläche reaktionsfähig zu halten.
Fazit
Dapper eliminiert das repetitive ADO.NET-Boilerplate, während du die volle Kontrolle über dein SQL behältst. Installiere das NuGet-Paket, definiere eine Model-Klasse und ersetze dein manuelles SqliteCommand– / DataReader-Code durch Query(Of T) und Execute(). Wenn du deine Datenbank noch nicht aufgesetzt hast, starte zuerst mit dem VB.NET SQLite Guide und komm dann hierher zurück, um deine Datenzugriffsschicht zu vereinfachen.