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

loresoft / FluentCommand / 8850661434

26 Apr 2024 03:30PM UTC coverage: 54.48% (+2.6%) from 51.881%
8850661434

push

github

pwelter34
update debug settings

1152 of 2717 branches covered (42.4%)

Branch coverage included in aggregate %.

3682 of 6156 relevant lines covered (59.81%)

701.76 hits per line

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

80.0
/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 bool _openedConnection;
18
    private int _connectionRequestCount;
19

20
    /// <summary>
21
    /// Gets the underlying <see cref="DbConnection"/> for the session.
22
    /// </summary>
23
    public DbConnection Connection { get; }
2,838✔
24

25
    /// <summary>
26
    /// Gets the underlying <see cref="DbTransaction"/> for the session.
27
    /// </summary>
28
    public DbTransaction Transaction { get; private set; }
492✔
29

30
    /// <summary>
31
    /// Gets the underlying <see cref="IDataCache"/> for the session.
32
    /// </summary>
33
    public IDataCache Cache { get; }
393✔
34

35
    /// <summary>
36
    /// Gets the query generator provider.
37
    /// </summary>
38
    /// <value>
39
    /// The query generator provider.
40
    /// </value>
41
    public IQueryGenerator QueryGenerator { get; }
156✔
42

43
    /// <summary>
44
    /// Gets the data command query logger.
45
    /// </summary>
46
    /// <value>
47
    /// The data command query logger.
48
    /// </value>
49
    public IDataQueryLogger QueryLogger { get; }
441✔
50

51

52
    /// <summary>
53
    /// Initializes a new instance of the <see cref="DataSession" /> class.
54
    /// </summary>
55
    /// <param name="connection">The DbConnection to use for the session.</param>
56
    /// <param name="disposeConnection">if set to <c>true</c> dispose connection with this session.</param>
57
    /// <param name="cache">The <see cref="IDataCache" /> used to cached results of queries.</param>
58
    /// <param name="queryGenerator">The query generator provider.</param>
59
    /// <param name="logger">The logger delegate for writing log messages.</param>
60
    /// <exception cref="ArgumentNullException"><paramref name="connection" /> is null</exception>
61
    /// <exception cref="ArgumentException">Invalid connection string on <paramref name="connection" /> instance.</exception>
62
    public DataSession(DbConnection connection, bool disposeConnection = true, IDataCache cache = null, IQueryGenerator queryGenerator = null, IDataQueryLogger logger = null)
3✔
63
    {
64
        if (connection == null)
3!
65
            throw new ArgumentNullException(nameof(connection));
×
66

67
        if (string.IsNullOrEmpty(connection.ConnectionString))
3!
68
            throw new ArgumentException("Invalid connection string", nameof(connection));
×
69

70
        Connection = connection;
3✔
71
        Cache = cache;
3✔
72
        QueryGenerator = queryGenerator ?? new SqlServerGenerator();
3✔
73
        QueryLogger = logger;
3✔
74

75
        _disposeConnection = disposeConnection;
3✔
76
    }
3✔
77

78
    /// <summary>
79
    /// Initializes a new instance of the <see cref="DataSession"/> class.
80
    /// </summary>
81
    /// <param name="transaction">The DbTransaction to use for the session.</param>
82
    /// <param name="disposeConnection">if set to <c>true</c> dispose connection with this session.</param>
83
    /// <param name="cache">The <see cref="IDataCache" /> used to cached results of queries.</param>
84
    /// <param name="queryGenerator">The query generator provider.</param>
85
    /// <param name="logger">The logger delegate for writing log messages.</param>
86
    /// <exception cref="ArgumentNullException"><paramref name="transaction" /> is null</exception>
87
    /// <exception cref="ArgumentException">Invalid connection string on <paramref name="transaction" /> instance.</exception>
88
    public DataSession(DbTransaction transaction, bool disposeConnection = false, IDataCache cache = null, IQueryGenerator queryGenerator = null, IDataQueryLogger logger = null)
89
        : this(transaction?.Connection, disposeConnection, cache, queryGenerator, logger)
×
90
    {
91
        Transaction = transaction ?? throw new ArgumentNullException(nameof(transaction));
×
92
    }
×
93

94
    /// <summary>
95
    /// Initializes a new instance of the <see cref="DataSession" /> class.
96
    /// </summary>
97
    /// <param name="dataConfiguration">The configuration for the session</param>
98
    /// <exception cref="ArgumentNullException"><paramref name="dataConfiguration"/> is null</exception>
99
    public DataSession(IDataConfiguration dataConfiguration)
420✔
100
    {
101
        if (dataConfiguration == null)
420!
102
            throw new ArgumentNullException(nameof(dataConfiguration));
×
103

104

105
        Connection = dataConfiguration.CreateConnection();
420✔
106
        Cache = dataConfiguration.DataCache;
420✔
107
        QueryGenerator = dataConfiguration.QueryGenerator;
420✔
108
        QueryLogger = dataConfiguration.QueryLogger;
420✔
109
        _disposeConnection = true;
420✔
110
    }
