4

I try to extend the DataRow object with this generic method :

public static T? Get<T>(this DataRow row, string field) where T : struct
{
  if (row.IsNull(field))
    return default(T);
  else
    return (T)row[field];
}

It's work fine when T is int, decimal, double, etc.

But when I try to use with string, I have this error :

"The type 'string' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method 'System.Nullable'"

How can I correct this ?

I know that string is not a struct but I wan't to return null if the string field is DBNull.

klashar
  • 2,519
  • 2
  • 28
  • 38
A.Baudouin
  • 2,855
  • 3
  • 24
  • 28
  • 1
    Do you have a purpose for constraining T to struct? – Wes P Feb 09 '11 at 16:47
  • the problem is with T?, you can't have String? type – Kris Ivanov Feb 09 '11 at 16:50
  • Yes cause if there is not this constraint you have a compilation error cause you can't return a nullable type (T?). I know that is the problem and I cannot use string cause it is not a struct. But if someone have an idea ? – A.Baudouin Feb 09 '11 at 16:52
  • Why not just use the Field-Method from DataSetExtensions as mentioned in one of the answers below? –  Mar 09 '12 at 11:30

7 Answers7

6

I think that this is what you want:

public static T? GetValue<T>(this DataRow row, string field) where T : struct
{
    if (row.IsNull(field))
        return new T?();
    else
        return (T?)row[field];
}

public static T GetReference<T>(this DataRow row, string field) where T : class
{
    if (row.IsNull(field))
        return default(T);
    else
        return (T)row[field];
}
davmos
  • 9,324
  • 4
  • 40
  • 43
satnhak
  • 9,407
  • 5
  • 63
  • 81
  • I think it would be wise to also check the table columns to verify the attempted field name exists. Code edit to follow. – pim Apr 04 '16 at 18:30
  • Generally, answers are not supposed to be production ready, but present the key idea needed to answer the question. – satnhak Apr 05 '16 at 01:00
  • No disrespect intended. Your answer is correct, hence my up vote. Edits are part of the community. It improves the code and makes the intended functionality more discernible. – pim Apr 05 '16 at 13:14
  • 1
    @PimBrouwers - None taken! However, how you want to handle the non-existence of a field would be application specific. You could throw an exception, or return the default value, or something else: none of which is wrong. – satnhak Apr 06 '16 at 05:11
5

string is not a struct, but a class. That is what the error message tells you. Just remove the constraint.

Maybe you want to have a look at the DataRowExtensions.

davmos
  • 9,324
  • 4
  • 40
  • 43
sloth
  • 99,095
  • 21
  • 171
  • 219
2

Unfortunately, you're not going to be able to get the Nullable return type AND support for reference types by using generics, unless you specify that you want a Nullable returned when you make the call

public static T Get<T>(this DataRow row, string field)
{
    if (row.IsNull(field))
        return default(T);
    else
        return (T)row[field];
}

and when you call

var id = dr.Get<int?>("user_id");

I didn't test this, just tossed it up here. Give it a shot.

EDIT:

Alternatively, if you really wanted to convert the value types into nullables and still be able to support reference types something like this might work

public static object GetDr<T>(this DataRow row, string field)
{
    // might want to throw some type checking to make 
    // sure row[field] is the same type as T
    if (typeof(T).IsValueType)
    {
        Type nullableType = typeof(Nullable<>).MakeGenericType(typeof(T));
        if (row.IsNull(field))
            return Activator.CreateInstance(nullableType);
        else
            return Activator.CreateInstance(nullableType, new[] { row[field] });
    }
    else
    {
        return row[field];
    }
}

However, it'd require a cast on every usage

var id = dr.Get<string>("username") as string;
var id = (int?)dr.Get<int>("user_id");

This is, however, not going to be nearly as efficient as just accepting the nullable type in the generic type parameters.

davmos
  • 9,324
  • 4
  • 40
  • 43
