diff --git a/CHANGELOG.md b/CHANGELOG.md
index 12bd30e..f0c6051 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,14 @@
+## 4.6.0 (2019-06-03)
+**Summary** - Support replacing `DBNull` with `null` when calling `GetValues` on an `IDataRecord`.
+
+### New Features
+When working with ADO.NET classes, you often have to deal with `null`s by checking for instances of `DBNull`. Most of the time, this involves calling `IDataRecord.IsDBNull()` or comparing values to `DBNull.Value`. The `DataRecordExtensions` class provides a lot of convenience methods, like `GetNullableString`, that save you from having to distinguish between `null` and `DBNull` all the time. The `FlatFileDataReader` and `DataTableExtensions` classes also provide a lot of features/options surrounding `null` handling. One area where this was overlooked was in the `DataRecordExtensions.GetValues` method.
+
+It was decided that `object[] GetValues()` *(extension)* and `int GetValues(object[])` *(built-in)* should have similar semantics, so instead an optional parameter was added: `replaceDBNulls` that allows specifying whether `DBNull`s should be replaced in the destination array or not (defaults to keeping `DBNull`s). An additional extension: `int GetValues(object[], bool)` was added to provide similar semantics for the built-in method.
+
+### Other
+There were also some tests failing due to culture-specific date formatting. Those unit tests were updated to format dates in a culture-insensitive way.
+
## 4.5.0 (2019-05-29)
**Summary** - Disable wrapping delimited values in quotes.
diff --git a/FlatFiles.Test/DataRecordExtensionsTester.cs b/FlatFiles.Test/DataRecordExtensionsTester.cs
index 2c2442e..a63d7b5 100644
--- a/FlatFiles.Test/DataRecordExtensionsTester.cs
+++ b/FlatFiles.Test/DataRecordExtensionsTester.cs
@@ -69,17 +69,6 @@ public void TestNullableExtensions_AllNotNull()
Assert.IsFalse(dataReader.Read(), "Too many records were read.");
}
-
- [TestMethod]
- public void TestGetValues_DbNullToNull()
- {
- var record = new MockDataRecord();
- var values = record.GetValues();
-
- Assert.AreEqual(6, values.Length);
- Assert.AreEqual(null, values[2]);
- }
-
private static SeparatedValueSchema GetSchema()
{
var mapper = SeparatedValueTypeMapper.Define(() => new NullableValues());
@@ -124,12 +113,60 @@ public class NullableValues
public DayOfWeek? EnumValue { get; set; }
}
- private class MockDataRecord : IDataRecord
+ [TestMethod]
+ public void TestGetValues_DBNullToNull()
+ {
+ var record = new FakeDataRecord(new object[]
+ {
+ 0, DateTime.UnixEpoch, DBNull.Value, 3.14159, 3.14159m, "A String"
+ });
+ var values = record.GetValues(replaceDBNulls: true);
+ Assert.AreEqual(6, values.Length);
+ Assert.AreEqual(null, values[2]);
+ }
+
+ [TestMethod]
+ public void TestGetValues_LargerArray()
{
- private readonly object[] _values =
+ var record = new FakeDataRecord(new object[]
{
0, DateTime.UnixEpoch, DBNull.Value, 3.14159, 3.14159m, "A String"
+ });
+ var values = new object[10];
+ int length = record.GetValues(values);
+ Assert.AreEqual(6, length);
+ var expected = new object[]
+ {
+ 0, DateTime.UnixEpoch, DBNull.Value, 3.14159, 3.14159m, "A String", null, null, null, null
};
+ CollectionAssert.AreEqual(expected, values);
+ }
+
+ [TestMethod]
+ public void TestGetValues_LargerArray_DBNullToNull()
+ {
+ var record = new FakeDataRecord(new object[]
+ {
+ 0, DateTime.UnixEpoch, DBNull.Value, 3.14159, 3.14159m, "A String"
+ });
+ var values = new object[10];
+ int length = record.GetValues(values, replaceDBNulls: true);
+ Assert.AreEqual(6, length);
+ var expected = new object[]
+ {
+ 0, DateTime.UnixEpoch, null, 3.14159, 3.14159m, "A String", null, null, null, null
+ };
+ CollectionAssert.AreEqual(expected, values);
+ }
+
+ private class FakeDataRecord : IDataRecord
+ {
+ public FakeDataRecord(object[] values)
+ {
+ Values = values;
+ }
+
+ public object[] Values { get; }
public bool GetBoolean(int i)
{
@@ -233,21 +270,14 @@ public object GetValue(int i)
public int GetValues(object[] values)
{
- int i;
- for (i = 0; i < values.Length && i < _values.Length; i++)
- {
- values[i] = _values[i];
- }
-
- return i;
+ int length = Math.Min(values.Length, Values.Length);
+ Array.Copy(Values, values, length);
+ return length;
}
- public bool IsDBNull(int i)
- {
- throw new NotImplementedException();
- }
+ public bool IsDBNull(int i) => Values[i] == null || Values[i] == DBNull.Value;
- public int FieldCount => _values.Length;
+ public int FieldCount => Values.Length;
public object this[int i]
{
diff --git a/FlatFiles.Test/DateTimeColumnTester.cs b/FlatFiles.Test/DateTimeColumnTester.cs
index e2e72b8..9506dec 100644
--- a/FlatFiles.Test/DateTimeColumnTester.cs
+++ b/FlatFiles.Test/DateTimeColumnTester.cs
@@ -89,7 +89,7 @@ public void TestParse_FormatProvider_FormatString_ParsesExactly()
InputFormat = "d",
FormatProvider = CultureInfo.InvariantCulture
};
- DateTime actual = (DateTime)column.Parse(null, "1/19/2013");
+ DateTime actual = (DateTime)column.Parse(null, "01/19/2013");
DateTime expected = new DateTime(2013, 1, 19);
Assert.AreEqual(expected, actual);
}
diff --git a/FlatFiles/DataRecordExtensions.cs b/FlatFiles/DataRecordExtensions.cs
index 7e64082..738a2b7 100644
--- a/FlatFiles/DataRecordExtensions.cs
+++ b/FlatFiles/DataRecordExtensions.cs
@@ -1018,19 +1018,36 @@ private static object ToEnum(Type type, object value)
/// Creates an array of objects with the column values of the current record.
///
/// The IDataRecord to get the values for.
+ /// Indicates whether DBNull instances should be replaced with nulls.
/// An array of objects with the column values of the current record.
- public static object[] GetValues(this IDataRecord record)
+ public static object[] GetValues(this IDataRecord record, bool replaceDBNulls = false)
{
var values = new object[record.FieldCount];
- record.GetValues(values);
- for (int i = 0; i < values.Length; i++)
+ record.GetValues(values, replaceDBNulls);
+ return values;
+ }
+
+ ///
+ /// Populates an array of objects with the column values of the current record.
+ ///
+ /// The IDataRecord to get the values for.
+ /// The array to store the values in.
+ /// Indicates whether DBNull instances should be replaced with nulls.
+ /// The number of objects copied to the array.
+ public static int GetValues(this IDataRecord record, object[] values, bool replaceDBNulls)
+ {
+ int result = record.GetValues(values);
+ if (replaceDBNulls)
{
- if (values[i] == DBNull.Value)
+ for (int index = 0; index != result; ++index)
{
- values[i] = null;
+ if (values[index] == DBNull.Value)
+ {
+ values[index] = null;
+ }
}
}
- return values;
+ return result;
}
#endregion
diff --git a/FlatFiles/FlatFiles.csproj b/FlatFiles/FlatFiles.csproj
index c7ea2de..5a1a88a 100644
--- a/FlatFiles/FlatFiles.csproj
+++ b/FlatFiles/FlatFiles.csproj
@@ -10,10 +10,10 @@
https://github.com/jehugaleahsa/FlatFiles.git
git
csv;comma;tab;separated;value;delimited;flat;file;fixed;width;fixed-width;length;fixed-length;parser;parsing;parse
- Allow disabling quotes in delimited files.
+ Support replacing DBNull with null when reading from IDataRecord.
true
FlatFiles.snk
- 4.5.0
+ 4.6.0
@@ -27,8 +27,8 @@
7.3
https://raw.githubusercontent.com/jehugaleahsa/FlatFiles/master/icon.png
- 4.5.0.0
- 4.5.0.0
+ 4.6.0.0
+ 4.6.0.0