Repository navigation
Expand file tree
/
Copy pathSQLiteSession.cs
More file actions
173 lines (155 loc) · 6.61 KB
/
Copy pathSQLiteSession.cs
File metadata and controls
173 lines (155 loc) · 6.61 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
using System;
using System.Collections.Generic;
using System.Data;
using Microsoft.Data.Sqlite;
namespace DBAClientX;
/// <summary>
/// Represents a SQLite connection session owned and managed by <see cref="SQLite"/>.
/// </summary>
/// <remarks>
/// The session keeps provider-specific connection and transaction objects inside DBAClientX while
/// allowing callers to run related commands against the same SQLite connection.
/// A session and its prepared commands must not execute concurrently or be disposed during execution.
/// </remarks>
public sealed class SQLiteSession : IDisposable
{
private readonly SQLite _client;
private readonly SqliteConnection _connection;
private readonly SqliteTransaction? _transaction;
private readonly bool _ownsConnection;
private bool _disposed;
internal SQLiteSession(SQLite client, SqliteConnection connection)
: this(client, connection, transaction: null, ownsConnection: true)
{
}
internal SQLiteSession(SQLite client, SqliteConnection connection, SqliteTransaction transaction)
: this(client, connection, transaction, ownsConnection: false)
{
}
private SQLiteSession(
SQLite client,
SqliteConnection connection,
SqliteTransaction? transaction,
bool ownsConnection)
{
_client = client ?? throw new ArgumentNullException(nameof(client));
_connection = connection ?? throw new ArgumentNullException(nameof(connection));
_transaction = transaction;
_ownsConnection = ownsConnection;
}
/// <summary>
/// Executes a SQL statement that does not return rows using this session's connection.
/// </summary>
/// <param name="query">SQL command text.</param>
/// <param name="parameters">Optional parameter values.</param>
/// <returns>The number of rows affected by the command.</returns>
public int ExecuteNonQuery(string query, IDictionary<string, object?>? parameters = null)
{
ThrowIfDisposed();
return _client.ExecuteSessionNonQuery(_connection, _transaction, query, parameters);
}
/// <summary>
/// Executes a SQL statement and returns the first column of the first row.
/// </summary>
/// <param name="query">SQL command text.</param>
/// <param name="parameters">Optional parameter values.</param>
/// <returns>The scalar value returned by the provider.</returns>
public object? ExecuteScalar(string query, IDictionary<string, object?>? parameters = null)
{
ThrowIfDisposed();
return _client.ExecuteSessionScalar(_connection, _transaction, query, parameters);
}
/// <summary>
/// Executes a query and maps each row through a provider-neutral <see cref="IDataRecord"/>.
/// </summary>
/// <typeparam name="T">The row projection type.</typeparam>
/// <param name="query">SQL query text.</param>
/// <param name="map">Mapping callback invoked for each row.</param>
/// <param name="parameters">Optional parameter values.</param>
/// <param name="initialize">Optional callback invoked once after the reader opens.</param>
/// <returns>The mapped rows.</returns>
public IReadOnlyList<T> QueryAsList<T>(
string query,
Func<IDataRecord, T> map,
IDictionary<string, object?>? parameters = null,
Action<IDataRecord>? initialize = null)
{
ThrowIfDisposed();
return _client.ExecuteSessionQueryAsList(_connection, _transaction, query, map, parameters, initialize);
}
/// <summary>
/// Prepares a statement once on this session's connection and transaction for repeated execution.
/// </summary>
/// <param name="query">SQL text using named parameters such as <c>$name</c> or <c>@name</c>.</param>
/// <param name="parameterNames">Parameter names in the order values will be passed to each execution.</param>
/// <returns>A prepared command; dispose it before this session ends.</returns>
public SQLitePreparedCommand Prepare(string query, params string[] parameterNames)
{
ThrowIfDisposed();
return _client.CreateSessionPreparedCommand(_connection, _transaction, query, parameterNames ?? Array.Empty<string>());
}
/// <summary>
/// Prepares an <c>INSERT</c> into <paramref name="table"/> for the given columns; pass one value per column to each execution.
/// </summary>
/// <param name="table">Destination table (optionally schema-qualified).</param>
/// <param name="columns">Destination columns in value order.</param>
/// <returns>A prepared command; dispose it before this session ends.</returns>
/// <remarks>Use it inside <see cref="RunInTransaction(System.Action{SQLiteSession})"/> to write many rows quickly and atomically.</remarks>
public SQLitePreparedCommand PrepareInsert(string table, params string[] columns)
{
ThrowIfDisposed();
(string query, string[] names) = SQLite.BuildPreparedInsert(table, columns);
return _client.CreateSessionPreparedCommand(_connection, _transaction, query, names);
}
/// <summary>
/// Runs a group of operations inside one SQLite transaction using this session's connection.
/// </summary>
/// <typeparam name="TResult">The operation result type.</typeparam>
/// <param name="operation">Callback that receives a transaction-bound session.</param>
/// <returns>The callback result after the transaction commits.</returns>
public TResult RunInTransaction<TResult>(Func<SQLiteSession, TResult> operation)
{
ThrowIfDisposed();
if (_transaction != null)
{
throw new DbaTransactionException("A session transaction is already active.");
}
return _client.ExecuteSessionTransaction(_connection, operation);
}
/// <summary>
/// Runs a group of operations inside one SQLite transaction using this session's connection.
/// </summary>
/// <param name="operation">Callback that receives a transaction-bound session.</param>
public void RunInTransaction(Action<SQLiteSession> operation)
{
if (operation == null)
{
throw new ArgumentNullException(nameof(operation));
}
RunInTransaction(session =>
{
operation(session);
return true;
});
}
/// <inheritdoc />
public void Dispose()
{
if (_disposed)
{
return;
}
if (_ownsConnection)
{
_connection.Dispose();
}
_disposed = true;
}
private void ThrowIfDisposed()
{
if (_disposed)
{
throw new ObjectDisposedException(nameof(SQLiteSession));
}
}
}