• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

loresoft / FluentCommand / 22970781217

11 Mar 2026 07:32PM UTC coverage: 56.585% (+0.2%) from 56.372%
22970781217

push

github

pwelter34
Add ParameterJson overloads

1277 of 2823 branches covered (45.24%)

Branch coverage included in aggregate %.

0 of 4 new or added lines in 1 file covered. (0.0%)

64 existing lines in 4 files now uncovered.

3892 of 6312 relevant lines covered (61.66%)

367.61 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

83.33
/src/FluentCommand/DataSession.cs
1
using System.Data;
2
using System.Data.Common;
3

4
using FluentCommand.Query.Generators;
5

6
namespace FluentCommand;
7

8
/// <summary>
9
/// A fluent class for a data session.
10
/// </summary>
11
/// <seealso cref="FluentCommand.DisposableBase" />
12
/// <seealso cref="FluentCommand.IDataSession" />
13
public class DataSession : DisposableBase, IDataSession
14
{
15
    private readonly bool _disposeConnection;
16

17
    private readonly IDataInterceptor[] _interceptors;
18
    private readonly IDataConnectionInterceptor[] _connectionInterceptors;
19
    private readonly IDataCommandInterceptor[] _commandInterceptors;
20

21
    private bool _openedConnection;
22
    private int _connectionRequestCount;
23

24
    /// <summary>
25
    /// Gets the underlying <see cref="DbConnection"/> for the session.
26
    /// </summary>
27
    public DbConnection Connection { get; }
1,060✔
28

29
    /// <summary>
30
    /// Gets the underlying <see cref="DbTransaction"/> for the session.
31
    /// </summary>
32
    public DbTransaction Transaction { get; private set; }
174✔
33

34
    /// <summary>
35
    /// Gets the underlying <see cref="IDataCache"/> for the session.
36
    /// </summary>
37
    public IDataCache Cache { get; }
138✔
38

39
    /// <summary>
40
    /// Gets the query generator provider.
41
    /// </summary>
42
    /// <value>
43
    /// The query generator provider.
44
    /// </value>
45
    public IQueryGenerator QueryGenerator { get; }
56✔
46

47
    /// <summary>
48
    /// Gets the data command query logger.
49
    /// </summary>
50
    /// <value>
51
    /// The data command query logger.
52
    /// </value>
53
    public IDataQueryLogger QueryLogger { get; }
156✔
54

55
    /// <summary>
56
    /// Gets the interceptors registered for this session.
57
    /// </summary>
58
    /// <value>
59
    /// The list of <see cref="IDataInterceptor"/> instances active for this session.
60
    /// </value>
61
    public IReadOnlyList<IDataInterceptor> Interceptors => _interceptors;
4✔
62

63

64
    /// <summary>
65
    /// Initializes a new instance of the <see cref="DataSession" /> class.
66
    /// </summary>
67
    /// <param name="connection">The DbConnection to use for the session.</param>
68
    /// <param name="disposeConnection">if set to <c>true</c> dispose connection with this session.</param>
69
    /// <param name="cache">The <see cref="IDataCache" /> used to cached results of queries.</param>
70
    /// <param name="queryGenerator">The query generator provider.</param>
71
    /// <param name="logger">The logger delegate for writing log messages.</param>
72
    /// <param name="interceptors">The interceptors to apply during this session's lifetime.</param>
73
    /// <exception cref="ArgumentNullException"><paramref name="connection" /> is null</exception>
74
    /// <exception cref="ArgumentException">Invalid connection string on <paramref name="connection" /> instance.</exception>
75
    public DataSession(
10✔
76
        DbConnection connection,
10✔
77
        bool disposeConnection = true,
10✔
78
        IDataCache cache = null,
10✔
79
        IQueryGenerator queryGenerator = null,
10✔
80
        IDataQueryLogger logger = null,
10✔
81
        IEnumerable<IDataInterceptor> interceptors = null)
10✔
82
    {
83
        if (connection == null)
10!
UNCOV
84
            throw new ArgumentNullException(nameof(connection));
×
85

86
        if (string.IsNullOrEmpty(connection.ConnectionString))
10!
UNCOV
87
            throw new ArgumentException("Invalid connection string", nameof(connection));
×
88

89
        Connection = connection;
10✔
90
        Cache = cache;
10✔
91
        QueryGenerator = queryGenerator ?? new SqlServerGenerator();
10✔
92
        QueryLogger = logger;
10✔
93

94
        _interceptors = interceptors is null ? [] : [.. interceptors];
10✔
95
        _connectionInterceptors = [.. _interceptors.OfType<IDataConnectionInterceptor>()];
10✔
96
        _commandInterceptors = [.. _interceptors.OfType<IDataCommandInterceptor>()];
10✔
97

98
        _disposeConnection = disposeConnection;
10✔
99
    }
10✔
100

101
    /// <summary>
102
    /// Initializes a new instance of the <see cref="DataSession"/> class.
103
    /// </summary>
104
    /// <param name="transaction">The DbTransaction to use for the session.</param>
105
    /// <param name="disposeConnection">if set to <c>true</c> dispose connection with this session.</param>
106
    /// <param name="cache">The <see cref="IDataCache" /> used to cached results of queries.</param>
107
    /// <param name="queryGenerator">The query generator provider.</param>
108
    /// <param name="logger">The logger delegate for writing log messages.</param>
109
    /// <param name="interceptors">The interceptors to apply during this session's lifetime.</param>
110
    /// <exception cref="ArgumentNullException"><paramref name="transaction" /> is null</exception>
111
    /// <exception cref="ArgumentException">Invalid connection string on <paramref name="transaction" /> instance.</exception>
112
    public DataSession(
113
        DbTransaction transaction,
114
        bool disposeConnection = false,
115
        IDataCache cache = null,
116
        IQueryGenerator queryGenerator = null,
117
        IDataQueryLogger logger = null,
118
        IEnumerable<IDataInterceptor> interceptors = null)
UNCOV
119
        : this(transaction?.Connection, disposeConnection, cache, queryGenerator, logger, interceptors)
×
120
    {
UNCOV
121
        Transaction = transaction ?? throw new ArgumentNullException(nameof(transaction));
×
UNCOV
122
    }
×
123

124
    /// <summary>
125
    /// Initializes a new instance of the <see cref="DataSession" /> class.
126
    /// </summary>
127
    /// <param name="dataConfiguration">The configuration for the session</param>
128
    /// <exception cref="ArgumentNullException"><paramref name="dataConfiguration"/> is null</exception>
129
    public DataSession(IDataConfiguration dataConfiguration)
144✔
130
    {
131
        if (dataConfiguration == null)
144!
UNCOV
132
            throw new ArgumentNullException(nameof(dataConfiguration));
×
133

134
        Connection = dataConfiguration.CreateConnection();
144✔
135
        Cache = dataConfiguration.DataCache;
144✔
136
        QueryGenerator = dataConfiguration.QueryGenerator;
144✔
137
        QueryLogger = dataConfiguration.QueryLogger;
144✔
138

139
        _interceptors = dataConfiguration.Interceptors is null ? [] : [.. dataConfiguration.Interceptors];
144!
140
        _connectionInterceptors = [.. _interceptors.OfType<IDataConnectionInterceptor>()];
144✔
141
        _commandInterceptors = [.. _interceptors.OfType<IDataCommandInterceptor>()];
144✔
142

143
        _disposeConnection = true;
144✔
144
    }
144✔
145

146

147
    /// <summary>
148
    /// Starts a database transaction with the specified isolation level.
149
    /// </summary>
150
    /// <param name="isolationLevel">Specifies the isolation level for the transaction.</param>
151
    /// <returns>
152
    /// A <see cref="DbTransaction" /> representing the new transaction.
153
    /// </returns>
154
    public DbTransaction BeginTransaction(IsolationLevel isolationLevel = IsolationLevel.Unspecified)
155
    {
156
        EnsureConnection();
1✔
157
        Transaction = Connection.BeginTransaction(isolationLevel);
1✔
158

159
        return Transaction;
1✔
160
    }
161

162
#if NETCOREAPP3_0_OR_GREATER
163
    /// <summary>
164
    /// Starts a database transaction with the specified isolation level.
165
    /// </summary>
166
    /// <param name="isolationLevel">Specifies the isolation level for the transaction.</param>
167
    /// <param name="cancellationToken">The cancellation instruction.</param>
168
    /// <returns>
169
    /// A <see cref="DbTransaction" /> representing the new transaction.
170
    /// </returns>
171
    public async Task<DbTransaction> BeginTransactionAsync(IsolationLevel isolationLevel = IsolationLevel.Unspecified, CancellationToken cancellationToken = default)
172
    {
173
        await EnsureConnectionAsync(cancellationToken);
2✔
174
        Transaction = await Connection.BeginTransactionAsync(isolationLevel, cancellationToken);
2✔
175

176
        return Transaction;
2✔
177
    }
2✔
178
#endif
179

180
    /// <summary>
181
    /// Starts a data command with the specified SQL.
182
    /// </summary>
183
    /// <param name="sql">The SQL statement.</param>
184
    /// <returns>
185
    /// A fluent <see langword="interface" /> to a data command.
186
    /// </returns>
187
    public IDataCommand Sql(string sql)
188
    {
189
        var dataCommand = new DataCommand(this, Transaction, _commandInterceptors);
152✔
190
        return dataCommand.Sql(sql);
152✔
191
    }
192

193
    /// <summary>
194
    /// Starts a data command with the specified stored procedure name.
195
    /// </summary>
196
    /// <param name="storedProcedureName">Name of the stored procedure.</param>
197
    /// <returns>
198
    /// A fluent <see langword="interface" /> to a data command.
199
    /// </returns>
200
    public IDataCommand StoredProcedure(string storedProcedureName)
201
    {
202
        var dataCommand = new DataCommand(this, Transaction, _commandInterceptors);
5✔
203
        return dataCommand.StoredProcedure(storedProcedureName);
5✔
204
    }
205

206

207
    /// <summary>
208
    /// Ensures the connection is open.
209
    /// </summary>
210
    /// <exception cref="InvalidOperationException">Failed to open connection</exception>
211
    public void EnsureConnection()
212
    {
213
        AssertDisposed();
62✔
214

215
        bool justOpened = false;
62✔
216
        if (ConnectionState.Closed == Connection.State)
62✔
217
        {
218
            Connection.Open();
59✔
219
            _openedConnection = true;
59✔
220
            justOpened = true;
59✔
221
        }
222

223
        if (_openedConnection)
62✔
224
            _connectionRequestCount++;
62✔
225

226
        // Check the connection was opened correctly
227
        if (Connection.State is ConnectionState.Closed or ConnectionState.Broken)
62!
UNCOV
228
            throw new InvalidOperationException($"Execution of the command requires an open and available connection. The connection's current state is {Connection.State}.");
×
229

230
        // run connection opened interceptors only when the connection was just opened by this context
231
        if (justOpened)
62✔
232
        {
233
            foreach (var interceptor in _connectionInterceptors)
124✔
234
                interceptor.ConnectionOpened(Connection, this);
3✔
235
        }
236
    }
62✔
237

238
    /// <summary>
239
    /// Ensures the connection is open asynchronous.
240
    /// </summary>
241
    /// <param name="cancellationToken">The cancellation instruction.</param>
242
    /// <returns>A task representing the asynchronous operation.</returns>
243
    /// <exception cref="InvalidOperationException">Failed to open connection</exception>
244
    public async Task EnsureConnectionAsync(CancellationToken cancellationToken = default)
245
    {
246
        AssertDisposed();
109✔
247

248
        bool justOpened = false;
109✔
249
        if (ConnectionState.Closed == Connection.State)
109✔
250
        {
251
            await Connection.OpenAsync(cancellationToken).ConfigureAwait(false);
106✔
252
            _openedConnection = true;
106✔
253
            justOpened = true;
106✔
254
        }
255

256
        if (_openedConnection)
109✔
257
            _connectionRequestCount++;
109✔
258

259
        // Check the connection was opened correctly
260
        if (Connection.State is ConnectionState.Closed or ConnectionState.Broken)
109!
UNCOV
261
            throw new InvalidOperationException($"Execution of the command requires an open and available connection. The connection's current state is {Connection.State}.");
×
262

263
        // run connection opened interceptors only when the connection was just opened by this context
264
        if (justOpened)
109✔
265
        {
266
            foreach (var interceptor in _connectionInterceptors)
214✔
267
                await interceptor.ConnectionOpenedAsync(Connection, this, cancellationToken).ConfigureAwait(false);
1✔
268
        }
269
    }
109✔
270

271
    /// <summary>
272
    /// Releases the connection.
273
    /// </summary>
274
    public void ReleaseConnection()
275
    {
276
        AssertDisposed();
65✔
277

278
        if (!_openedConnection)
65✔
279
            return;
2✔
280

281
        if (_connectionRequestCount > 0)
63✔
282
            _connectionRequestCount--;
63✔
283

284
        if (_connectionRequestCount != 0)
63✔
285
            return;
3✔
286

287
        // When no operation is using the connection and the context had opened the connection
288
        // the connection can be closed
289
        Connection.Close();
60✔
290
        _openedConnection = false;
60✔
291
    }
60✔
292

293
#if NETCOREAPP3_0_OR_GREATER
294
    /// <summary>
295
    /// Releases the connection.
296
    /// </summary>
297
    public async Task ReleaseConnectionAsync()
298
    {
299
        AssertDisposed();
107✔
300

301
        if (!_openedConnection)
107✔
302
            return;
2✔
303

304
        if (_connectionRequestCount > 0)
105✔
305
            _connectionRequestCount--;
105✔
306

307
        if (_connectionRequestCount != 0)
105✔
308
            return;
3✔
309

310
        // When no operation is using the connection and the context had opened the connection
311
        // the connection can be closed
312
        await Connection.CloseAsync();
102✔
313
        _openedConnection = false;
102✔
314
    }
107✔
315

316
    /// <summary>
317
    /// Disposes the managed resources.
318
    /// </summary>
319
    protected override async ValueTask DisposeResourcesAsync()
320
    {
321
        if (Connection == null)
77!
UNCOV
322
            return;
×
323

324
        // Dispose the connection created
325
        if (_disposeConnection)
77✔
326
            await Connection.DisposeAsync();
77✔
327
    }
77✔
328
#endif
329

330
    /// <summary>
331
    /// Disposes the managed resources.
332
    /// </summary>
333
    protected override void DisposeManagedResources()
334
    {
335
        if (Connection == null)
22!
UNCOV
336
            return;
×
337

338
        // Dispose the connection created
339
        if (_disposeConnection)
22✔
340
            Connection.Dispose();
22✔
341
    }
22✔
342
}
343