420✔
111

112

113
    /// <summary>
114
    /// Starts a database transaction with the specified isolation level.
115
    /// </summary>
116
    /// <param name="isolationLevel">Specifies the isolation level for the transaction.</param>
117
    /// <returns>
118
    /// A <see cref="DbTransaction" /> representing the new transaction.
119
    /// </returns>
120
    public DbTransaction BeginTransaction(IsolationLevel isolationLevel = IsolationLevel.Unspecified)
121
    {
122
        EnsureConnection();
3✔
123
        Transaction = Connection.BeginTransaction(isolationLevel);
3✔
124

125
        return Transaction;
3✔
126
    }
127

128
#if NETCOREAPP3_0_OR_GREATER
129
    /// <summary>
130
    /// Starts a database transaction with the specified isolation level.
131
    /// </summary>
132
    /// <param name="isolationLevel">Specifies the isolation level for the transaction.</param>
133
    /// <param name="cancellationToken">The cancellation instruction.</param>
134
    /// <returns>
135
    /// A <see cref="DbTransaction" /> representing the new transaction.
136
    /// </returns>
137
    public async Task<DbTransaction> BeginTransactionAsync(IsolationLevel isolationLevel = IsolationLevel.Unspecified, CancellationToken cancellationToken = default)
138
    {
139
        await EnsureConnectionAsync(cancellationToken);
6✔
140
        Transaction = await Connection.BeginTransactionAsync(isolationLevel, cancellationToken);
6✔
141

142
        return Transaction;
6✔
143
    }
6✔
144
#endif
145

146
    /// <summary>
147
    /// Starts a data command with the specified SQL.
148
    /// </summary>
149
    /// <param name="sql">The SQL statement.</param>
150
    /// <returns>
151
    /// A fluent <see langword="interface" /> to a data command.
152
    /// </returns>
153
    public IDataCommand Sql(string sql)
154
    {
155
        var dataCommand = new DataCommand(this, Transaction);
429✔
156
        return dataCommand.Sql(sql);
429✔
157
    }
158

159
    /// <summary>
160
    /// Starts a data command with the specified stored procedure name.
161
    /// </summary>
162
    /// <param name="storedProcedureName">Name of the stored procedure.</param>
163
    /// <returns>
164
    /// A fluent <see langword="interface" /> to a data command.
165
    /// </returns>
166
    public IDataCommand StoredProcedure(string storedProcedureName)
167
    {
168
        var dataCommand = new DataCommand(this, Transaction);
15✔
169
        return dataCommand.StoredProcedure(storedProcedureName);
15✔
170
    }
171

172

173
    /// <summary>
174
    /// Ensures the connection is open.
175
    /// </summary>
176
    /// <exception cref="InvalidOperationException">Failed to open connection</exception>
177
    public void EnsureConnection()
178
    {
179
        AssertDisposed();
171✔
180

181
        if (ConnectionState.Closed == Connection.State)
171✔
182
        {
183
            Connection.Open();
165✔
184
            _openedConnection = true;
165✔
185
        }
186

187
        if (_openedConnection)
171✔
188
            _connectionRequestCount++;
171✔
189

190
        // Check the connection was opened correctly
191
        if (Connection.State is ConnectionState.Closed or ConnectionState.Broken)
171!
192
            throw new InvalidOperationException($"Execution of the command requires an open and available connection. The connection's current state is {Connection.State}.");
×
193
    }
171✔
194

195
    /// <summary>
196
    /// Ensures the connection is open asynchronous.
197
    /// </summary>
198
    /// <param name="cancellationToken">The cancellation instruction.</param>
199
    /// <returns>A task representing the asynchronous operation.</returns>
200
    /// <exception cref="InvalidOperationException">Failed to open connection</exception>
201
    public async Task EnsureConnectionAsync(CancellationToken cancellationToken = default)
202
    {
203
        AssertDisposed();
300✔
204

205
        if (ConnectionState.Closed == Connection.State)
300✔
206
        {
207
            await Connection.OpenAsync(cancellationToken).ConfigureAwait(false);
291✔
208
            _openedConnection = true;
291✔
209
        }
210

211
        if (_openedConnection)
300✔
212
            _connectionRequestCount++;
300✔
213

214
        // Check the connection was opened correctly
215
        if (Connection.State is ConnectionState.Closed or ConnectionState.Broken)
300!
216
            throw new InvalidOperationException($"Execution of the command requires an open and available connection. The connection's current state is {Connection.State}.");
×
217
    }
300✔
218

219
    /// <summary>
220
    /// Releases the connection.
221
    /// </summary>
222
    public void ReleaseConnection()
