Skip to content

Commit dbd2a0f

Browse files
committed
misc updates
1 parent 31d2563 commit dbd2a0f

19 files changed

+1057
-368
lines changed

src/FluentCommand.Csv/CsvCommandExtensions.cs

Lines changed: 166 additions & 110 deletions
Large diffs are not rendered by default.

src/FluentCommand.Csv/FluentCommand.Csv.csproj

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<TargetFrameworks>netstandard2.0;net8.0;net9.0</TargetFrameworks>
4+
<TargetFrameworks>net8.0;net9.0</TargetFrameworks>
55
<RootNamespace>FluentCommand</RootNamespace>
66
</PropertyGroup>
77

88
<ItemGroup>
9-
<PackageReference Include="CsvHelper" Version="33.1.0" />
109
<PackageReference Include="Microsoft.IO.RecyclableMemoryStream" Version="3.0.1" />
1110
</ItemGroup>
1211

src/FluentCommand.SqlServer/FluentCommand.SqlServer.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
</ItemGroup>
1010

1111
<ItemGroup>
12-
<PackageReference Include="MicroSoft.Data.SqlClient" Version="6.0.1" />
12+
<PackageReference Include="MicroSoft.Data.SqlClient" Version="6.0.2" />
1313
</ItemGroup>
1414

1515
</Project>

src/FluentCommand/Attributes/GenerateReaderAttribute.cs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,26 @@
33
namespace FluentCommand.Attributes;
44

55
/// <summary>
6-
/// Source generate a data reader for the specified type
6+
/// Specifies that a source generator should create a data reader for the given type.
77
/// </summary>
8-
/// <param name="type">The type to generate a data reader for</param>
8+
/// <remarks>
9+
/// Apply this attribute to an assembly, class, interface, or module to indicate that a data reader should be generated
10+
/// for the specified <see cref="Type"/>. This is typically used in conjunction with source generators.
11+
/// </remarks>
12+
/// <example>
13+
/// <code>
14+
/// [assembly: GenerateReader(typeof(Product))]
15+
/// </code>
16+
/// </example>
17+
/// <param name="type">
18+
/// The <see cref="Type"/> for which to generate a data reader. Must not be <c>null</c>.
19+
/// </param>
920
[Conditional("FLUENTCOMMAND_GENERATOR")]
1021
[AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Module, AllowMultiple = true)]
1122
public class GenerateReaderAttribute(Type type) : Attribute
1223
{
24+
/// <summary>
25+
/// Gets the type for which the data reader will be generated.
26+
/// </summary>
1327
public Type Type { get; } = type ?? throw new ArgumentNullException(nameof(type));
1428
}

src/FluentCommand/ConcurrencyToken.cs