344
/// <summary>
345
/// A fluent class for a data session by discriminator.  Used to register multiple instances of IDataSession.
346
/// </summary>
347
/// <typeparam name="TDiscriminator">The type of the discriminator.</typeparam>
348
/// <seealso cref="FluentCommand.DisposableBase" />
349
/// <seealso cref="FluentCommand.IDataSession" />
350
public class DataSession<TDiscriminator> : DataSession, IDataSession<TDiscriminator>
351
{
352
    /// <summary>
353
    /// Initializes a new instance of the <see cref="DataSession" /> class.
354
    /// </summary>
355
    /// <param name="connection">The DbConnection to use for the session.</param>
356
    /// <param name="disposeConnection">if set to <c>true</c> dispose connection with this session.</param>
357
    /// <param name="cache">The <see cref="IDataCache" /> used to cached results of queries.</param>
358
    /// <param name="queryGenerator">The query generator provider.</param>
359
    /// <param name="logger">The logger delegate for writing log messages.</param>
360
    /// <param name="interceptors">The interceptors to apply during this session's lifetime.</param>
361
    /// <exception cref="ArgumentNullException"><paramref name="connection" /> is null</exception>
362
    /// <exception cref="ArgumentException">Invalid connection string on <paramref name="connection" /> instance.</exception>
363
    public DataSession(
364
        DbConnection connection,
365
        bool disposeConnection = true,
366
        IDataCache cache = null,
367
        IQueryGenerator queryGenerator = null,
368
        IDataQueryLogger logger = null,
369
        IEnumerable<IDataInterceptor> interceptors = null)
UNCOV
370
        : base(connection, disposeConnection, cache, queryGenerator, logger, interceptors)
×
371
    {
UNCOV
372
    }
×
373

374
    /// <summary>
375
    /// Initializes a new instance of the <see cref="DataSession"/> class.
376
    /// </summary>
377
    /// <param name="transaction">The DbTransaction to use for the session.</param>
378
    /// <param name="disposeConnection">if set to <c>true</c> dispose connection with this session.</param>
379
    /// <param name="cache">The <see cref="IDataCache" /> used to cached results of queries.</param>
380
    /// <param name="queryGenerator">The query generator provider.</param>
381
    /// <param name="logger">The logger delegate for writing log messages.</param>
382
    /// <param name="interceptors">The interceptors to apply during this session's lifetime.</param>
383
    /// <exception cref="ArgumentNullException"><paramref name="transaction" /> is null</exception>
384
    /// <exception cref="ArgumentException">Invalid connection string on <paramref name="transaction" /> instance.</exception>
385
    public DataSession(
386
        DbTransaction transaction,
387
        bool disposeConnection = false,
388
        IDataCache cache = null,
389
        IQueryGenerator queryGenerator = null,
390
        IDataQueryLogger logger = null,
391
        IEnumerable<IDataInterceptor> interceptors = null)
UNCOV
392
        : base(transaction, disposeConnection, cache, queryGenerator, logger, interceptors)
×
393
    {
UNCOV
394
    }
×
395

396
    /// <summary>
397
    /// Initializes a new instance of the <see cref="DataSession" /> class.
398
    /// </summary>
399
    /// <param name="dataConfiguration">The configuration for the session</param>
400
    /// <exception cref="ArgumentNullException"><paramref name="dataConfiguration" /> is null</exception>
401
    public DataSession(IDataConfiguration<TDiscriminator> dataConfiguration)
402
        : base(dataConfiguration)
1✔
403
    {
404
    }
1✔
405
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc