diff --git a/src/CsvHelper/CsvDataReader.cs b/src/CsvHelper/CsvDataReader.cs index a90ec01c4..2572099f7 100644 --- a/src/CsvHelper/CsvDataReader.cs +++ b/src/CsvHelper/CsvDataReader.cs @@ -13,12 +13,14 @@ namespace CsvHelper; /// public class CsvDataReader : IDataReader { - private readonly CsvReader csv; + private readonly bool leaveOpen; + private readonly CsvReader csv; private readonly DataTable schemaTable; private bool skipNextRead; + private bool disposed; - /// - public object this[int i] + /// + public object this[int i] { get { @@ -45,7 +47,7 @@ public int Depth } /// - public bool IsClosed { get; private set; } + public bool IsClosed => disposed; /// public int RecordsAffected @@ -65,16 +67,18 @@ public int FieldCount } } - /// - /// Initializes a new instance of the class. - /// - /// The CSV. - /// The DataTable representing the file schema. - public CsvDataReader(CsvReader csv, DataTable? schemaTable = null) + /// + /// Initializes a new instance of the class. + /// + /// The CSV. + /// The DataTable representing the file schema. + /// true to leave the open after the object is disposed, otherwise false. + public CsvDataReader(CsvReader csv, DataTable? schemaTable = null, bool leaveOpen = false) { this.csv = csv; + this.leaveOpen = leaveOpen; - csv.Read(); + csv.Read(); if (csv.Configuration.HasHeaderRecord && csv.HeaderRecord == null) { @@ -94,11 +98,33 @@ public void Close() Dispose(); } - /// + /// public void Dispose() { - csv.Dispose(); - IsClosed = true; + Dispose(disposing: true); + GC.SuppressFinalize(this); + } + + /// + /// Disposes the object. + /// + /// Indicates if the object is being disposed. + protected virtual void Dispose(bool disposing) + { + if (disposed) + { + return; + } + + if (disposing) + { + if (!leaveOpen) + { + csv?.Dispose(); + } + } + + disposed = true; } /// diff --git a/tests/CsvHelper.Tests/CsvDataReaderDisposalTests.cs b/tests/CsvHelper.Tests/CsvDataReaderDisposalTests.cs new file mode 100644 index 000000000..fd8ee514b --- /dev/null +++ b/tests/CsvHelper.Tests/CsvDataReaderDisposalTests.cs @@ -0,0 +1,48 @@ +using System.Globalization; +using System.IO; +using System.Text; +using Xunit; + +namespace CsvHelper.Tests +{ + public class CsvDataReaderDisposalTests + { + [Fact] + public void ShouldNotDisposeCsvReaderWhenLeaveOpenParameterIsTrue() + { + var s = new StringBuilder(); + s.AppendLine("StringColumn"); + s.AppendLine("one"); + using (var reader = new StringReader(s.ToString())) + using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture)) + { + var dataReader = new CsvDataReader(csv, leaveOpen: true); + dataReader.Dispose(); + + var record = csv.GetRecord(); + Assert.NotNull(record); + } + } + + [Fact] + public void DisposeShouldSetIsClosed() + { + var s = new StringBuilder(); + s.AppendLine("StringColumn"); + s.AppendLine("one"); + using (var reader = new StringReader(s.ToString())) + using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture)) + { + var dataReader = new CsvDataReader(csv, leaveOpen: true); + dataReader.Dispose(); + + Assert.True(dataReader.IsClosed); + } + } + + private class TestRecord() + { + public string StringColumn { get; set; } = string.Empty; + } + } +}