Skip to content

Commit 2bd082d

Browse files
Add MSSqlClient project and enhance documentation
- Updated solution file to include `Open.Database.Extensions.MSSqlClient` and `Source\Directory.Build.props`. - Expanded `README.md` with a new "Principles" section and updated "Features" and "8.0 Release Notes". - Added XML documentation comments to constructors in `ExpressiveCommand`, `ExpressiveCommandBase`, `ExpressiveDbCommand`, and `ExpressiveDbCommandBase` for improved clarity. - Refactored connection management methods in `ConnectionProvider.Command.cs` to utilize the new `AsPool` method. - Enhanced `Retrieve.cs` with a `SuppressMessage` attribute and improved handling of cancellation tokens in asynchronous methods. - Added `using System.Data.Common` directive in `IDbConnectionFactory.cs` for better clarity. - Updated `DbConnectionFactoryExtensions` to include new overloads for the `AsPool` method, enhancing flexibility in connection management.
1 parent ff44b90 commit 2bd082d

10 files changed

+80
-22
lines changed

Open.Database.Extensions.sln

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
1919
ProjectSection(SolutionItems) = preProject
2020
.editorconfig = .editorconfig
2121
Source\Directory.Build.props = Source\Directory.Build.props
22+
README.md = README.md
2223
EndProjectSection
2324
EndProject
2425
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Open.Database.Extensions.MSSqlClient", "Source\MSSqlClient\Open.Database.Extensions.MSSqlClient.csproj", "{B9245504-9E2F-417F-87AC-8950BFB4FD4E}"

README.md

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,30 @@
44

55
Useful set of utilities and abstractions for simplifying modern database operations and ensuring dependency injection compatibility.
66

7-
## Connection Factories
7+
## Principles
8+
9+
- Minimize connection open time.
10+
- Deferred/lazy transformation.
11+
- Optimize for specific use cases.
12+
- Minimize boilerplate code.
13+
14+
## Features
15+
- Provides a fluent interface for database operations.
16+
- Supports dependency injection for connection factories.
17+
- Support both synchronous and asynchronous operations.
18+
- Provides expressive commands for executing SQL queries and stored procedures.
19+
20+
### Connection Factories
821

922
Connection factories facilitate creation and disposal of connections without the concern of a connection reference or need for awareness of a connection string.
1023
A `SqlConnectionFactory` is provided and can be overridden to provide more specific dependency injection configurations.
1124

12-
## Expressive Commands
25+
### Expressive Commands
1326

1427
The provided expressive command classes allow for an expressive means to append parameters and execute the results without lengthy complicated setup.
1528

1629
Extensions are provided to create commands from connection factories.
1730

18-
## 8.0 Release Notes
19-
20-
- All `.ConfigureAwait(true)` are now `.ConfigureAwait(false)` as they should be. The caller will need to `.ConfigureAwait(true)` if they need to resume on the calling context.
21-
- Added `Open.Database.Extensions.MSSqlClient` for `Microsoft.Data.SqlClient` support.
22-
- .NET 8.0 added to targets to ensure potential compliation and performance improvements are available.
23-
- Improved nullable integrity.
24-
2531
### Example
2632

2733
```cs
@@ -127,3 +133,10 @@ public static bool TryTransaction()
127133
.ExecuteScalar<bool>();
128134
}));
129135
```
136+
137+
## 8.0 Release Notes
138+
139+
- All `.ConfigureAwait(true)` are now `.ConfigureAwait(false)` as they should be. The caller will need to `.ConfigureAwait(true)` if they need to resume on the calling context.
140+
- Added `Open.Database.Extensions.MSSqlClient` for `Microsoft.Data.SqlClient` support.
141+
- .NET 8.0 added to targets to ensure potential compliation and performance improvements are available.
142+
- Improved nullable integrity.

