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

loresoft / FluentCommand / 23307015362

19 Mar 2026 05:01PM UTC coverage: 57.452% (+0.05%) from 57.402%
23307015362

push

github

pwelter34
Support null/empty TVP parameters

1412 of 3077 branches covered (45.89%)

Branch coverage included in aggregate %.

8 of 11 new or added lines in 2 files covered. (72.73%)

4289 of 6846 relevant lines covered (62.65%)

331.19 hits per line

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

87.72
/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. Can be <c>null</c> or empty.</param>
29
    public SqlDataRecordAdapter(IEnumerable<T>? source)
4✔
30
    {
31
        _source = source;
4✔
32
    }
4✔
33

34
    /// <inheritdoc/>
35
    public IEnumerator<SqlDataRecord> GetEnumerator()
36
    {
37
        if (_source is null)
7!
NEW
38
            yield break;
×
39

40
        var (metaData, columns) = GetCachedMetaData();
7✔
41
        var record = new SqlDataRecord(metaData);
7✔
42

43
        foreach (var item in _source)
223✔
44
        {
45
            for (int i = 0; i < columns.Length; i++)
1,454✔
46
            {
47
                var value = columns[i].GetValue(item);
621✔
48
                if (value is null)
621✔
49
                    record.SetDBNull(i);
206✔
50
                else
51
                    record.SetValue(i, value);
415✔
52
            }
53

54
            yield return record;
106✔
55
        }
56
    }
4✔
57

58
    /// <inheritdoc/>
59
    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
×
60

61
    private static (SqlMetaData[] MetaData, IMemberAccessor[] Columns) GetCachedMetaData()
62
    {
63
        // short-circuit to avoid the overhead of GetOrAdd and BuildMetaData
64
        if (_metaDataCache.TryGetValue(typeof(T), out var cached))
7✔
65
            return cached;
5✔
66

67
        return _metaDataCache.GetOrAdd(typeof(T), static _ => BuildMetaData());
4✔
68
    }
69

70
    private static (SqlMetaData[] MetaData, IMemberAccessor[] Columns) BuildMetaData()
71
    {
72
        var typeAccessor = TypeAccessor.GetAccessor<T>();
2✔
73
        var properties = typeAccessor.GetProperties().ToList();
2✔
74

75
        var metaData = new SqlMetaData[properties.Count];
2✔
76
        var columns = new IMemberAccessor[properties.Count];
2✔
77

78
        for (int i = 0; i < properties.Count; i++)
18✔
79
        {
80
            var property = properties[i];
7✔
81
            columns[i] = property;
7✔
82

83
            var underlyingType = property.MemberType.GetUnderlyingType();
7✔
84
            var sqlDbType = SqlTypeMapping.DbType(underlyingType);
7✔
85

86
            metaData[i] = sqlDbType switch
7!
87
            {
7✔
88
                SqlDbType.NVarChar => new SqlMetaData(property.Column, sqlDbType, -1),
4✔
89
                SqlDbType.VarBinary => new SqlMetaData(property.Column, sqlDbType, -1),
×
90
                SqlDbType.Decimal => new SqlMetaData(property.Column, sqlDbType, 18, 6),
×
91
                _ => new SqlMetaData(property.Column, sqlDbType)
3✔
92
            };
7✔
93
        }
94

95
        return (metaData, columns);
2✔
96
    }
97
}
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