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

loresoft / FluentCommand / 23278216331

19 Mar 2026 03:19AM UTC coverage: 57.398% (+0.7%) from 56.658%
23278216331

push

github

pwelter34
Enable nullable and improve source generators

1403 of 3069 branches covered (45.72%)

Branch coverage included in aggregate %.

527 of 907 new or added lines in 58 files covered. (58.1%)

22 existing lines in 10 files now uncovered.

4288 of 6846 relevant lines covered (62.64%)

330.58 hits per line

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

66.67
/src/FluentCommand/ConcurrencyToken.cs
1
using FluentCommand.Internal;
2

3
namespace FluentCommand;
4

5
/// <summary>
6
/// A structure to hold concurrency token
7
/// </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>
11
public readonly struct ConcurrencyToken : IEquatable<ConcurrencyToken>
12
{
13
    /// <summary>
14
    /// The default empty token
15
    /// </summary>
16
    public static readonly ConcurrencyToken None = new([]);
1✔
17

18
    /// <summary>
19
    /// Gets the underlying value of the token.
20
    /// </summary>
21
    /// <value>
22
    /// The underlying value of the token.
23
    /// </value>
24
    public byte[] Value { get; }
57✔
25

26
    /// <summary>
27
    /// Initializes a new instance of the <see cref="ConcurrencyToken"/> struct.
28
    /// </summary>
29
    /// <param name="value">The value.</param>
30
    public ConcurrencyToken(byte[] value)
31
    {
32
        Value = value ?? [];
1,347✔
33
    }
1,347✔
34

35
    /// <summary>
36
    /// Initializes a new instance of the <see cref="ConcurrencyToken"/> struct.
37
    /// </summary>
38
    /// <param name="value">The value.</param>
39
    public ConcurrencyToken(string? value)
40
    {
41
#if NET5_0_OR_GREATER
42
        Value = string.IsNullOrEmpty(value) ? [] : Convert.FromHexString(value);
3✔
43
#else
44
        Value = string.IsNullOrEmpty(value) ? [] : FromHexString(value);
45
#endif
46
    }
3✔
47

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);
2✔
55
    }
2✔
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);
2✔
64
    }
2✔
65

66
    /// <inheritdoc />
67
    public override string ToString()
68
    {
69
#if NET5_0_OR_GREATER
70
        return Value != null ? Convert.ToHexString(Value) : string.Empty;
2!
71
#else
72
        return Value != null ? ToHexString(Value) : string.Empty;
73
#endif
74
    }
75

76
    /// <inheritdoc />
77
    public override bool Equals(object? obj)
78
    {
79
        return obj is ConcurrencyToken token && Equals(token);
×
80
    }
81

82
    /// <inheritdoc />
83
    public bool Equals(ConcurrencyToken other)
84
    {
85
        if (ReferenceEquals(Value, other.Value))
6✔
86
            return true;
3✔
87

88
        if (Value is null || other.Value is null)
3!
NEW
89
            return false;
×
90

91
        return Value.AsSpan().SequenceEqual(other.Value);
3✔
92
    }
93

94
    /// <inheritdoc />
95
    public override int GetHashCode()
96
    {
NEW
97
        if (Value is null || Value.Length == 0)
×
NEW
98
            return 0;
×
99

100
        unchecked
101
        {
NEW
102
            int hash = 17;
×
103

NEW
104
            foreach (var b in Value)
×
NEW
105
                hash = (hash * 31) + b;
×
106

NEW
107
            return hash;
×
108
        }
109
    }
110

111

112
    /// <summary>
113
    /// Performs an implicit conversion from <see cref="ConcurrencyToken"/> to byte array.
114
    /// </summary>
115
    /// <param name="token">The concurrency token.</param>
116
    /// <returns>
117
    /// The result of the conversion.
118
    /// </returns>
119
    public static implicit operator byte[](ConcurrencyToken token) => token.Value;
1✔
120

121
    /// <summary>
122
    /// Performs an implicit conversion from <see cref="ConcurrencyToken"/> to <see cref="System.String"/>.
123
    /// </summary>
124
    /// <param name="token">The concurrency token.</param>
125
    /// <returns>
126
    /// The result of the conversion.
127
    /// </returns>
128
    public static implicit operator string(ConcurrencyToken token) => token.ToString();
1✔
129

130
    /// <summary>
131
    /// Performs an implicit conversion from <see cref="ConcurrencyToken"/> to <see cref="long"/>.
132
    /// </summary>
133
    /// <param name="token">The concurrency token.</param>
134
    /// <returns>
135
    /// The result of the conversion.
136
    /// </returns>
137
    public static implicit operator long(ConcurrencyToken token)
138
    {
139
        if (token.Value == null || token.Value.Length == 0)
2!
140
            return 0L;
×
141

142
        if (token.Value.Length < sizeof(long))
2✔
143
            throw new InvalidCastException("The token value is too short to convert to a long.");
1✔
144

145
        // Use little-endian to match BitConverter default
146
        return BitConverter.ToInt64(token.Value, 0);
1✔
147
    }