Lines changed: 112 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,15 @@ namespace FluentCommand;
55
/// <summary>
66
/// A structure to hold concurrency token
77
/// </summary>
8+
/// <remarks>
9+
/// This structure is commonly used to represent SQL Server <c>rowversion</c> (also known as <c>timestamp</c>) columns for optimistic concurrency control.
10+
/// </remarks>
811
public readonly struct ConcurrencyToken : IEquatable<ConcurrencyToken>
912
{
1013
/// <summary>
1114
/// The default empty token
1215
/// </summary>
13-
public static readonly ConcurrencyToken None = new(Array.Empty<byte>());
16+
public static readonly ConcurrencyToken None = new([]);
1417

1518
/// <summary>
1619
/// Gets the underlying value of the token.
@@ -26,7 +29,7 @@ namespace FluentCommand;
2629
/// <param name="value">The value.</param>
2730
public ConcurrencyToken(byte[] value)
2831
{
29-
Value = value ?? Array.Empty<byte>();
32+
Value = value ?? [];
3033
}
3134

3235
/// <summary>
@@ -36,12 +39,30 @@ public ConcurrencyToken(byte[] value)
3639
public ConcurrencyToken(string value)
3740
{
3841
#if NET5_0_OR_GREATER
39-
Value = string.IsNullOrEmpty(value) ? Array.Empty<byte>() : Convert.FromHexString(value);
42+
Value = string.IsNullOrEmpty(value) ? [] : Convert.FromHexString(value);
4043
#else
41-
Value = string.IsNullOrEmpty(value) ? Array.Empty<byte>() : FromHexString(value);
44+
Value = string.IsNullOrEmpty(value) ? [] : FromHexString(value);
4245
#endif
4346
}
4447

48+
/// <summary>
49+
/// Initializes a new instance of the <see cref="ConcurrencyToken"/> struct from a <see cref="long"/> value.
50+
/// </summary>
51+
/// <param name="value">The long value.</param>
52+
public ConcurrencyToken(long value)
53+
{
54+
Value = BitConverter.GetBytes(value);
55+
}
56+
57+
/// <summary>
58+
/// Initializes a new instance of the <see cref="ConcurrencyToken"/> struct from a <see cref="ulong"/> value.
59+
/// </summary>
60+
/// <param name="value">The ulong value.</param>
61+
public ConcurrencyToken(ulong value)
62+
{
63+
Value = BitConverter.GetBytes(value);
64+
}
65+
4566
/// <inheritdoc />
4667
public override string ToString()
4768
{
@@ -70,6 +91,7 @@ public override int GetHashCode()
7091
return Value.GetHashCode();
7192
}
7293

94+
7395
/// <summary>
7496
/// Performs an implicit conversion from <see cref="ConcurrencyToken"/> to byte array.
7597
/// </summary>
@@ -88,6 +110,45 @@ public override int GetHashCode()
88110
/// </returns>
89111
public static implicit operator string(ConcurrencyToken token) => token.ToString();
90112

113+
/// <summary>
114+
/// Performs an implicit conversion from <see cref="ConcurrencyToken"/> to <see cref="long"/>.
115+
/// </summary>
116+
/// <param name="token">The concurrency token.</param>
117+
/// <returns>
118+
/// The result of the conversion.
119+
/// </returns>
120+
public static implicit operator long(ConcurrencyToken token)
121+
{
122+
if (token.Value == null || token.Value.Length == 0)
123+
return 0L;
124+
125+
if (token.Value.Length < sizeof(long))
126+
throw new InvalidCastException("The token value is too short to convert to a long.");
127+
128+
// Use little-endian to match BitConverter default
129+
return BitConverter.ToInt64(token.Value, 0);
130+
}
131+
132+
/// <summary>
133+
/// Performs an implicit conversion from <see cref="ConcurrencyToken"/> to <see cref="ulong"/>.
134+
/// </summary>
135+
/// <param name="token">The concurrency token.</param>
136+
/// <returns>
137+
/// The result of the conversion.
138+
/// </returns>
139+
public static implicit operator ulong(ConcurrencyToken token)
140+
{
141+
if (token.Value == null || token.Value.Length == 0)
142+
return 0UL;
143+
144+
if (token.Value.Length < sizeof(ulong))
145+
throw new InvalidCastException("The token value is too short to convert to a ulong.");
146+
147+
// Use little-endian to match BitConverter default
148+
return BitConverter.ToUInt64(token.Value, 0);
149+
}
150+
151+
91152
/// <summary>
92153
/// Performs an implicit conversion from byte array to <see cref="ConcurrencyToken"/>.
93154
/// </summary>
@@ -106,6 +167,53 @@ public override int GetHashCode()
106167
/// </returns>
107168
public static implicit operator ConcurrencyToken(string token) => new(token);
108169

170+
/// <summary>
171+
/// Performs an implicit conversion from <see cref="long"/> to <see cref="ConcurrencyToken"/>.
172+
/// </summary>
173+
/// <param name="value">The long value.</param>
174+
/// <returns>
175+
/// The result of the conversion.
176+
/// </returns>
177+
public static implicit operator ConcurrencyToken(long value)
178+
{
179+
var bytes = BitConverter.GetBytes(value);
180+
return new ConcurrencyToken(bytes);
181+
}
182+
183+
/// <summary>
184+
/// Performs an implicit conversion from <see cref="ulong"/> to <see cref="ConcurrencyToken"/>.
185+
/// </summary>
186+
/// <param name="value">The ulong value.</param>
187+
/// <returns>
188+
/// The result of the conversion.
189+
/// </returns>
190+
public static implicit operator ConcurrencyToken(ulong value)
191+
{
192+
var bytes = BitConverter.GetBytes(value);
193+
return new ConcurrencyToken(bytes);
194+
}
195+
196+
197+
/// <summary>
198+
/// Determines whether two <see cref="ConcurrencyToken"/> instances are equal.
199+
/// </summary>
200+
/// <param name="left">The first <see cref="ConcurrencyToken"/> to compare.</param>
201+
/// <param name="right">The second <see cref="ConcurrencyToken"/> to compare.</param>
202+
/// <returns>
203+
/// <see langword="true"/> if the two <see cref="ConcurrencyToken"/> instances are equal; otherwise, <see langword="false"/>.
204+
/// </returns>
205+
public static bool operator ==(ConcurrencyToken left, ConcurrencyToken right) => left.Equals(right);
206+
207+
/// <summary>
208+
/// Determines whether two <see cref="ConcurrencyToken"/> instances are not equal.
209+
/// </summary>
210+
/// <param name="left">The first <see cref="ConcurrencyToken"/> to compare.</param>
211+
/// <param name="right">The second <see cref="ConcurrencyToken"/> to compare.</param>
212+
/// <returns>
213+
/// <see langword="true"/> if the two <see cref="ConcurrencyToken"/> instances are not equal; otherwise, <see langword="false"/>.
214+
/// </returns>
215+
public static bool operator !=(ConcurrencyToken left, ConcurrencyToken right) => !(left == right);
216+
109217

110218
#if NETSTANDARD2_0
111219
private static string ToHexString(byte[] bytes)

src/FluentCommand/DisposableBase.cs

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
namespace FluentCommand;
22

33
/// <summary>
4-
/// A base class that implements <see cref="IDisposable"/>
4+
/// Provides a base implementation of the <see cref="IDisposable"/> pattern, including support for asynchronous disposal on supported platforms.
55
/// </summary>
66
public abstract class DisposableBase
77
: IDisposable
@@ -12,7 +12,7 @@ public abstract class DisposableBase
1212
private int _disposeState;
1313

1414
/// <summary>
15-
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
15+
/// Releases all resources used by the current instance of the <see cref="DisposableBase"/> class.
1616
/// </summary>
1717
public void Dispose()
1818
{
@@ -21,9 +21,11 @@ public void Dispose()
2121
}
2222

2323
/// <summary>
24-
/// Releases unmanaged and - optionally - managed resources
24+
/// Releases the unmanaged resources used by the object and, optionally, releases the managed resources.
2525
/// </summary>
26-
/// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
26+
/// <param name="disposing">
27+
/// <c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.
28+
/// </param>
2729
protected void Dispose(bool disposing)
2830
{
2931
// set state to disposing
@@ -40,7 +42,7 @@ protected void Dispose(bool disposing)
4042
}
4143

4244
/// <summary>
43-
/// Throws <see cref="ObjectDisposedException"/> if this instance is disposed.
45+
/// Throws an <see cref="ObjectDisposedException"/> if this instance has already been disposed.
4446
/// </summary>
4547
protected void AssertDisposed()
4648
{
@@ -49,27 +51,27 @@ protected void AssertDisposed()
4951
}
5052

5153
/// <summary>
52-
/// Get the disposed state of the object.
54+
/// Gets a value indicating whether this instance has been disposed.
5355
/// </summary>
5456
protected bool IsDisposed => _disposeState != 0;
5557

5658
/// <summary>
57-
/// Disposes the managed resources.
59+
/// Releases managed resources. Override this method to dispose managed resources in derived classes.
5860
/// </summary>
5961
protected virtual void DisposeManagedResources()
6062
{ }
6163

6264
/// <summary>
63-
/// Disposes the unmanaged resources.
65+
/// Releases unmanaged resources. Override this method to dispose unmanaged resources in derived classes.
6466
/// </summary>
6567
protected virtual void DisposeUnmanagedResources()
6668
{ }
6769

6870
#if NETCOREAPP3_0_OR_GREATER
6971
/// <summary>
70-
/// Disposes the asynchronous.
72+
/// Asynchronously releases all resources used by the current instance of the <see cref="DisposableBase"/> class.
7173
/// </summary>
72-
/// <returns></returns>
74+
/// <returns>A <see cref="ValueTask"/> that represents the asynchronous dispose operation.</returns>
7375
public async ValueTask DisposeAsync()
7476
{
7577
// set state to disposing
@@ -80,20 +82,23 @@ public async ValueTask DisposeAsync()
8082

8183
// set state to disposed
8284
Interlocked.Exchange(ref _disposeState, 2);
85+
86+
GC.SuppressFinalize(this);
8387
}
8488

8589
/// <summary>
86-
/// Disposes the managed resources.
90+
/// Asynchronously releases managed resources. Override this method to asynchronously dispose managed resources in derived classes.
8791
/// </summary>
92+
/// <returns>A <see cref="ValueTask"/> that represents the asynchronous dispose operation.</returns>
8893
protected virtual ValueTask DisposeResourcesAsync()
8994
{
9095
return ValueTask.CompletedTask;
9196
}
9297
#endif
9398

9499
/// <summary>
95-
/// Releases unmanaged resources and performs other cleanup operations before the
96-
/// <see cref="DisposableBase"/> is reclaimed by garbage collection.
100+
/// Finalizes an instance of the <see cref="DisposableBase"/> class.
101+
/// Releases unmanaged resources and performs other cleanup operations before the object is reclaimed by garbage collection.
97102
/// </summary>
98103
~DisposableBase()
99104
{

src/FluentCommand/Extensions/CollectionExtensions.cs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,3 @@
1-
using System;
2-
using System.Collections.Generic;
3-
using System.Linq;
4-
51
namespace FluentCommand.Extensions;
62

73
/// <summary>
@@ -10,15 +6,15 @@ namespace FluentCommand.Extensions;
106
public static class CollectionExtensions
117
{
128
/// <summary>
13-
/// Gets the first element from the <paramref name="source"/> that passes the test specified by <paramref name="predicate" />;
9+
/// Gets the first element from the <paramref name="source"/> that passes the test specified by <paramref name="predicate" />;
1410
/// otherwise the <paramref name="valueFactory"/> is called and the result is added to the source.
1511
/// </summary>
1612
/// <typeparam name="T">The type of the elements of <paramref name="source" />.</typeparam>
1713
/// <param name="source">An <see cref="T:System.Collections.Generic.ICollection`1" /> to get or add an element from.</param>
1814
/// <param name="predicate">A function to test each element for a condition.</param>
1915
/// <param name="valueFactory">The function used to generate a value when not found in the collection.</param>
2016
/// <returns>
21-
/// The value from <paramref name="valueFactory"/> if <paramref name="source" /> is empty or if no element passes the test specified by <paramref name="predicate" />;
17+
/// The value from <paramref name="valueFactory"/> if <paramref name="source" /> is empty or if no element passes the test specified by <paramref name="predicate" />;
2218
/// otherwise, the first element in <paramref name="source" /> that passes the test specified by <paramref name="predicate" />.
2319
/// </returns>
2420
public static T FirstOrAdd<T>(this ICollection<T> source, Func<T, bool> predicate, Func<T> valueFactory)

src/FluentCommand/Extensions/DataTableExtensions.cs

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,28 @@
1-
using System.Collections.Generic;
21
using System.Data;
32

43
namespace FluentCommand.Extensions;
54

65
/// <summary>
7-
/// Extension method for <see cref="DataTable"/>
6+
/// Provides extension methods for the <see cref="DataTable"/> class.
87
/// </summary>
98
public static class DataTableExtensions
109
{
1110
/// <summary>
12-
/// Converts the IEnumerable to a <see cref="DataTable" />.
11+
/// Converts an <see cref="IEnumerable{T}"/> to a <see cref="DataTable"/> instance.
1312
/// </summary>
14-
/// <typeparam name="T">The type of the source data</typeparam>
15-
/// <param name="source">The source to convert.</param>
16-
/// <param name="ignoreNames">The ignored property names.</param>
17-
/// <returns>A <see cref="DataTable"/> from the specified source.</returns>
13+
/// <typeparam name="T">The type of the elements in the source collection.</typeparam>
14+
/// <param name="source">The collection of objects to convert to a <see cref="DataTable"/>.</param>
15+
/// <param name="ignoreNames">
16+
/// An optional collection of property names to ignore when creating columns in the <see cref="DataTable"/>.
17+
/// If <c>null</c>, all public properties are included.
18+
/// </param>
19+
/// <returns>
20+
/// A <see cref="DataTable"/> populated with the data from the <paramref name="source"/> collection,
21+
/// or <c>null</c> if <paramref name="source"/> is <c>null</c>.
22+
/// </returns>
23+
/// <remarks>
24+
/// This method uses <see cref="ListDataReader{T}"/> to read the data from the source collection and load it into a <see cref="DataTable"/>.
25+
/// </remarks>
1826
public static DataTable ToDataTable<T>(this IEnumerable<T> source, IEnumerable<string> ignoreNames = null)
1927
where T : class
2028
{

0 commit comments

Comments
 (0)