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