Skip to content

System.Data.SqlClient

Benn Benson edited this page Jan 14, 2019 · 1 revision

System.Data.SqlClient and Microsoft.SqlServer.Compact

For SQL Server, only null translation is necessary but unwrapping can be added if you want to use the methods and properties that return Sql* types.

For SQL Server Compact, change SqlCommand to SqlCeCommand and SqlParameter to SqlCeParameter.

public static class SqlClientExtensions
{
    private static readonly ValueExtractor _extractor = ValueExtractor.Create(config =>
    {
        config.UseNullTester(obj => obj is DBNull);

        // Configure unwrapping of the Sql* types that implement INullable.
        config.UseUnwrapper(
            // Test for System.Data.SqlTypes.INullable.
            obj => obj is INullable,
            // The Sql* types all have a public 'Value' property, but it's defined by each type
            // rather than by a common base class or interface.  Use a little reflection to get it.
            // This could be optimized, of course.
            obj => ((INullable)obj).IsNull ? null : obj.GetType().GetProperty("Value").GetValue(obj));
    });

    public static T Column<T>(this SqlDataReader reader, int i)
    {
        if (reader is null)
            throw new ArgumentNullException(nameof(reader));

        return _valueExtractor.Extract<T>(reader.GetValue(i));
    }

    public static T Column<T>(this SqlDataReader reader, string name)
    {
        if (reader is null)
            throw new ArgumentNullException(nameof(reader));

        return _valueExtractor.Extract<T>(reader.GetValue(reader.GetOrdinal(name)));
    }

    public static T GetValue<T>(this SqlParameter parameter)
    {
        if (parameter is null)
            throw new ArgumentNullException(nameof(parameter));

        return _valueExtractor.Extract<T>(parameter.Value);
    }

    // These extension methods retrieve the Sql* types.  This is a rare use case so they
    // can be omitted along with the UseUnwrapper call in the configuration above.
    public static T SqlColumn<T>(this SqlDataReader reader, int i)
    {
        if (reader is null)
            throw new ArgumentNullException(nameof(reader));

        return _valueExtractor.Extract<T>(reader.GetSqlValue(i));
    }

    public static T SqlColumn<T>(this SqlDataReader reader, string name)
    {
        if (reader is null)
            throw new ArgumentNullException(nameof(reader));

        return _valueExtractor.Extract<T>(reader.GetSqlValue(reader.GetOrdinal(name)));
    }

    public static T GetSqlValue<T>(this SqlParameter parameter)
    {
        if (parameter is null)
            throw new ArgumentNullException(nameof(parameter));

        return _valueExtractor.Extract<T>(parameter.SqlValue);
    }
}

Usage

Call the Column extension method on the data reader and specify the BCL data type that corresponds to the type of the column.

int id = reader.Column<int>("ID");
string name = reader.Column<string>("Name");

Clone this wiki locally