72

At the moment, when I iterate over the DataRow instances, I do this.

foreach(DataRow row in table)
  return yield new Thingy { Name = row["hazaa"] };

Sooner of later (i.e. sooner), I'll get the table to be missing the column donkey and the poo will hit the fan. After some extensive googling (about 30 seconds) I discovered the following protection syntax.

foreach(DataRow row in table)
  if(row.Table.Columns.Contains("donkey"))
    return yield new Thingy { Name = row["hazaa"] };
  else
    return null;

Now - is this the simplest syntax?! Really? I was expecting a method that gets me the field if it exists or null otherwise. Or at least a Contains method directly on the row.

Am I missing something? I'll be mapping in many fields that way so the code will look dreadfully unreadable...

Konrad Viltersten
  • 36,151
  • 76
  • 250
  • 438

6 Answers6

131

You can create an extension method to make it cleaner:

static class DataRowExtensions
{
    public static object GetValue(this DataRow row, string column)
    {
        return row.Table.Columns.Contains(column) ? row[column] : null;
    }
}

Now call it like below:

foreach(DataRow row in table)
    return yield new Thingy { Name = row.GetValue("hazaa") };
Varun K
  • 3,593
  • 2
  • 25
  • 26
22

As your DataTable table always has the same columns ( they won`t change for any row ) you only need to check for the columnname once.

if (table.Columns.Contains("donkey"))
{
    foreach ...
}
Heslacher
  • 2,167
  • 22
  • 37
  • This is nice for one column but however, as op said there will eventually be many columns so how will you manage them if you don't have an idea which may be present and which may not? – Varun K Aug 13 '13 at 12:00
  • @Varun, I for sure wouldn't keep the fixed string. What would you do if the column `hazaa` won`t exist anymore ? – Heslacher Aug 13 '13 at 12:15
  • @Heslacher That's the whole point. I need to manage that the column will sometimes come up and sometimes not. I have no control over that but I need to managed it still. Your example forks for a single column and can be extrapolated for multiple. However, please note that the question is if there's syntax for the check other than via table as you presented. – Konrad Viltersten Aug 13 '13 at 21:40
10

I really liked the approach taken by @Varun K. So, having that as a departing point I just wanted to put my two cents, in case it helps someone else. I simply improved it making it generic instead of just using object as a return type.

static class Extensions
{
  public static T Get<T>(this DataRow self, string column)
  {
    return self.Table.Columns.Contains(column)
      ? (T)self[column]
      : default(T);
    }
  }
}
Konrad Viltersten
  • 36,151
  • 76
  • 250
  • 438
Sebastian Inones
  • 1,561
  • 1
  • 19
  • 32
  • Credit should be given where the credit is due. But your answer is so much of an improvement that I chose to re-accept it as the new correct answer. I also took the liberty of adapting it slightly to the generic nature of it by changing the names into more abstract ones. I hope it was okay. – Konrad Viltersten Feb 19 '21 at 13:54
  • Thanks for clarifying where credits should be. In addition, I appreciate a lot that you took the liberty to make the method even better. I believe that's the idea. To take ownership of the code and try to make each time one more step better. – Sebastian Inones Feb 19 '21 at 16:58
  • 2
    This may lead to misleading results if, for example, you attempt to get an integer value from a column that doesn't exist. The method will return 0, which might lead someone to believe that the column existed and the field contained a valid value of 0. – vargonian Mar 19 '21 at 19:31
8

To build on the answer by Varun K, use a generic type parameter:

public static T GetValue<T>(this DataRow row, string column)
{
    if (!row.Table.Columns.Contains(column))
        return default(T);

    object value = row[ColumnName];
    if (value == DBNull.Value)
        return default(T);
    return (T)value;
}
howcheng
  • 2,211
  • 2
  • 17
  • 24
4
foreach (DataColumn item in row.Table.Columns)
{
    switch (item.ColumnName)
    {
        case "ID":
            {
                p.ID = Convert.ToInt32(row[item.ColumnName].ToString());
            }
            break;
        case "firstName":
            {
                p.firstName = row[item.ColumnName].ToString();
            }
            break;
        case "lastName":
            {
                p.lastName = row[item.ColumnName].ToString();
            }
            break;

        default:
            break;
    };
}
Driton
  • 41
  • 1
2

Sometimes a column name might exist, but a row does not contain the data for that column; for example, after filling DataTable using ReadXML.

A simple, fast and secure solution would be to use type checking:

if(row["columnname"].GetType() != typeof(System.DBNull)){
    //DataRow contains "columname"
}else{
    //a safe scope to set default cell data
}
Luis Correa
  • 174
  • 1
  • 6
  • 1
    I find it exciting that this question **still** gets new answers, upvotes and comments. Given that it's been shy of a week 7 whole years since it's been asked (and answered) I wonder how come users get it in their searches. Since it's been asked, EF took over and together with Dapper and nHybernate eliminated its relevance, I'd assume. How did you happen to stubble upon it? (Also, +1 for a new angle to it.) – Konrad Viltersten Aug 09 '20 at 07:45