Best practice to check if DataRow contains a certain column
Asked Answered
T

6

77

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...

Totalitarianism answered 13/8, 2013 at 11:47 Comment(0)
D
15

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);
    }
  }
}
Dnieper answered 19/2, 2021 at 13:43 Comment(3)
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.Totalitarianism
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.Dnieper
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.Maxi
O
135

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") };
Ornamental answered 13/8, 2013 at 11:54 Comment(3)
This was a nice solution. Not sure why it didn't get upvoted more. +1 from me anyway.Totalitarianism
WoW! A Very neat solution. Thank you!Strick
Nice one! However, implemented in the wild it should also check that row and column are ok values.Commonly
S
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 ...
}
Surface answered 13/8, 2013 at 11:52 Comment(3)
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?Ornamental
@Varun, I for sure wouldn't keep the fixed string. What would you do if the column hazaa won`t exist anymore ?Surface
@Surface 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.Totalitarianism
D
15

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);
    }
  }
}
Dnieper answered 19/2, 2021 at 13:43 Comment(3)
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.Totalitarianism
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.Dnieper
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.Maxi
F
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;
}
Flaherty answered 12/12, 2019 at 0:11 Comment(0)
S
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;
    };
}
Sold answered 27/11, 2013 at 13:52 Comment(1)
This is actually perfect for what I need. (I don't love it, but I don't think I can do better...)Sarcocarp
S
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
}
Sophiasophie answered 8/8, 2020 at 19:23 Comment(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.)Totalitarianism

© 2022 - 2024 — McMap. All rights reserved.