Best practice to check if DataRow contains a certain column - c#

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

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") };

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

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);
}
}
}

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;
}

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;
};
}

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
}

Related

comparing datarow value with a string in if

I have an application which stores a user selected value to the value in my dataset filled datatable. I need to set another column in the table based on this comparison. But the comparison is not working. It always returns false, not entering in the if condition.
foreach (DataRow dr in dsQuestions.Tables[0].Rows)
{
if (dr["Data"] == indicater[0])
{
dr["IsSelected"] = true;
}
}
indiactor[0] is a string array and dr["data"] is also of type string but it shows a warning that it needs to a string type.
The DataRow indexer returns the field at that index as object not as string.
I would recommend to use the strongly typed Field-extension method which also supports nullables:
if (dr.Field<String>("Data") == indicater[0]){}
... and the SetField method that also support nullable types:
dr.SetField("IsSelected", true);
Update if indicater[0] is really a string[] (not a single string), how do you want to compare a string with a string[]? If you for example want to check if the array contains this data:
if (indicater[0].Contains(dr.Field<String>("Data"))){}
That would also explain why it never enters the if: because == only compares strings by equality, other types which don't have overridden the ==-operator will compare only the reference. A string is never the same reference as a string[]. But you don't get a compile time error because you can compare an object with everything else.
First of all string can't compare using == you should use equals method:
foreach (DataRow dr in dsQuestions.Tables[0].Rows)
{
if (dr["Data"].tostring().Equals(indicater[0]))
{
dr["IsSelected"] = true;
}
May be useful for you
DataRow[] result = table.Select("Id = 1");
foreach (DataRow row in result)
{
if (row[0].Equals(indicater[0]))
{
//IsSelected
row[1]=true;
Console.WriteLine("{0}", row[0]);
}
}
Try this:
foreach (DataRow dr in dsQuestions.Tables[0].Rows)
{
if (dr["Data"].ToString() == indicater[0].ToString())
{
Convert.ToBoolean(dr["IsSelected"].ToString()) = true;
}
}

Checking if a table contains a Row C#

I'm trying to figure out if my data table contains a certain row. So I used the .Contains method, but when I tried to run my program I was met with a MissingPrimaryKeyException which I can't figure out.
DataTable table = dataQuery.executeQuery();
foreach(DataRow row in table.Rows)
{
if(table.Rows.Contains("certainRow"))
{
blah blah blah
...
I am only getting the exception the 2nd time I call table.Rows which is what is making no sense to me. If my table didn't have a primary key, wouldn't it have given me an error in the foreach call?
EDIT: I just realized that Contains did something else than I thought it did. Matthijs... yes that is kind of what I want. I need to know if "certainRow" is actually a row that exists otherwise I cannot manipulate the data in that row.
Unfortunately I am new to C# and haven't programmed in anything in over 5years so I cannot figure out if there is a method that does what I want. Or if I need to create something myself.
EDIT#2:
I figured out a solution to my problem using the DataTableReader.
private bool doesExist(string rowName, DataTable table)
{
bool value = false;
DataTableReader reader = new DataTableReader(table);
while (reader.Read())
{
for (int i = 0; i < reader.FieldCount; i++)
{
if (reader.GetValue(i) == rowName)
{
value = true;
}
}
}
return value;
}
You should be able to use the DataTableReader, more information can be found here.
An example, would be:
private static DataTable GetCustomer()
{
DataTable table = new DataTable();
DataColumn id = table.Columns.Add(#"Id", typeof(int));
table.Columns.Add(#"Name", typeof(string));
table.PrimaryKey = new DataColumn[] { id };
table.Rows.Add(new object[] { 1, #"John" });
return table;
}
The above code will build a table, as you see you define your Primary Key and add the Content. That is how you would build the table. Now to read, you would:
using(DataTableReader reader = new DataTableReader(new DataTable[] { customer }))
{
do
{
if(reader.HasRows)
{
// Do Something
}
} while (reader.NextResult());
}
As you can see it validates the row exist, then performs a task. Very simple example, hope it helps.
The .Contains will return a boolean for a true or false value.
In regards to your comment, you can call reader[#"ColumnName"] and it should attempt to read it without an issue. So if:
if(reader[#"ColumnName"] != DBNull.Value)
{
// Do Something.
}
You could just write:
bool contains = table.Rows.Cast<DataRow>().SelectMany(r => r.ItemArray).Contains(value);

List Sorting and pattern-matching

I'm trying to sort a list of telegramms to a List of Slaves.
If the PrimeAddress and the SecondaryAddress match, the telegrams belongs to the Slave.
The devices are stored in a Datatable.
I want to check if the deivce already contains the telegramm.
My first attempt looks something like this:
public static DataTable mdlform_NewMBUStele(int LoggerID, List<MbusTelegram> mList, DataTable _deviceDataTable)
{
//TODO Das ist total dirty und gar nicht clean hier...
foreach (DataRow dRow in _deviceDataTable.Rows)
{
if (dRow.ItemArray[3] is Slave)
{
foreach (MbusTelegram mb in mList)
{
int primeID = (int)dRow.ItemArray[1];
if (primeID == LoggerID)
{
Slave slv = (Slave)dRow.ItemArray[3];
foreach (MbusTelegram mbus in mList)
{
if (slv.PrimeAddress == mbus.Header.PrimeAddress && slv.SecondaryAdd == mbus.FixedDataHeader.SecondaryAddress)
{
if (slv.ListOfTelegramms == null)
{
slv.ListOfTelegramms = new List<MbusTelegram>();
}
if (!slv.ListOfTelegramms.Contains(mbus))
{
slv.ListOfTelegramms.Add(mbus);
//TODO Check if the slave already contains the telegramm, if so don't add it..
}
}
}
}
}
}
}
return _deviceDataTable;
}
Structure of the datatable:
private void IniDataTable()
{
_deviceDataTable = new DataTable("Table");
_deviceDataTable.Columns.Add("ID", typeof(int));
_deviceDataTable.Columns.Add("IDParent", typeof(int));
_deviceDataTable.Columns.Add("Name", typeof(string));
_deviceDataTable.Columns.Add("Object", typeof(object));
_deviceDataTable.Rows.Add(new object[] { 0, DBNull.Value, "Addressen", null });
//GenerateDummyDataTable();
IniDeviceTreeView();
}
This code doesn't work very well and it doesn't check if the device already contains the telegramm. Any better ideas?
There's a few things you can do to optimize your code. Firstly take all the things that doesn't change out of the inner loop. E.g. every type you use ItemArray. They are not changing for each iteration of the inner loop but with each iteration of the outer loop secondly you seem to be iterating twice over the same collection never using the variable of the outer loop (mb) so you can eleminate that loop entirely. I've done both in the code below. I've also converted the inner most loop to LINQ but that's mostly because I find it easier to read. I'm not sure the Distinct solves the TODO. I'm not sure I get the TODO at all. It might and it might not solve it you'll need to verify that
public static DataTable mdlform_NewMBUStele(int LoggerID, List<MbusTelegram> mList, DataTable _deviceDataTable){
//TODO Das ist total dirty und gar nicht clean hier...
foreach (DataRow dRow in _deviceDataTable.Rows.Cast<DataRow>().Where(d=>d.ItemArray[3] is Slave)){
var primeID = (int) dRow.ItemArray[1];
var slv = (Slave) dRow.ItemArray[3];
if (slv.ListOfTelegramms == null){
slv.ListOfTelegramms = new List<MbusTelegram>();
}
var list = slv.ListOfTelegramms;
if (primeID == LoggerID){
var items = from m in mList
where
slv.PrimeAddress == m.Header.PrimeAddress &&
slv.SecondaryAdd == m.FixedDataHeader.SecondaryAddress && !list.Contains(m, MbusTelegramEqualityComparer.Default)
select m;
list.AddRange(items.Distinct(MbusTelegramEqualityComparer.Default));
}
}
return _deviceDataTable;
}
if you also want to LINQify it for readability you could do as below (this might perform slightly worse):
public static DataTable mdlform_NewMBUStele2(int LoggerID, List<MbusTelegram> mList, DataTable _deviceDataTable){
var pairs = from dRow in _deviceDataTable.Rows.Cast<DataRow>()
where dRow.ItemArray[3] is Slave
let primeID = (int) dRow.ItemArray[1]
let slv = (Slave) dRow.ItemArray[3]
let list = slv.ListOfTelegramms
where primeID == LoggerID
select
new{
list,
items = (from m in mList
where
slv.PrimeAddress == m.Header.PrimeAddress &&
slv.SecondaryAdd == m.FixedDataHeader.SecondaryAddress &&
!list.Contains(m, MbusTelegramEqualityComparer.Default)
select m).Distinct(MbusTelegramEqualityComparer.Default)
};
foreach (var pair in pairs){
pair.list.AddRange(pair.items);
}
return _deviceDataTable;
}
the latter exampl requires that ListOfTelegrams is initialized to an empty list instead of null
EDIT
to have value comparison use a IEqualityComparer. The below uses the timestamps only for equality. The code is updated with the usage. If the ListOfTelegrams can contain duplicates you'll need to handle this in the call to distinct as well otherwise you can leave the call to Distinct out.
public class MbusTelegramEqualityComparer : IEqualityComparer<MbusTelegram>{
public static readonly MbusTelegramEqualityComparer Default = new MbusTelegramEqualityComparer();
public bool Equals(MbusTelegram x, MbusTelegram y){
return x.TimeStamp == y.TimeStamp;
}
public int GetHashCode(MbusTelegram obj){
return obj.TimeStamp.GetHashCode();
}
}

C# DataRow Empty-check

I got this:
DataTable dtEntity = CreateDataTable();
drEntity = dtEntity.NewRow();
Then I add data to the row (or not).
Lots of code, really don't know if there's anything inside the row.
Depends on the input (i am importing from some files).
I'd like to do something like:
if (drEntity`s EVERY CELL IS NOT EMPTY)
{
dtEntity.Rows.Add(drEntity);
}
else
{
//don't add, will create a new one (drEntity = dtEntity.NewRow();)
}
Is there some nice way to check if the DataRow's every cell is empty?
Or I should foreach, and check them one by one?
A simple method along the lines of:
bool AreAllColumnsEmpty(DataRow dr)
{
if (dr == null)
{
return true;
}
else
{
foreach(var value in dr.ItemArray)
{
if (value != null)
{
return false;
}
}
return true;
}
}
Should give you what you're after, and to make it "nice" (as there's nothing as far as I'm aware, in the Framework), you could wrap it up as an extension method, and then your resultant code would be:
if (datarow.AreAllColumnsEmpty())
{
}
else
{
}
I created an extension method (gosh I wish Java had these) called IsEmpty as follows:
public static bool IsEmpty(this DataRow row)
{
return row == null || row.ItemArray.All(i => i is DBNull);
}
The other answers here are correct. I just felt mine lent brevity in its succinct use of Linq to Objects. BTW, this is really useful in conjunction with Excel parsing since users may tack on a row down the page (thousands of lines) with no regard to how that affects parsing the data.
In the same class, I put any other helpers I found useful, like parsers so that if the field contains text that you know should be a number, you can parse it fluently. Minor pro tip for anyone new to the idea. (Anyone at SO, really? Nah!)
With that in mind, here is an enhanced version:
public static bool IsEmpty(this DataRow row)
{
return row == null || row.ItemArray.All(i => i.IsNullEquivalent());
}
public static bool IsNullEquivalent(this object value)
{
return value == null
|| value is DBNull
|| string.IsNullOrWhiteSpace(value.ToString());
}
Now you have another useful helper, IsNullEquivalent which can be used in this context and any other, too. You could extend this to include things like "n/a" or "TBD" if you know that your data has placeholders like that.
I prefer approach of Tommy Carlier, but with a little change.
foreach (DataColumn column in row.Table.Columns)
if (!row.IsNull(column))
return false;
return true;
I suppose this approach looks more simple and cleaner.
public static bool AreAllCellsEmpty(DataRow row)
{
if (row == null) throw new ArgumentNullException("row");
for (int i = row.Table.Columns.Count - 1; i >= 0; i--)
if (!row.IsNull(i))
return false;
return true;
}
I know this has been answered already and it's an old question, but here's an extension method to do the same:
public static class DataExtensions
{
public static bool AreAllCellsEmpty(this DataRow row)
{
var itemArray = row.ItemArray;
if(itemArray==null)
return true;
return itemArray.All(x => string.IsNullOrWhiteSpace(x.ToString()));
}
}
And you use it like so:
if (dr.AreAllCellsEmpty())
// etc
You could use this:
if(drEntity.ItemArray.Where(c => IsNotEmpty(c)).ToArray().Length == 0)
{
// Row is empty
}
IsNotEmpty(cell) would be your own implementation, checking whether the data is null or empty, based on what type of data is in the cell. If it's a simple string, it could end up looking something like this:
if(drEntity.ItemArray.Where(c => c != null && !c.Equals("")).ToArray().Length == 0)
{
// Row is empty
}
else
{
// Row is not empty
}
Still, it essentially checks each cell for emptiness, and lets you know whether all cells in the row are empty.
DataTable.NewRow will initialize each field to:
the default value for each DataColumn (DataColumn.DefaultValue)
except for auto-increment columns (DataColumn.AutoIncrement == true), which will be initialized to the next auto-increment value.
and expression columns (DataColumn.Expression.Length > 0) are also a special case; the default value will depend on the default values of columns on which the expression is calculated.
So you should probably be checking something like:
bool isDirty = false;
for (int i=0; i<table.Columns.Count; i++)
{
if (table.Columns[i].Expression.Length > 0) continue;
if (table.Columns[i].AutoIncrement) continue;
if (row[i] != table.Columns[i].DefaultValue) isDirty = true;
}
I'll leave the LINQ version as an exercise :)
AFAIK, there is no method that does this in the framework. Even if there was support for something like this in the framework, it would essentially be doing the same thing. And that would be looking at each cell in the DataRow to see if it is empty.
I did it like this:
var listOfRows = new List<DataRow>();
foreach (var row in resultTable.Rows.Cast<DataRow>())
{
var isEmpty = row.ItemArray.All(x => x == null || (x!= null && string.IsNullOrWhiteSpace(x.ToString())));
if (!isEmpty)
{
listOfRows.Add(row);
}
}
Maybe a better solution would be to add an extra column that is automatically set to 1 on each row. As soon as there is an element that is not null change it to a 0.
then
If(drEntitity.rows[i].coulmn[8] = 1)
{
dtEntity.Rows.Add(drEntity);
}
else
{
//don't add, will create a new one (drEntity = dtEntity.NewRow();)
}
To delete null and also empty entries Try this
foreach (var column in drEntitity.Columns.Cast<DataColumn>().ToArray())
{
if (drEntitity.AsEnumerable().All(dr => dr.IsNull(column) | string.IsNullOrEmpty( dr[column].ToString())))
drEntitity.Columns.Remove(column);
}

Refactoring my DataTable search code

I am using C# 2.0, and I can't help but think this isn't the most efficient way to search a collection (in this case a DataTable) for a value:
bool found = false;
foreach (DataRow row in data.Rows)
{
if (id == row["rowID"])
{
found = true;
break;
}
}
if (!found)
{
//Do stuff here
}
Can anyone think of a "cleaner" way to do this?
Look at the datatable's Select() method:
http://msdn.microsoft.com/en-us/library/b5c0xc84(VS.80).aspx
DataRow[] rows = data.Select("rowID=" + id.ToString());
if (rows.Length > 0)
{
//Do stuff here
}
This is a linear search and it is the slowest actual search there is.
One alternative, if you want to continue using a DataTable, is to define a primary key and use the Find() method:
myTable.PrimaryKey = new DataColumn[] { new DataColumn("rowID") };
DataRow row = myTable.Find(id);

Categories