• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In
You are now the owner of this repo.

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

67.72
/src/FluentCommand/ListDataReader.cs
1
using System.Collections;
2
using System.Data;
3
using System.Data.Common;
4

5
using FluentCommand.Extensions;
6
using FluentCommand.Reflection;
7

8
namespace FluentCommand;
9

10
/// <summary>
11
/// Read a list of items using a <see cref="DbDataReader"/>
12
/// </summary>
13
/// <typeparam name="T">The type of items being read</typeparam>
14
public class ListDataReader<T> : DbDataReader where T : class
15
{
16
    // ReSharper disable once StaticMemberInGenericType
17
    private static readonly TypeAccessor _typeAccessor;
18

19
    private readonly IEnumerator<T> _iterator;
20
    private readonly List<IMemberAccessor> _activeColumns;
21
    private readonly Dictionary<string, int> _columnOrdinals;
22
    private bool _closed;
23
    private bool _disposed;
24

25
    static ListDataReader()
26
    {
27
        _typeAccessor = TypeAccessor.GetAccessor<T>();
5✔
28
    }
5✔
29

30
    /// <summary>
31
    /// Initializes a new instance of the <see cref="ListDataReader{T}"/> class.
32
    /// </summary>
33
    /// <param name="list">The list of items to read</param>
34
    /// <param name="ignoreNames">A list of property names to ignore in the reader</param>
35
    public ListDataReader(IEnumerable<T> list, IEnumerable<string>? ignoreNames = null)
29✔
36
    {
37
        if (list is null)
29✔
38
            throw new ArgumentNullException(nameof(list));
1✔
39

40
        _iterator = list.GetEnumerator();
28✔
41

42
        var ignored = new HashSet<string>(ignoreNames ?? []);
28✔
43

44
        _activeColumns = _typeAccessor
28✔
45
            .GetProperties()
28✔
46
            .Where(c => !ignored.Contains(c.Name))
216✔
47
            .ToList();
28✔
48

49
        _columnOrdinals = _activeColumns
28✔
50
            .Select((col, idx) => new { col.Column, idx })
213✔
51
            .ToDictionary(x => x.Column, x => x.idx, StringComparer.OrdinalIgnoreCase);
454✔
52
    }
28✔
53

54
    /// <summary>
55
    /// Gets a value indicating the depth of nesting for the current row. Always returns 0.
56
    /// </summary>
57
    public override int Depth => 0;
×
58

59
    /// <summary>
60
    /// Gets a value indicating whether the data reader is closed.
61
    /// </summary>
62
    public override bool IsClosed => _closed;
3✔
63

64
    /// <summary>
65
    /// Gets the number of rows affected. Always returns 0.
66
    /// </summary>
67
    public override int RecordsAffected => 0;
×
68

69
    /// <summary>
70
    /// Gets a value indicating whether the reader has rows. Always returns false.
71
    /// </summary>
72
    public override bool HasRows => false;
×
73

74
    /// <summary>
75
    /// Returns a <see cref="DataTable"/> that describes the column metadata of the data reader.
76
    /// </summary>
77
    /// <returns>A <see cref="DataTable"/> describing the column metadata.</returns>
78
    public override DataTable GetSchemaTable()
79
    {
80
        // these are the columns used by DataTable load
81
        var table = new DataTable
2✔
82
        {
2✔
83
            Columns =
2✔
84
            {
2✔
85
                {"ColumnOrdinal", typeof(int)},
2✔
86
                {"ColumnName", typeof(string)},
2✔
87
                {"DataType", typeof(Type)},
2✔
88
                {"ColumnSize", typeof(int)},
2✔
89
                {"AllowDBNull", typeof(bool)}
2✔
90
            }
2✔
91
        };
2✔
92

93
        for (int i = 0; i < _activeColumns.Count; i++)
14✔
94
        {
95
            var rowData = new object[5];
5✔
96
            rowData[0] = i;
5✔
97
            rowData[1] = _activeColumns[i].Column;
5✔
98
            rowData[2] = _activeColumns[i].MemberType.GetUnderlyingType();
5✔
99
            rowData[3] = -1;
5✔
100
            rowData[4] = _activeColumns[i].MemberType.IsNullable();
5✔
101

102
            table.Rows.Add(rowData);
5✔
103
        }
104

105
        return table;
2✔
106
    }
107

108
    /// <summary>
109
    /// Advances the data reader to the next result. Always returns false.
110
    /// </summary>
111
    /// <returns>false, as multiple result sets are not supported.</returns>
112
    public override bool NextResult() => false;
1✔
113

114
    /// <summary>
115
    /// Advances the data reader to the next record.
116
    /// </summary>
117
    /// <returns>true if there are more rows; otherwise, false.</returns>
118
    public override bool Read() => _iterator.MoveNext();
1,137✔
119

120
    /// <summary>
121
    /// Returns an enumerator that iterates through the rows of the data reader.
122
    /// </summary>
123
    /// <returns>An <see cref="IEnumerator"/> for the data reader.</returns>
124
    public override IEnumerator GetEnumerator() => new DbEnumerator(this);
×
125

126
    /// <summary>
127
    /// Closes the data reader and releases resources.
128
    /// </summary>
129
    public override void Close()
130
    {
131
        Dispose(true);
25✔
132
    }
25✔
133

134
    /// <summary>
135
    /// Releases the unmanaged resources used by the ListDataReader and optionally releases the managed resources.
136
    /// </summary>
137
    /// <param name="disposing">true to release both managed and unmanaged resources; false to release only unmanaged resources.</param>
138
    protected override void Dispose(bool disposing)
139
    {
140
        if (_disposed)
48✔
141
            return;
24✔
142

143
        if (disposing)
24✔
144
        {
145
            _iterator.Dispose();
24✔
146
            _closed = true;
24✔
147
        }
148

149
        _disposed = true;
24✔
150
        base.Dispose(disposing);
24✔
151
    }
24✔
152

153
    /// <inheritdoc/>
154
    public override string GetName(int i) => _activeColumns[i].Column;
2,543✔
155

156
    /// <inheritdoc/>
157
    public override string GetDataTypeName(int i) => _activeColumns[i].MemberType.Name;
×
158

159
    /// <inheritdoc/>
160
    public override Type GetFieldType(int i) => _activeColumns[i].MemberType;
2,497✔
161

162
    /// <inheritdoc/>
163
    public override object GetValue(int i) => _activeColumns[i].GetValue(_iterator.Current)!;
11,015✔
164

165
    /// <inheritdoc/>
166
    public override int GetValues(object[] values)
167
    {
168
        int count = Math.Min(_activeColumns.Count, values.Length);
2✔
169
        for (int i = 0; i < count; i++)
12✔
170
            values[i] = GetValue(i);
4✔
171
        return count;
2✔
172
    }
173

174
    /// <inheritdoc/>
175
    public override int GetOrdinal(string name)
176
    {
177
        if (_columnOrdinals.TryGetValue(name, out int ordinal))
101✔
178
            return ordinal;
98✔
179

180
        return -1;
3✔
181
    }
182

183
    /// <inheritdoc/>
184
    public override bool GetBoolean(int i) => (bool)GetValue(i);
×
185

186
    /// <inheritdoc/>
187
    public override byte GetByte(int i) => (byte)GetValue(i);
×
188

189
    /// <inheritdoc/>
190
    public override long GetBytes(int i, long fieldOffset, byte[]? buffer, int bufferOffset, int length)
191
    {
192
        byte[] value = (byte[])GetValue(i);
×
193

194
        int available = value.Length - (int)fieldOffset;
×
195
        if (available <= 0)
×
196
            return 0;
×
197

198
        int count = Math.Min(length, available);
×
199

NEW
200
        if (buffer != null)
×
NEW
201
            Buffer.BlockCopy(value, (int)fieldOffset, buffer, bufferOffset, count);
×
202

203
        return count;
×
204
    }
205

206
    /// <inheritdoc/>
207
    public override char GetChar(int i) => (char)GetValue(i);
×
208

209
    /// <inheritdoc/>
210
    public override long GetChars(int i, long fieldOffset, char[]? buffer, int bufferOffset, int length)
211
    {
212
        if (buffer == null)
×
213
            throw new ArgumentNullException(nameof(buffer));
×
214

215
        string value = (string)GetValue(i);
×
216

217
        int available = value.Length - (int)fieldOffset;
×
218
        if (available <= 0)
×
219
            return 0;
×
220

221
        int count = Math.Min(length, available);
×
222

223
        value.CopyTo((int)fieldOffset, buffer, bufferOffset, count);
×
224

225
        return count;
×
226
    }
227

228
    /// <inheritdoc/>
229
    public override Guid GetGuid(int i) => (Guid)GetValue(i);
×
230

231
    /// <inheritdoc/>
232
    public override short GetInt16(int i) => (short)GetValue(i);
×
233

234
    /// <inheritdoc/>
235
    public override int GetInt32(int i) => (int)GetValue(i);
4✔
236

237
    /// <inheritdoc/>
238
    public override long GetInt64(int i) => (long)GetValue(i);
×
239

240
    /// <inheritdoc/>
241
    public override float GetFloat(int i) => (float)GetValue(i);
×
242

243
    /// <inheritdoc/>
244
    public override double GetDouble(int i) => (double)GetValue(i);
×
245

246
    /// <inheritdoc/>
247
    public override string GetString(int i) => (string)GetValue(i);
4✔
248

249
    /// <inheritdoc/>
250
    public override decimal GetDecimal(int i) => (decimal)GetValue(i);
×
251

252
    /// <inheritdoc/>
253
    public override DateTime GetDateTime(int i) => (DateTime)GetValue(i);
2✔
254

255
    /// <inheritdoc/>
256
    public override bool IsDBNull(int i)
257
    {
258
        var value = GetValue(i);
1✔
259
        return value == null || value == DBNull.Value;
1!
260
    }
261

262
    /// <inheritdoc/>
263
    public override int FieldCount => _activeColumns.Count;
2,964✔
264

265
    /// <inheritdoc/>
266
    public override object this[int i] => GetValue(i);
×
267

268
    /// <inheritdoc/>
269
    public override object this[string name] => GetValue(GetOrdinal(name));
2✔
270
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc