From 7d2d7886e2c32d450e583aeb2ee1b8c3bf73fc0d Mon Sep 17 00:00:00 2001 From: Ally Dewji Date: Mon, 2 Mar 2026 22:54:25 -0500 Subject: [PATCH 1/3] Added jonreis Exception thrown with duplicated colum names --- code/CsvReader.UnitTests/CsvReaderTest.cs | 34 +++++++++ code/CsvReader/CsvReader.cs | 27 ++++++- .../Events/DuplicateHeaderEventArgs.cs | 71 +++++++++++++++++++ .../Exceptions/DuplicateHeaderException.cs | 71 +++++++++++++++++++ 4 files changed, 201 insertions(+), 2 deletions(-) create mode 100644 code/CsvReader/Events/DuplicateHeaderEventArgs.cs create mode 100644 code/CsvReader/Exceptions/DuplicateHeaderException.cs diff --git a/code/CsvReader.UnitTests/CsvReaderTest.cs b/code/CsvReader.UnitTests/CsvReaderTest.cs index 6c55ea4..5d7ca5a 100644 --- a/code/CsvReader.UnitTests/CsvReaderTest.cs +++ b/code/CsvReader.UnitTests/CsvReaderTest.cs @@ -1319,6 +1319,40 @@ public void HasHeader_NullHeader() } } + [Test] + public void HasHeader_DuplicateHeader() + { + try + { + using (CsvReader csvReader = new CsvReader(new StringReader("Header,Header\r\nValue1,Value2"), true)) + { + csvReader.ReadNextRecord(); + } + Assert.Fail("Expected DuplicateHeaderException"); + } + catch (DuplicateHeaderException ex) + { + Assert.AreEqual("Header", ex.HeaderName); + Assert.AreEqual(1, ex.ColumnIndex); + } + catch (Exception) + { + Assert.Fail("Expected DuplicateHeaderException"); + } + } + + [Test] + public void HasHeader_DuplicateHeader_Override() + { + using (CsvReader csvReader = new CsvReader(new StringReader("Header,Header\r\nValue1,Value2"), true)) + { + csvReader.DuplicateHeaderEncountered += (s, e) => e.HeaderName = $"{e.HeaderName}_{e.Index}"; + csvReader.ReadNextRecord(); + Assert.AreEqual(csvReader.Columns[0].Name, "Header"); + Assert.AreEqual(csvReader.Columns[1].Name, "Header_1"); + } + } + [Test] public void HasHeader_HeaderExists() { diff --git a/code/CsvReader/CsvReader.cs b/code/CsvReader/CsvReader.cs index 2cbcff5..47893b2 100644 --- a/code/CsvReader/CsvReader.cs +++ b/code/CsvReader/CsvReader.cs @@ -417,6 +417,13 @@ protected virtual void OnParseError(ParseErrorEventArgs e) handler(this, e); } + /// + /// Occurs when HasHeaders is true and a duplicate Column Header Name is encountered. + /// Setting the HeaderName property on this column will prevent the library from throwing a duplicate key exception + /// + public event EventHandler DuplicateHeaderEncountered; + + /// /// Gets the comment character indicating that a line is commented out. /// @@ -1745,10 +1752,24 @@ protected virtual bool ReadNextRecord(bool onlyReadHeaders, bool skipToNextLine) Type = typeof(string) }; + int existingIndex; + if (_fieldHeaderIndexes.TryGetValue(headerName, out existingIndex)) + { + if (DuplicateHeaderEncountered == null) + throw new DuplicateHeaderException(headerName, i); + + DuplicateHeaderEventArgs args = new DuplicateHeaderEventArgs(headerName, i, existingIndex); + DuplicateHeaderEncountered(this, args); + col.Name = args.HeaderName; + } + + _fieldHeaderIndexes.Add(col.Name, i); + // Should be correct as we are going in ascending order. Columns.Add(col); } - _fieldHeaderIndexes.Add(headerName, i); + else + _fieldHeaderIndexes.Add(headerName, i); } // Proceed to first record @@ -2360,10 +2381,12 @@ string IDataRecord.GetName(int i) EnsureInitialize(); ValidateDataReader(DataReaderValidations.IsNotClosed); - if (i < 0 || i >= Columns.Count) + if (i < 0 || i >= FieldCount) throw new ArgumentOutOfRangeException("i", i, string.Format(CultureInfo.InvariantCulture, ExceptionMessage.FieldIndexOutOfRange, i)); + if (i >= Columns.Count) return null; + return Columns[i].Name; } diff --git a/code/CsvReader/Events/DuplicateHeaderEventArgs.cs b/code/CsvReader/Events/DuplicateHeaderEventArgs.cs new file mode 100644 index 0000000..33b7148 --- /dev/null +++ b/code/CsvReader/Events/DuplicateHeaderEventArgs.cs @@ -0,0 +1,71 @@ +// LumenWorks.Framework.IO.CSV.ParseErrorEventArgs +// Copyright (c) 2006 S�bastien Lorion +// +// MIT license (http://en.wikipedia.org/wiki/MIT_License) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +using System; + +namespace CsvReader.Events +{ + /// + /// Provides data for the event. + /// + public class DuplicateHeaderEventArgs + : EventArgs + { + #region Constructors + + /// + /// Initializes a new instance of the DuplicateHeaderEventArgs class. + /// + /// The name of the duplicate header. + /// The index of the duplicate header being added. + /// The index of the duplicate header that is already in the Column collection. + public DuplicateHeaderEventArgs(string headerName, int index, int existingDuplicateIndex) + { + HeaderName = headerName; + Index = index; + ExistingDuplicateIndex = existingDuplicateIndex; + } + + #endregion + + #region Properties + + /// + /// Name of the header that is a duplicate. + /// + /// The header name. + public string HeaderName { get; set; } + + /// + /// Index of the duplicate header being added + /// + /// The column index + public int Index { get; } + + /// + /// Index of the duplicate header that has already been added to the Column collection + /// + /// The column index + public int ExistingDuplicateIndex { get; } + + #endregion + } +} diff --git a/code/CsvReader/Exceptions/DuplicateHeaderException.cs b/code/CsvReader/Exceptions/DuplicateHeaderException.cs new file mode 100644 index 0000000..6d64c9a --- /dev/null +++ b/code/CsvReader/Exceptions/DuplicateHeaderException.cs @@ -0,0 +1,71 @@ +// LumenWorks.Framework.IO.Csv.MalformedCsvException +// Copyright (c) 2005 S�bastien Lorion +// +// MIT license (http://en.wikipedia.org/wiki/MIT_License) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +using System; + +namespace CsvReader.Exceptions +{ + /// + /// Represents the exception that is thrown when a duplicate column header name encounter. + /// + public class DuplicateHeaderException + : Exception + { + /// + /// Contains the message that describes the error. + /// + private string _headerName; + + /// + /// Contains the column index where the duplicate was found. + /// + private int _columnIndex; + + /// + /// Initializes a new instance of the DuplicateHeaderException class. + /// + public DuplicateHeaderException(string headerName, int columnIndex) + : base($"Duplicate header {headerName} encountered at column index {columnIndex}") + { + _headerName = headerName; + _columnIndex = columnIndex; + } + + + /// + /// Gets the HeaderName of the column with the duplicate. + /// + /// The name of the column header. + public string HeaderName + { + get { return _headerName; } + } + + /// + /// Gets the column index where the duplicate was found. + /// + /// The index of the column. + public int ColumnIndex + { + get { return _columnIndex; } + } + } +} From 90986cbb3746711d5b06000c01f3b3cf4806ed50 Mon Sep 17 00:00:00 2001 From: Ally Dewji Date: Mon, 2 Mar 2026 22:55:37 -0500 Subject: [PATCH 2/3] Upgraded NET version to latest --- CsvReader.sln | 6 ++++-- RELEASE_NOTES.md | 5 +++++ .../CsvReader.Benchmarks.csproj | 18 +++++++++++++++++- .../CsvReader.UnitTests.csproj | 18 +++++++++++++++++- .../CsvReader.WebDemo/CsvReader.WebDemo.csproj | 18 +++++++++++++++++- .../CsvReader.WpfDemo/CsvReader.WpfDemo.csproj | 18 +++++++++++++++++- code/CsvReader.WpfDemo/MainViewModel.cs | 4 +--- code/CsvReader/CsvReader.csproj | 2 +- nuget/LumenWorksCsvReader2.nuspec | 14 +++++++------- packet.dependencies | 4 ++-- 10 files changed, 88 insertions(+), 19 deletions(-) diff --git a/CsvReader.sln b/CsvReader.sln index 19b6c92..39f8f9d 100644 --- a/CsvReader.sln +++ b/CsvReader.sln @@ -1,12 +1,14 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.30621.155 +# Visual Studio Version 18 +VisualStudioVersion = 18.3.11520.95 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{D23ED3F3-7347-4369-B8AA-4E622924D01E}" ProjectSection(SolutionItems) = preProject build.fsx = build.fsx License.md = License.md + nuget\LumenWorksCsvReader2.nuspec = nuget\LumenWorksCsvReader2.nuspec + packet.dependencies = packet.dependencies README.md = README.md RELEASE_NOTES.md = RELEASE_NOTES.md EndProjectSection diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 0779b3e..a93eca8 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,3 +1,8 @@ +#### 4.5.0 (2026-03-01) +* Removed support of .NET 6.0 and .NET 7.0 +* Added support of .NET 9.0 and 10.0 +* Exception thrown with duplicated colum names + #### 4.4.0 (2024-09-12) * Added MapDataToDto method to map CSV file to an IEnumerable where T is a type of an entity/DTO * Removed support of .NET Framework 4.7.2, .NET Core 3.1 and .NET 5.0. Added support of .NET 7.0 and 8.0 diff --git a/code/CsvReader.Benchmarks/CsvReader.Benchmarks.csproj b/code/CsvReader.Benchmarks/CsvReader.Benchmarks.csproj index b0cb9a2..a475e6b 100644 --- a/code/CsvReader.Benchmarks/CsvReader.Benchmarks.csproj +++ b/code/CsvReader.Benchmarks/CsvReader.Benchmarks.csproj @@ -2,11 +2,27 @@ Exe - net48;net6.0-windows;net7.0-windows;net8.0-windows + net48;net8.0-windows;net9.0-windows;net10.0-windows false $(SolutionDir)build\CsvReader.Benchmarks\$(Configuration) + + 8 + + + + 8 + + + + 8 + + + + 8 + + diff --git a/code/CsvReader.UnitTests/CsvReader.UnitTests.csproj b/code/CsvReader.UnitTests/CsvReader.UnitTests.csproj index f51a6c1..df61ce3 100644 --- a/code/CsvReader.UnitTests/CsvReader.UnitTests.csproj +++ b/code/CsvReader.UnitTests/CsvReader.UnitTests.csproj @@ -1,12 +1,28 @@  - net48;net6.0;net7.0;net8.0 + net48;net8.0;net9.0;net10.0 false false $(SolutionDir)build\CsvReader.UnitTests\$(Configuration) + + 8 + + + + 8 + + + + 8 + + + + 8 + + diff --git a/code/CsvReader.WebDemo/CsvReader.WebDemo.csproj b/code/CsvReader.WebDemo/CsvReader.WebDemo.csproj index 46fd1c2..7991e2c 100644 --- a/code/CsvReader.WebDemo/CsvReader.WebDemo.csproj +++ b/code/CsvReader.WebDemo/CsvReader.WebDemo.csproj @@ -1,11 +1,27 @@ - net6.0;net7.0;net8.0 + net8.0;net9.0;net10.0 false $(SolutionDir)build\CsvReader.WebDemo\$(Configuration) + + 8 + + + + 8 + + + + 8 + + + + 8 + + diff --git a/code/CsvReader.WpfDemo/CsvReader.WpfDemo.csproj b/code/CsvReader.WpfDemo/CsvReader.WpfDemo.csproj index 323bb4b..6c5f3e8 100644 --- a/code/CsvReader.WpfDemo/CsvReader.WpfDemo.csproj +++ b/code/CsvReader.WpfDemo/CsvReader.WpfDemo.csproj @@ -2,12 +2,28 @@ WinExe - net48;net6.0-windows;net7.0-windows;net8.0-windows + net48;net8.0-windows;net9.0-windows;net10.0-windows true false $(SolutionDir)build\CsvReader.WpfDemo\$(Configuration) + + 8 + + + + 8 + + + + 8 + + + + 8 + + diff --git a/code/CsvReader.WpfDemo/MainViewModel.cs b/code/CsvReader.WpfDemo/MainViewModel.cs index 7824409..b56b616 100644 --- a/code/CsvReader.WpfDemo/MainViewModel.cs +++ b/code/CsvReader.WpfDemo/MainViewModel.cs @@ -54,9 +54,7 @@ public DataView DataView { get { -#if NET472 - return new DataView(_data); -#elif NET48 +#if NET48 return new DataView(_data); #else return _data.AsDataView(); diff --git a/code/CsvReader/CsvReader.csproj b/code/CsvReader/CsvReader.csproj index 69fa1da..41d2584 100644 --- a/code/CsvReader/CsvReader.csproj +++ b/code/CsvReader/CsvReader.csproj @@ -1,7 +1,7 @@ - netstandard2.0;net48;net6.0;net7.0;net8.0 + netstandard2.0;net48;net8.0;net9.0;net10.0 true LumenWorks.Framework.snk false diff --git a/nuget/LumenWorksCsvReader2.nuspec b/nuget/LumenWorksCsvReader2.nuspec index cdf8c8b..8306110 100644 --- a/nuget/LumenWorksCsvReader2.nuspec +++ b/nuget/LumenWorksCsvReader2.nuspec @@ -2,7 +2,7 @@ LumenWorksCsvReader2 - 4.4.0 + 4.5.0 LumenWorks CSV Reader 2 Sébastien Lorion, Paul Hatcher, Maxim Ivanov Maxim Ivanov @@ -12,16 +12,16 @@ false An extended version of LumenWorksCsvReader - Added MapDataToDto<T> method to map CSV file to an IEnumerable<T> where T is a type of an entity/DTO - Removed support of .NET Framework 4.7.2, .NET Core 3.1 and .NET 5.0. Added support of .NET 7.0 and 8.0 + + Removed support of .NET 6.0 and .NET 7.0. Added support of .NET 9.0 and 10.0. Exception thrown with duplicated colum names Copyright © 2005 Sébastien Lorion, 2014 Paul Hatcher, 2020-2022 Maxim Ivanov CSV cvs-reader - - + + @@ -29,9 +29,9 @@ - - + + diff --git a/packet.dependencies b/packet.dependencies index 5f0f25e..225ce2c 100644 --- a/packet.dependencies +++ b/packet.dependencies @@ -1,9 +1,9 @@ group build source https://api.nuget.org/v3/index.json - framework: net6.0 + framework: net8.0 storage: none - nuget FSharp.Core 6.0.3 + nuget FSharp.Core 8.0.400 nuget Newtonsoft.Json nuget NuGet.Protocol nuget NUnit From e8ed35a1a50bc443bc8ea83ed75f9eeb45f1b281 Mon Sep 17 00:00:00 2001 From: Ally Dewji Date: Mon, 2 Mar 2026 23:54:20 -0500 Subject: [PATCH 3/3] updated csproj to have configs for manual pack step --- code/CsvReader/CsvReader.csproj | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/code/CsvReader/CsvReader.csproj b/code/CsvReader/CsvReader.csproj index 41d2584..ead4a5f 100644 --- a/code/CsvReader/CsvReader.csproj +++ b/code/CsvReader/CsvReader.csproj @@ -14,12 +14,22 @@ github CSV cvs-reader Added an additional CsvReader constructor parameter to specify a custom 'new line' character + README.md + False + 4.5.0 + + + True + \ + + + ExceptionMessage.resx