223
    {
224
        AssertDisposed();
174✔
225

226
        if (!_openedConnection)
174✔
227
            return;
6✔
228

229
        if (_connectionRequestCount > 0)
168✔
230
            _connectionRequestCount--;
168✔
231

232
        if (_connectionRequestCount != 0)
168✔
233
            return;
6✔
234

235
        // When no operation is using the connection and the context had opened the connection
236
        // the connection can be closed
237
        Connection.Close();
162✔
238
        _openedConnection = false;
162✔
239
    }
162✔
240

241
#if NETCOREAPP3_0_OR_GREATER
242
    /// <summary>
243
    /// Releases the connection.
244
    /// </summary>
245
    public async Task ReleaseConnectionAsync()
246
    {
247
        AssertDisposed();
300✔
248

249
        if (!_openedConnection)
300✔
250
            return;
6✔
251

252
        if (_connectionRequestCount > 0)
294✔
253
            _connectionRequestCount--;
294✔
254

255
        if (_connectionRequestCount != 0)
294✔
256
            return;
9✔
257

258
        // When no operation is using the connection and the context had opened the connection
259
        // the connection can be closed
260
        await Connection.CloseAsync();
285✔
261
        _openedConnection = false;
285✔
262
    }
300✔
263

264
    /// <summary>
265
    /// Disposes the managed resources.
266
    /// </summary>
267
    protected override async ValueTask DisposeResourcesAsync()
268
    {
269
        if (Connection == null)
183!
270
            return;
×
271

272
        // Dispose the connection created
273
        if (_disposeConnection)
183✔
274
            await Connection.DisposeAsync();
183✔
275
    }
183✔
276
#endif
277

278
    /// <summary>
279
    /// Disposes the managed resources.
280
    /// </summary>
281
    protected override void DisposeManagedResources()
282
    {
283
        if (Connection == null)
45!
284
            return;
×
285

286
        // Dispose the connection created
287
        if (_disposeConnection)
45✔
288
            Connection.Dispose();
45✔
289
    }
45✔
290
}
291

292
/// <summary>
293
/// A fluent class for a data session by discriminator.  Used to register multiple instances of IDataSession.
294
/// </summary>
295
/// <typeparam name="TDiscriminator">The type of the discriminator.</typeparam>
296
/// <seealso cref="FluentCommand.DisposableBase" />
297
/// <seealso cref="FluentCommand.IDataSession" />
298
public class DataSession<TDiscriminator> : DataSession, IDataSession<TDiscriminator>
299
{
300
    /// <summary>
301
    /// Initializes a new instance of the <see cref="DataSession" /> class.
302
    /// </summary>
303
    /// <param name="connection">The DbConnection to use for the session.</param>
304
    /// <param name="disposeConnection">if set to <c>true</c> dispose connection with this session.</param>
305
    /// <param name="cache">The <see cref="IDataCache" /> used to cached results of queries.</param>
306
    /// <param name="queryGenerator">The query generator provider.</param>
307
    /// <param name="logger">The logger delegate for writing log messages.</param>
308
    /// <exception cref="ArgumentNullException"><paramref name="connection" /> is null</exception>
309
    /// <exception cref="ArgumentException">Invalid connection string on <paramref name="connection" /> instance.</exception>
310
    public DataSession(DbConnection connection, bool disposeConnection = true, IDataCache cache = null, IQueryGenerator queryGenerator = null, IDataQueryLogger logger = null)
311
        : base(connection, disposeConnection, cache, queryGenerator, logger)
×
312
    {
313
    }
×
314

315
    /// <summary>
316
    /// Initializes a new instance of the <see cref="DataSession"/> class.
317
    /// </summary>
318
    /// <param name="transaction">The DbTransaction to use for the session.</param>
319
    /// <param name="disposeConnection">if set to <c>true</c> dispose connection with this session.</param>
320
    /// <param name="cache">The <see cref="IDataCache" /> used to cached results of queries.</param>
321
    /// <param name="queryGenerator">The query generator provider.</param>
322
    /// <param name="logger">The logger delegate for writing log messages.</param>
323
    /// <exception cref="ArgumentNullException"><paramref name="transaction" /> is null</exception>
324
    /// <exception cref="ArgumentException">Invalid connection string on <paramref name="transaction" /> instance.</exception>
325
    public DataSession(DbTransaction transaction, bool disposeConnection = false, IDataCache cache = null, IQueryGenerator queryGenerator = null, IDataQueryLogger logger = null)
326
        : base(transaction, disposeConnection, cache, queryGenerator, logger)
×
327
    {
328
    }
×
329

330
    /// <summary>
331
    /// Initializes a new instance of the <see cref="DataSession" /> class.
332
    /// </summary>
333
    /// <param name="dataConfiguration">The configuration for the session</param>
334
    /// <exception cref="ArgumentNullException"><paramref name="dataConfiguration" /> is null</exception>
335
    public DataSession(IDataConfiguration<TDiscriminator> dataConfiguration)
336
        : base(dataConfiguration)
3✔
337
    {
338
    }
3✔
339
}
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