148

149
    /// <summary>
150
    /// Performs an implicit conversion from <see cref="ConcurrencyToken"/> to <see cref="ulong"/>.
151
    /// </summary>
152
    /// <param name="token">The concurrency token.</param>
153
    /// <returns>
154
    /// The result of the conversion.
155
    /// </returns>
156
    public static implicit operator ulong(ConcurrencyToken token)
157
    {
158
        if (token.Value == null || token.Value.Length == 0)
2!
159
            return 0UL;
×
160

161
        if (token.Value.Length < sizeof(ulong))
2✔
162
            throw new InvalidCastException("The token value is too short to convert to a ulong.");
1✔
163

164
        // Use little-endian to match BitConverter default
165
        return BitConverter.ToUInt64(token.Value, 0);
1✔
166
    }
167

168

169
    /// <summary>
170
    /// Performs an implicit conversion from byte array to <see cref="ConcurrencyToken"/>.
171
    /// </summary>
172
    /// <param name="token">The concurrency token.</param>
173
    /// <returns>
174
    /// The result of the conversion.
175
    /// </returns>
176
    public static implicit operator ConcurrencyToken(byte[] token) => new(token);
53✔
177

178
    /// <summary>
179
    /// Performs an implicit conversion from <see cref="System.String"/> to <see cref="ConcurrencyToken"/>.
180
    /// </summary>
181
    /// <param name="token">The concurrency token.</param>
182
    /// <returns>
183
    /// The result of the conversion.
184
    /// </returns>
185
    public static implicit operator ConcurrencyToken(string token) => new(token);
1✔
186

187
    /// <summary>
188
    /// Performs an implicit conversion from <see cref="long"/> to <see cref="ConcurrencyToken"/>.
189
    /// </summary>
190
    /// <param name="value">The long value.</param>
191
    /// <returns>
192
    /// The result of the conversion.
193
    /// </returns>
194
    public static implicit operator ConcurrencyToken(long value)
195
    {
196
        var bytes = BitConverter.GetBytes(value);
1✔
197
        return new ConcurrencyToken(bytes);
1✔
198
    }
199

200
    /// <summary>
201
    /// Performs an implicit conversion from <see cref="ulong"/> to <see cref="ConcurrencyToken"/>.
202
    /// </summary>
203
    /// <param name="value">The ulong value.</param>
204
    /// <returns>
205
    /// The result of the conversion.
206
    /// </returns>
207
    public static implicit operator ConcurrencyToken(ulong value)
208
    {
209
        var bytes = BitConverter.GetBytes(value);
1✔
210
        return new ConcurrencyToken(bytes);
1✔
211
    }
212

213

214
    /// <summary>
215
    /// Determines whether two <see cref="ConcurrencyToken"/> instances are equal.
216
    /// </summary>
217
    /// <param name="left">The first <see cref="ConcurrencyToken"/> to compare.</param>
218
    /// <param name="right">The second <see cref="ConcurrencyToken"/> to compare.</param>
219
    /// <returns>
220
    /// <see langword="true"/> if the two <see cref="ConcurrencyToken"/> instances are equal; otherwise, <see langword="false"/>.
221
    /// </returns>
222
    public static bool operator ==(ConcurrencyToken left, ConcurrencyToken right) => left.Equals(right);
4✔
223

224
    /// <summary>
225
    /// Determines whether two <see cref="ConcurrencyToken"/> instances are not equal.
226
    /// </summary>
227
    /// <param name="left">The first <see cref="ConcurrencyToken"/> to compare.</param>
228
    /// <param name="right">The second <see cref="ConcurrencyToken"/> to compare.</param>
229
    /// <returns>
230
    /// <see langword="true"/> if the two <see cref="ConcurrencyToken"/> instances are not equal; otherwise, <see langword="false"/>.
231
    /// </returns>
232
    public static bool operator !=(ConcurrencyToken left, ConcurrencyToken right) => !(left == right);
2✔
233

234

235
#if NETSTANDARD2_0
236
    private static string ToHexString(byte[] bytes)
237
    {
238
        var hex = StringBuilderCache.Acquire(bytes.Length * 2);
239

240
        foreach (var b in bytes)
241
            hex.Append(b.ToString("X2"));
242

243
        return StringBuilderCache.ToString(hex);
244
    }
245

246
    private static byte[] FromHexString(string? hexString)
247
    {
248
        if (hexString == null)
249
            return [];
250

251
        var hexLength = hexString.Length;
252
        var bytes = new byte[hexLength / 2];
253

254
        for (var i = 0; i < hexLength; i += 2)
255
            bytes[i / 2] = Convert.ToByte(hexString.Substring(i, 2), 16);
256

257
        return bytes;
258
    }
259
#endif
260
}
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