Joerg
Joerg

Reputation: 904

How to write generic method for casting DBNull value to Nullable?

I have a database with a nullable column foo_date, where Npgsql maps the sql NULL value to an instance of the C# class DBNull. In my C# aggregate I use the type DateTime? for said column. So the question is how to easily convert DBNull to a nullable type.

I want to write a utility method like, e.g.,

public static class DbUtil
{
    public static T? CastToNullable<T>(object obj)
    {
        if (DBNull.Value.Equals(obj))
           return null;
        return (T)obj;
    }
}

which I would like to use like this:

IDataRecord rec = ...
DateTime? fooDate = DbUtil.CastToNullable<DateTime>(rec["foo_date"]);

However, I get the compiler error:

Error CS0403 Cannot convert null to type parameter 'T' because it could be a non-nullable value type. Consider using 'default(T)' instead.

When I replace return null by return default(T?), the compiler is happy, but the method does not return null but the default Date, i.e., 01.01.0001.

What is the correct way to write the generic utility method above?

Upvotes: 0

Views: 473

Answers (3)

Shay Rojansky
Shay Rojansky

Reputation: 16672

It seems like you want to access columns on your NpgsqlDataReader, but to get .NET null for null columns instead of DBNull.Value. If so, the usual way is to write use an extension method as follows:

public static class DbDataReaderExtensions
{
    public static T? GetValueOrDefault<T>(this DbDataReader reader, int ordinal)
        where T : class
        => reader.IsDBNull(ordinal) ? null : reader.GetFieldValue<T>(ordinal);
}

This can be used directly on your reader:

var s = reader.GetValueOrDefault<string>(0);

Upvotes: 1

canton7
canton7

Reputation: 42225

I'm assuming you've got nullable reference types enabled, otherwise that T? would be a compiler error.

T?, where T is a generic type parameter, has multiple meanings unfortunately.

  • When T is a value type (i.e. you have a where T : struct constraint), T? means Nullable<T>.
  • When T is a reference type (i.e. you have a where T : class constraint), T? means a reference type which is allowed to be null.
  • When T is unconstrained, T? means "If T is a reference type, then this is allowed to be null; otherwise no effect".

In other words, if you have:

T? Foo<T>() => default;

If you call Foo<int>(), you get back an int, not an int?.

If however you have:

T? Foo<T>() where T : struct => default;

then Foo<int>() returns an int?.

In other words, your signature needs to be:

public static T? CastToNullable<T>(object obj) where T : struct
{
    if (DBNull.Value.Equals(obj))
       return null;
    return (T)obj;
}

Upvotes: 2

ASpirin
ASpirin

Reputation: 3651

Please check Nullable structure which is in fact long/real version of T?

In that case your method would be

public static T? CastToNullable<T>(object obj) where T: struct
{
    if (DBNull.Value.Equals(obj))
       return null;
    return new Nullable<T>((T)obj);
}

That would work only for structures, for reference types you could add

public static T CastToNullableObj<T>(object obj) where T: class
{
    if (DBNull.Value.Equals(obj))
       return null;
    return (T)obj;
}

Upvotes: 2

Related Questions