Wes P
  • 9,622
  • 14
  • 41
  • 48
  • The DataRow method Field do the same thing. But this method is very dangerous, imagine that you call Get on a DBNull field, It will return 0 and could have a big impact ... – A.Baudouin Feb 09 '11 at 17:12
  • I could throw Exception but I find It's really not the same thing that I want to do at the beginning ... – A.Baudouin Feb 09 '11 at 17:19
  • 1
    I understand your concern, though, it sounds like Field does what you need for reference types, then, does it not? Returns null if DBNull is found in the database? Why not just use Field for reference types and your Get for value types, or better yet, name it GetNullable. – Wes P Feb 09 '11 at 17:20
  • @Anatole: In that case my version might be preferable. If the column is null and you called `Get` then it throws an exception; if you called `Get` then it returns null. – LukeH Feb 09 '11 at 17:22
  • @LukeH : I understand your method, but it does the same thing that Field : if you call Field on a DBNull field you will have a InvalidCastException and you must use int?. – A.Baudouin Feb 09 '11 at 17:38
  • @Anatole: And why would that be undesirable behaviour? If you know that the field can't possibly be null then call `Get`; if it could be null then call `Get`. If a field turns out to be null when I "know" it can't possibly be null then I'd want to see an exception, not some kind of subtle, silent failure. – LukeH Feb 09 '11 at 17:42
  • @Wes P : in fact I want to do the same thing that Field but force Nullable type for T. If it's not possible, it's not interesting cause GetNullable is quite Field for exemple ... – A.Baudouin Feb 09 '11 at 17:45
  • Well, like I mentioned in the answer, it's impossible for you to have the Generic Type saftey, return Nullable and have this work for reference types. However, sacrificing any one of those will make the other two work, e.g. type safety. You could go ahead and accept the generic type parameter, and return type object doing some reflection tricks to create a nullable for value types. See my updated post. – Wes P Feb 09 '11 at 19:32
2
ds.Tables[7].Rows.OfType<DataRow>().ToList().ForEach(f => tempList.Add(new MyEntity
{
  Id = int.Parse(f.ItemArray[0].ToString()),
  Title = f.ItemArray[1].ToString()
 }));
Surjit Samra
  • 4,614
  • 1
  • 26
  • 36
Bekir Topuz
  • 221
  • 2
  • 3
  • Although it doesn't extend the DataRow object, it's a very nifty routine. There should be a snippet library somewhere at stack... – netfed Feb 14 '17 at 18:21
1

As Wes points out, your issue is the constraint to struct. I'd expect that extension method to work without constraints...

Ah, I see now, you're returning T? Well, I am not sure but can you define two variants of the method one constraining to struct, the other to class and returning T ?

flq
  • 22,247
  • 8
  • 55
  • 77
1

You have an explicit condition that prevents this from working with string:

 where T : struct

System.String is a class, not a struct. If your goal is to handle value types and string, I would make a separate method for string, and leave this alone for your other types.

Reed Copsey
  • 554,122
  • 78
  • 1,158
  • 1,373
1

How about something like this? Not quite the same as your example, but pretty usable for reference-types, nullable value-types and non-nullable value-types alike:

int v = row.Get<int>("vvv");               // throws if column is null
int? w = row.Get<int?>("www");             // set to null if column is null
int x = row.Get<int?>("xxx") ?? -1;        // set to -1 if column is null
string y = row.Get<string>("yyy");         // set to null if column is null
string z = row.Get<string>("zzz") ?? ""    // set to "" if column is null

// ...

public static T Get<T>(this DataRow source, string columnName)
{
    if (source == null)
        throw new ArgumentNullException("source");

    if (columnName == null)
        throw new ArgumentNullException("columnName");

    if (columnName.Length < 1)
        throw new ArgumentException("Name cannot be empty.", "columnName");

    if (source.IsNull(columnName))
    {
        T defaultValue = default(T);
        if (defaultValue == null)
            return defaultValue;
    }

    // throws if the column is null and T is a non-nullable value type
    return (T)source[columnName];
}
LukeH
  • 263,068
  • 57
  • 365
  • 409