I was asked to update a business rule to allow a specific column to be added. Because our datalayer uses only DataSets as an interface to the database that is what we work with in the business layer.
Below I have created a couple of DataTable and DataRow extensions to use for this businessrule and I would like your opinion on things I might have missed. This is the method I use to search for changes
private static bool hasColumnChanged(StringComparison stringComparison, bool ignoreWhitespace, DataRow row, DataColumn col)
{
bool isEqual = true;
if (row[col, DataRowVersion.Original] != DBNull.Value && row[col, DataRowVersion.Current] != DBNull.Value)
{
if (ignoreWhitespace)
isEqual = row[col, DataRowVersion.Original].ToString().Trim().Equals(row[col, DataRowVersion.Current].ToString().Trim(), stringComparison);
else
isEqual = row[col, DataRowVersion.Original].ToString().Equals(row[col, DataRowVersion.Current].ToString(), stringComparison);
}
else
isEqual = row[col, DataRowVersion.Original].Equals(row[col, DataRowVersion.Current]);
return !isEqual;
}
And these are simply the extensions using the code:
public static List<DataColumn> GetChangedColumns(this DataTable table)
{
return table.GetChangedColumns(StringComparison.InvariantCultureIgnoreCase, false);
}
public static List<DataColumn> GetChangedColumns(this DataTable table, bool ignoreWhitespace)
{
return table.GetChangedColumns(StringComparison.InvariantCultureIgnoreCase, ignoreWhitespace);
}
public static List<DataColumn> GetChangedColumns(this DataTable table, StringComparison stringComparison, bool ignoreWhitespace)
{
if (table == null) throw new ArgumentNullException("table");
List<DataColumn> columnsChanged = new List<DataColumn>();
foreach (DataRow row in table.GetChanges().Rows)
{
foreach (DataColumn col in row.Table.Columns)
{
if (!columnsChanged.Contains(col) && hasColumnChanged(stringComparison, ignoreWhitespace, row, col))
columnsChanged.Add(col);
}
}
return columnsChanged;
}
public static List<DataColumn> GetChangedColumns(this DataRow row)
{
return row.GetChangedColumns(StringComparison.InvariantCultureIgnoreCase, false);
}
public static List<DataColumn> GetChangedColumns(this DataRow row, bool ignoreWhitespace)
{
return row.GetChangedColumns(StringComparison.InvariantCultureIgnoreCase, ignoreWhitespace);
}
public static List<DataColumn> GetChangedColumns(this DataRow row, StringComparison stringComparison, bool ignoreWhitespace)
{
if (row == null) throw new ArgumentNullException("row");
List<DataColumn> columnsChanged = new List<DataColumn>();
foreach (DataColumn col in row.Table.Columns)
{
if (!columnsChanged.Contains(col) && hasColumnChanged(stringComparison, ignoreWhitespace, row, col))
columnsChanged.Add(col);
}
return columnsChanged;
}
To test the above code I use this simple unit test:
[TestMethod]
public void DataTableAndDataRowGetChangedColumns()
{
DataSet ds = GetDummyDataSet();
ds.Tables[0].Rows[0][3] = DateTime.Now;
ds.Tables[0].Rows[0][2] = ds.Tables[0].Rows[1][2].ToString() + " ";
ds.Tables[0].Rows[0][1] = DBNull.Value;
List<DataColumn> changesForRow = ds.Tables[0].Rows[0].GetChangedColumns(true);
List<DataColumn> changesForTable = ds.Tables[0].GetChangedColumns(true);
// For now we just verify if the amount of changes is the same (ez way out)
Assert.IsTrue(changesForRow.Count.Equals(changesForTable.Count));
}
In the business rule I have implemented this piece of code which verifies if there are other datacolumns that have changed:
List<DataColumn> columnsChanged = dsChanges.Tables[0].GetChangedColumns(true);
if(columnsChanged.Any(c=>!c.ColumnName.Equals("DateUntill", StringComparison.InvariantCultureIgnoreCase)))
throw new BusinessException("This premium can not be changed, only DateUntill can still change");
Thank you very much for taking the time to read through my code!