Source/Core/ExpressiveCommand.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,17 @@ public ExpressiveCommand(
2727
{
2828
}
2929

30+
/// <summary>Constructs a <see cref="ExpressiveCommand"/>.</summary>
31+
/// <inheritdoc cref="ExpressiveCommandBase{TConnection, TCommand, TReader, TDbType, TThis}.ExpressiveCommandBase(IDbConnectionFactory{TConnection}, CommandType, string, IEnumerable{ExpressiveCommandBase{TConnection, TCommand, TReader, TDbType, TThis}.Param}?)" />
32+
public ExpressiveCommand(
33+
Func<IDbConnection> connFactory,
34+
CommandType type,
35+
string command,
36+
IEnumerable<Param>? @params = null)
37+
: base(connFactory, type, command, @params)
38+
{
39+
}
40+
3041
/// <summary>Constructs a <see cref="ExpressiveCommand"/>.</summary>
3142
/// <inheritdoc cref="ExpressiveCommandBase{TConnection, TCommand, TReader, TDbType, TThis}.ExpressiveCommandBase(TConnection, IDbTransaction?, CommandType, string, IEnumerable{ExpressiveCommandBase{TConnection, TCommand, TReader, TDbType, TThis}.Param}?)" />
3243
public ExpressiveCommand(

Source/Core/ExpressiveCommandBase.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,16 @@ protected ExpressiveCommandBase(
7171
{
7272
}
7373

74+
/// <inheritdoc cref="ExpressiveCommandBase(IDbConnectionFactory{TConnection}, CommandType, string, IEnumerable{Param}?)"/>
75+
protected ExpressiveCommandBase(
76+
Func<TConnection> connFactory,
77+
CommandType type,
78+
string command,
79+
IEnumerable<Param>? @params)
80+
: this((connFactory ?? throw new ArgumentNullException(nameof(connFactory))).AsPool(), type, command, @params)
81+
{
82+
}
83+
7484
/// <summary>Constructs a <see cref="ExpressiveCommandBase{TConnection, TCommand, TReader, TDbType, TThis}"/>.</summary>
7585
/// <param name="connection">The connection to execute the command on.</param>
7686
/// <param name="transaction">The optional transaction to execute the command on.</param>

Source/Core/ExpressiveDbCommand.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,16 @@ public ExpressiveDbCommand(
2727
{
2828
}
2929

30+
/// <inheritdoc cref="ExpressiveDbCommandBase{TConnection, TCommand, TReader, TDbType, TThis}.ExpressiveDbCommandBase(IDbConnectionFactory{TConnection}, CommandType, string, IEnumerable{ExpressiveCommandBase{TConnection, TCommand, TReader, TDbType, TThis}.Param}?)"/>
31+
public ExpressiveDbCommand(
32+
Func<DbConnection> connFactory,
33+
CommandType type,
34+
string command,
35+
IEnumerable<Param>? @params = null)
36+
: base(connFactory.AsPool(), type, command, @params)
37+
{
38+
}
39+
3040
/// <summary>Constructs a <see cref="ExpressiveDbCommand"/>.</summary>
3141
/// <inheritdoc />
3242
public ExpressiveDbCommand(

Source/Core/ExpressiveDbCommandBase.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,16 @@ protected ExpressiveDbCommandBase(
4545
{
4646
}
4747

48+
/// <inheritdoc cref="ExpressiveDbCommandBase(IDbConnectionFactory{TConnection}, CommandType, string, IEnumerable{ExpressiveCommandBase{TConnection, TCommand, TReader, TDbType, TThis}.Param}?)"/>
49+
protected ExpressiveDbCommandBase(
50+
Func<TConnection> connFactory,
51+
CommandType type,
52+
string command,
53+
IEnumerable<Param>? @params = null)
54+
: base(connFactory, type, command, @params)
55+
{
56+
}
57+
4858
/// <summary>Constructs a an expressive command.</summary>
4959
/// <param name="connection">The connection to execute the command on.</param>
5060
/// <param name="transaction">The optional transaction to execute the command on.</param>

Source/Core/Extensions/ConnectionProvider.Command.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ public static ExpressiveCommand Command(
192192
this Func<IDbConnection> connectionSource,
193193
string command,
194194
CommandType type = CommandType.Text)
195-
=> Command(DbConnectionFactory.Create(connectionSource), command, type);
195+
=> Command(connectionSource.AsPool(), command, type);
196196

197197
/// <summary>
198198
/// Creates an <see cref="ExpressiveCommand"/> with command type set to <see cref="CommandType.StoredProcedure"/> for subsequent configuration and execution.
@@ -216,7 +216,7 @@ public static ExpressiveDbCommand Command<TConnection>(
216216
string command,
217217
CommandType type = CommandType.Text)
218218
where TConnection : DbConnection
219-
=> Command(DbConnectionFactory.Create(connectionSource), command, type);
219+
=> Command(connectionSource.AsPool(), command, type);
220220

221221
/// <summary>
222222
/// Creates an <see cref="ExpressiveDbCommand"/> with command type set to <see cref="CommandType.StoredProcedure"/> for subsequent configuration and execution.
@@ -228,5 +228,5 @@ public static ExpressiveDbCommand StoredProcedure<TConnection>(
228228
this Func<TConnection> connectionSource,
229229
string procedureName)
230230
where TConnection : DbConnection
231-
=> StoredProcedure(DbConnectionFactory.Create(connectionSource), procedureName);
231+
=> StoredProcedure(connectionSource.AsPool(), procedureName);
232232
}

Source/Core/Extensions/Retrieve.cs

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11

22
namespace Open.Database.Extensions;
33

4+
[SuppressMessage("Design", "CA1068:CancellationToken parameters must come last", Justification = "Internal function that requires a cancellation token.")]
45
public static partial class CoreExtensions
56
{
67
internal static QueryResultQueue<object[]> RetrieveInternal(
@@ -141,12 +142,9 @@ public static ValueTask<QueryResultQueue<object[]>> RetrieveAsync(
141142
CancellationToken cancellationToken)
142143
=> RetrieveAsync(reader, true, cancellationToken);
143144

144-
[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1068:CancellationToken parameters must come last", Justification = "Internal function that requires a cancellation token.")]
145145
static ValueTask<QueryResultQueue<object[]>> RetrieveAsyncInternal(DbDataReader reader, CancellationToken cancellationToken, IEnumerable<int> ordinals, IEnumerable<string>? columnNames = null, bool readStarted = false, bool useReadAsync = true)
146146
=> RetrieveAsyncInternal(null, reader, cancellationToken, ordinals, columnNames, readStarted, useReadAsync)!;
147147

148-
[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1068:CancellationToken parameters must come last", Justification = "Internal function that requires a cancellation token.")]
149-
[System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0046:Convert to conditional expression", Justification = "<Pending>")]
150148
static async ValueTask<QueryResultQueue<object[]>> RetrieveAsyncInternal(ArrayPool<object>? arrayPool, DbDataReader reader, CancellationToken cancellationToken, IEnumerable<int> ordinals, IEnumerable<string>? columnNames = null, bool readStarted = false, bool useReadAsync = true)
151149
{
152150
if (reader is null) throw new ArgumentNullException(nameof(reader));
@@ -237,7 +235,6 @@ public static ValueTask<QueryResultQueue<object[]>> RetrieveAsync(
237235
/// <param name="n">The first ordinal to include in the request to the reader for each record.</param>
238236
/// <param name="others">The remaining ordinals to request from the reader for each record.</param>
239237
/// <inheritdoc cref="RetrieveAsync(DbDataReader, IEnumerable{int}, bool, CancellationToken)" />
240-
[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1068:CancellationToken parameters must come last", Justification = "Method takes params and cannot have a the cancellation token last.")]
241238
public static ValueTask<QueryResultQueue<object[]>> RetrieveAsync(
242239
this DbDataReader reader,
243240
CancellationToken cancellationToken,
@@ -295,7 +292,6 @@ public static ValueTask<QueryResultQueue<object[]>> RetrieveAsync(
295292
/// <param name="c">The first column name to include in the request to the reader for each record.</param>
296293
/// <param name="others">The remaining column names to request from the reader for each record.</param>
297294
/// <inheritdoc cref="RetrieveAsync(DbDataReader, IEnumerable{string}, bool, bool, CancellationToken)"/>
298-
[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1068:CancellationToken parameters must come last", Justification = "Method takes params and cannot have a the cancellation token last.")]
299295
public static ValueTask<QueryResultQueue<object[]>> RetrieveAsync(
300296
this DbDataReader reader,
301297
CancellationToken cancellationToken,
@@ -322,7 +318,6 @@ public static ValueTask<QueryResultQueue<object[]>> RetrieveAsync(
322318
CancellationToken cancellationToken)
323319
=> RetrieveAsync(command, true, cancellationToken);
324320

325-
[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1068:CancellationToken parameters must come last", Justification = "Internal function that requires a cancellation token.")]
326321
static ValueTask<QueryResultQueue<object[]>> RetrieveAsyncInternal(
327322
DbCommand command,
328323
CancellationToken cancellationToken,
@@ -362,7 +357,6 @@ public static ValueTask<QueryResultQueue<object[]>> RetrieveAsync(
362357
/// <param name="n">The first ordinal to include in the request to the reader for each record.</param>
363358
/// <param name="others">The remaining ordinals to request from the reader for each record.</param>
364359
/// <inheritdoc cref="RetrieveAsync(DbCommand, IEnumerable{string}, bool, bool, CancellationToken)"/>
365-
[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1068:CancellationToken parameters must come last", Justification = "Method takes params and cannot have a the cancellation token last.")]
366360
public static ValueTask<QueryResultQueue<object[]>> RetrieveAsync(
367361
this DbCommand command,
368362
CancellationToken cancellationToken,
@@ -411,7 +405,6 @@ public static ValueTask<QueryResultQueue<object[]>> RetrieveAsync(
411405
/// <param name="columnName">The first column name to include in the request to the reader for each record.</param>
412406
/// <param name="otherColumnNames">The remaining column names to request from the reader for each record.</param>
413407
/// <inheritdoc cref="RetrieveAsync(DbCommand, IEnumerable{string}, bool, bool, CancellationToken)"/>
414-
[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1068:CancellationToken parameters must come last", Justification = "Method takes params and cannot have a the cancellation token last.")]
415408
public static ValueTask<QueryResultQueue<object[]>> RetrieveAsync(
416409
this DbCommand command,
417410
CancellationToken cancellationToken,

Source/Core/Extensions/_.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@ internal static IEnumerable<T> Concat<T>(T first, ICollection<T> remaining)
4545
public static Span<object?> CopyToDBNullAsNull(this Span<object?> values, Span<object?> target)
4646
=> CopyToDBNullAsNull((ReadOnlySpan<object?>)values, target);
4747

48-
4948
/// <inheritdoc cref="CopyToDBNullAsNull(ReadOnlySpan{object?}, Span{object?})"/>
5049
public static Span<object?> CopyToDBNullAsNull(this ReadOnlySpan<object?> values, Memory<object?> target)
5150
=> CopyToDBNullAsNull(values, target.Span);

Source/Core/IDbConnectionFactory.cs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
namespace Open.Database.Extensions;
1+
using System.Data.Common;
2+
3+
namespace Open.Database.Extensions;
24

35
/// <summary>
46
/// Common interface for creating a connection. Can easily be used with dependency injection.
@@ -77,6 +79,10 @@ public void Give(IDbConnection connection)
7779
public static ConnectionFactoryToPoolAdapter AsPool(this IDbConnectionFactory connectionFactory)
7880
=> new (connectionFactory);
7981

82+
/// <inheritdoc cref="AsPool(IDbConnectionFactory)"/>
83+
public static ConnectionFactoryToPoolAdapter AsPool(this Func<IDbConnection> connectionFactory)
84+
=> new(connectionFactory);
85+
8086
/// <summary>
8187
/// Provides a connection pool that simply creates from a connection factory and disposes when returned.
8288
/// </summary>
@@ -86,6 +92,11 @@ public static ConnectionFactoryToPoolAdapter<TConnection> AsPool<TConnection>(th
8692
where TConnection : IDbConnection
8793
=> new (connectionFactory);
8894

95+
/// <inheritdoc cref="AsPool{TConnection}(IDbConnectionFactory{TConnection})"/>
96+
public static ConnectionFactoryToPoolAdapter<TConnection> AsPool<TConnection>(this Func<TConnection> connectionFactory)
97+
where TConnection : IDbConnection
98+
=> new(connectionFactory);
99+
89100
/// <summary>
90101
/// Coerces a non-generic connection factory to a generic one.
91102
/// </summary>

0 commit comments

Comments
 (0)