Skip to content

Commit c55987a

Browse files
authored
Add note about SqliteConnection not being thread-safe (#48623)
1 parent e789832 commit c55987a

File tree

2 files changed

+72
-65
lines changed

2 files changed

+72
-65
lines changed

docs/standard/data/sqlite/database-errors.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ Consider carefully how your app will handle these errors.
1818

1919
## Locking, retries, and timeouts
2020

21+
> [!WARNING]
22+
> Although SQLite supports concurrent access to the same database from multiple threads, the .NET APIs objects are not thread-safe. This means that `SqliteConnection`, `SqliteCommand` and `SqliteDataReader` cannot be shared and used concurrently from multiple threads.
23+
> When using Microsoft.Data.Sqlite from a concurrent application, simply create and open a new instance of `SqliteConnection` whenever you need to access the database (pooling ensures that this is a fast operation).
24+
2125
SQLite is aggressive when it comes to locking tables and database files. If your app enables any concurrent database access, you'll likely encounter busy and locked errors. You can mitigate many errors by using [write-ahead logging](async.md).
2226

2327
Whenever Microsoft.Data.Sqlite encounters a busy or locked error, it will automatically retry until it succeeds or the command timeout is reached.

samples/snippets/standard/data/sqlite/HelloWorldSample/Program.cs

Lines changed: 68 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -8,79 +8,82 @@ class Program
88
{
99
static void Main()
1010
{
11-
using (var connection = new SqliteConnection("Data Source=hello.db"))
11+
CreateAndSeed();
12+
Query();
13+
14+
// Clean up
15+
SqliteConnection.ClearAllPools();
16+
File.Delete("hello.db");
17+
}
18+
19+
static void CreateAndSeed()
20+
{
21+
using var connection = new SqliteConnection("Data Source=hello.db");
22+
23+
connection.Open();
24+
25+
var command = connection.CreateCommand();
26+
command.CommandText = """
27+
CREATE TABLE user (
28+
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
29+
name TEXT NOT NULL
30+
);
31+
32+
INSERT INTO user
33+
VALUES (1, 'Brice'),
34+
(2, 'Alexander'),
35+
(3, 'Nate');
36+
""";
37+
command.ExecuteNonQuery();
38+
39+
Console.Write("Name: ");
40+
var name = Console.ReadLine();
41+
42+
#region snippet_Parameter
43+
command.CommandText = "INSERT INTO user (name) VALUES ($name)";
44+
command.Parameters.AddWithValue("$name", name);
45+
#endregion
46+
command.ExecuteNonQuery();
47+
48+
command.CommandText = "SELECT last_insert_rowid()";
49+
var newId = (long)command.ExecuteScalar()!;
50+
51+
Console.WriteLine($"Your new user ID is {newId}.");
52+
}
53+
54+
static void Query()
55+
{
56+
Console.Write("User ID: ");
57+
var line = Console.ReadLine();
58+
if (line is null)
1259
{
13-
connection.Open();
14-
15-
var command = connection.CreateCommand();
16-
command.CommandText =
17-
@"
18-
CREATE TABLE user (
19-
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
20-
name TEXT NOT NULL
21-
);
22-
23-
INSERT INTO user
24-
VALUES (1, 'Brice'),
25-
(2, 'Alexander'),
26-
(3, 'Nate');
27-
";
28-
command.ExecuteNonQuery();
29-
30-
Console.Write("Name: ");
31-
var name = Console.ReadLine();
32-
33-
#region snippet_Parameter
34-
command.CommandText =
35-
@"
36-
INSERT INTO user (name)
37-
VALUES ($name)
38-
";
39-
command.Parameters.AddWithValue("$name", name);
40-
#endregion
41-
command.ExecuteNonQuery();
42-
43-
command.CommandText =
44-
@"
45-
SELECT last_insert_rowid()
46-
";
47-
var newId = (long)command.ExecuteScalar();
48-
49-
Console.WriteLine($"Your new user ID is {newId}.");
60+
return;
5061
}
5162

52-
Console.Write("User ID: ");
53-
var id = int.Parse(Console.ReadLine());
63+
var id = int.Parse(line);
5464

5565
#region snippet_HelloWorld
56-
using (var connection = new SqliteConnection("Data Source=hello.db"))
66+
using var connection = new SqliteConnection("Data Source=hello.db");
67+
68+
connection.Open();
69+
70+
using var command = connection.CreateCommand();
71+
command.CommandText = """
72+
SELECT name
73+
FROM user
74+
WHERE id = $id
75+
""";
76+
command.Parameters.AddWithValue("$id", id);
77+
78+
using var reader = command.ExecuteReader();
79+
80+
while (reader.Read())
5781
{
58-
connection.Open();
59-
60-
var command = connection.CreateCommand();
61-
command.CommandText =
62-
@"
63-
SELECT name
64-
FROM user
65-
WHERE id = $id
66-
";
67-
command.Parameters.AddWithValue("$id", id);
68-
69-
using (var reader = command.ExecuteReader())
70-
{
71-
while (reader.Read())
72-
{
73-
var name = reader.GetString(0);
74-
75-
Console.WriteLine($"Hello, {name}!");
76-
}
77-
}
82+
var name = reader.GetString(0);
83+
84+
Console.WriteLine($"Hello, {name}!");
7885
}
7986
#endregion
80-
81-
// Clean up
82-
SqliteConnection.ClearAllPools();
83-
File.Delete("hello.db");
8487
}
8588
}
8689
}

0 commit comments

Comments
 (0)