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

loresoft / FluentCommand / 23256000174

18 Mar 2026 04:13PM UTC coverage: 56.658% (+0.2%) from 56.486%
23256000174

push

github

pwelter34
Add SQL Server structured parameter support

1303 of 2875 branches covered (45.32%)

Branch coverage included in aggregate %.

106 of 149 new or added lines in 8 files covered. (71.14%)

6 existing lines in 1 file now uncovered.

3969 of 6430 relevant lines covered (61.73%)

361.39 hits per line

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

88.24
/src/FluentCommand.SqlServer/SqlDataRecordAdapter.cs
1
using System.Collections;
2
using System.Collections.Concurrent;
3
using System.Data;
4

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

8
using Microsoft.Data.SqlClient.Server;
9

10
namespace FluentCommand;
11

12
/// <summary>
13
/// Adapts an <see cref="IEnumerable{T}"/> to an <see cref="IEnumerable{SqlDataRecord}"/> for use
14
/// as a SQL Server table-valued parameter. Reuses a single <see cref="SqlDataRecord"/> per row
15
/// for minimal allocation. Caches <see cref="SqlMetaData"/> per type for efficiency.
16
/// </summary>
17
/// <typeparam name="T">The type of items being adapted.</typeparam>
18
public class SqlDataRecordAdapter<T> : IEnumerable<SqlDataRecord> where T : class
19
{
20
    // ReSharper disable once StaticMemberInGenericType
21
    private static readonly ConcurrentDictionary<Type, (SqlMetaData[] MetaData, IMemberAccessor[] Columns)> _metaDataCache = new();
2✔
22

23
    private readonly IEnumerable<T> _source;
24

25
    /// <summary>
26
    /// Initializes a new instance of the <see cref="SqlDataRecordAdapter{T}"/> class.
27
    /// </summary>
28
    /// <param name="source">The source collection to adapt.</param>
29
    /// <exception cref="ArgumentNullException">Thrown if <paramref name="source"/> is null.</exception>
30
    public SqlDataRecordAdapter(IEnumerable<T> source)
3✔
31
    {
32
        _source = source ?? throw new ArgumentNullException(nameof(source));
3!
33
    }
3✔
34

35
    /// <inheritdoc/>
36
    public IEnumerator<SqlDataRecord> GetEnumerator()
37
    {
38
        var (metaData, columns) = GetCachedMetaData();
3✔
39
        var record = new SqlDataRecord(metaData);
3✔
40

41
        foreach (var item in _source)
212✔
42
        {
43
            for (int i = 0; i < columns.Length; i++)
1,422✔
44
            {
45
                var value = columns[i].GetValue(item);
608✔
46
                if (value is null)
608✔
47
                    record.SetDBNull(i);
202✔
48
                else
49
                    record.SetValue(i, value);
406✔
50
            }
51

52
            yield return record;
103✔
53
        }
54
    }
3✔
55

56
    /// <inheritdoc/>
NEW
57
    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
×
58

59
    private static (SqlMetaData[] MetaData, IMemberAccessor[] Columns) GetCachedMetaData()
60
    {
61
        return _metaDataCache.GetOrAdd(typeof(T), _ => BuildMetaData());
5✔
62
    }
63

64
    private static (SqlMetaData[] MetaData, IMemberAccessor[] Columns) BuildMetaData()
65
    {
66
        var typeAccessor = TypeAccessor.GetAccessor<T>();
2✔
67
        var properties = typeAccessor.GetProperties().ToList();
2✔
68

69
        var metaData = new SqlMetaData[properties.Count];
2✔
70
        var columns = new IMemberAccessor[properties.Count];
2✔
71

72
        for (int i = 0; i < properties.Count; i++)
18✔
73
        {
74
            var property = properties[i];
7✔
75
            columns[i] = property;
7✔
76

77
            var underlyingType = property.MemberType.GetUnderlyingType();
7✔
78
            var sqlDbType = SqlTypeMapping.DbType(underlyingType);
7✔
79

80
            metaData[i] = sqlDbType switch
7!
81
            {
7✔
82
                SqlDbType.NVarChar => new SqlMetaData(property.Column, sqlDbType, -1),
4✔
NEW
83
                SqlDbType.VarBinary => new SqlMetaData(property.Column, sqlDbType, -1),
×
NEW
84
                SqlDbType.Decimal => new SqlMetaData(property.Column, sqlDbType, 18, 6),
×
85
                _ => new SqlMetaData(property.Column, sqlDbType)
3✔
86
            };
7✔
87
        }
88

89
        return (metaData, columns);
2✔
90
    }
91
}
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