@@ -14,23 +14,24 @@ namespace VirtoCommerce.ImportModule.CsvHelper
1414{
1515 public class CsvDataReader < TCsvImportable , TCsvClassMap > : IImportDataReader where TCsvClassMap : ClassMap
1616 {
17- private readonly Stream _stream ;
18-
1917 private readonly int _pageSize ;
2018 private readonly bool _needReadRaw ;
21- private int ? _totalCount ;
22- private string _headerRaw ;
19+ private bool _disposed ;
2320 protected CsvConfiguration CsvConfiguration { get ; set ; }
21+ protected readonly Stream Stream ;
22+ protected readonly Stream CountStream ;
2423 protected readonly CsvReader CsvReader ;
24+ protected string HeaderRaw ;
25+ protected int ? TotalCount ;
2526
2627 public bool HasMoreResults { get ; private set ; } = true ;
2728
2829 public CsvDataReader ( Stream stream , ImportContext context , bool needReadRaw = false )
2930 {
3031 CsvConfiguration = GetConfiguration ( context ) ;
3132
32- _stream = stream ;
33- CsvReader = new CsvReader ( new StreamReader ( _stream ) , CsvConfiguration ) ;
33+ Stream = stream ;
34+ CsvReader = new CsvReader ( new StreamReader ( Stream ) , CsvConfiguration ) ;
3435 CsvReader . Context . RegisterClassMap < TCsvClassMap > ( ) ;
3536
3637 _pageSize = Convert . ToInt32 ( context . ImportProfile . Settings . FirstOrDefault ( x => x . Name == CsvSettings . PageSize . Name ) ? . Value ?? 50 ) ;
@@ -41,42 +42,73 @@ public CsvDataReader(Stream stream, ImportContext context, CsvConfiguration csvC
4142 {
4243 CsvConfiguration = MergeWithDefaultConfig ( csvConfiguration , context ) ;
4344
44- _stream = stream ;
45- CsvReader = new CsvReader ( new StreamReader ( _stream ) , CsvConfiguration ) ;
45+ Stream = stream ;
46+ CsvReader = new CsvReader ( new StreamReader ( Stream ) , CsvConfiguration ) ;
4647 CsvReader . Context . RegisterClassMap < TCsvClassMap > ( ) ;
4748
4849 _pageSize = Convert . ToInt32 ( context . ImportProfile . Settings . FirstOrDefault ( x => x . Name == CsvSettings . PageSize . Name ) ? . Value ?? 50 ) ;
4950 _needReadRaw = needReadRaw ;
5051 }
5152
53+ public CsvDataReader ( Stream stream , Stream countStream , ImportContext context , bool needReadRaw = false )
54+ : this ( stream , context , needReadRaw )
55+ {
56+ CountStream = countStream ;
57+ }
58+
59+ public CsvDataReader ( Stream stream , Stream countStream , ImportContext context , CsvConfiguration csvConfiguration , bool needReadRaw = false )
60+ : this ( stream , context , csvConfiguration , needReadRaw )
61+ {
62+ CountStream = countStream ;
63+ }
64+
5265 public virtual async Task < int > GetTotalCountAsync ( ImportContext context )
5366 {
54- if ( _totalCount . HasValue )
67+ if ( TotalCount . HasValue )
5568 {
56- return _totalCount . Value ;
69+ return TotalCount . Value ;
5770 }
5871
59- var streamPosition = _stream . Position ;
60- _stream . Seek ( 0 , SeekOrigin . Begin ) ;
72+ Stream stream ;
73+ bool leaveOpen ;
74+ if ( Stream . CanSeek )
75+ {
76+ stream = Stream ;
77+ leaveOpen = true ;
78+ }
79+ else
80+ {
81+ stream = CountStream ?? throw new InvalidOperationException ( "Count stream is not provided." ) ;
82+ leaveOpen = false ;
83+ }
6184
62- var streamReader = new StreamReader ( _stream , leaveOpen : true ) ;
63- var csvReader = new CsvReader ( streamReader , CsvConfiguration ) ;
85+ var streamPosition = 0L ;
86+ if ( stream . CanSeek )
87+ {
88+ streamPosition = stream . Position ;
89+ stream . Seek ( 0 , SeekOrigin . Begin ) ;
90+ }
91+
92+ using var csvReader = new CsvReader ( new StreamReader ( stream ) , CsvConfiguration , leaveOpen ) ;
6493
6594 await csvReader . ReadAsync ( ) ;
6695 csvReader . ReadHeader ( ) ;
6796
68- _headerRaw = string . Join ( csvReader . Configuration . Delimiter , csvReader . HeaderRecord ) ;
97+ HeaderRaw = string . Join ( csvReader . Configuration . Delimiter , csvReader . HeaderRecord ) ;
6998
70- _totalCount = 0 ;
99+ TotalCount = 0 ;
71100
72101 while ( await csvReader . ReadAsync ( ) )
73102 {
74- _totalCount ++ ;
103+ TotalCount ++ ;
75104 }
76105
77- _stream . Seek ( streamPosition , SeekOrigin . Begin ) ;
106+ if ( stream . CanSeek )
107+ {
108+ stream . Seek ( streamPosition , SeekOrigin . Begin ) ;
109+ }
78110
79- return _totalCount . Value ;
111+ return TotalCount . Value ;
80112 }
81113
82114 public virtual async Task < object [ ] > ReadNextPageAsync ( ImportContext context )
@@ -103,7 +135,7 @@ public virtual async Task<object[]> ReadNextPageAsync(ImportContext context)
103135 result . Add ( new CsvImportRecord < TCsvImportable >
104136 {
105137 Row = row ,
106- RawHeader = _headerRaw ,
138+ RawHeader = HeaderRaw ,
107139 RawRecord = rawRecord ,
108140 Record = record ,
109141 } ) ;
@@ -188,21 +220,30 @@ public void Dispose()
188220
189221 protected virtual void Dispose ( bool disposing )
190222 {
191- CsvReader . Dispose ( ) ;
192- _stream ? . Dispose ( ) ;
223+ if ( _disposed )
224+ {
225+ return ;
226+ }
227+ if ( disposing )
228+ {
229+ CsvReader . Dispose ( ) ;
230+ Stream ? . Dispose ( ) ;
231+ CountStream ? . Dispose ( ) ;
232+ }
233+ _disposed = true ;
193234 }
194235
195236 private CsvConfiguration MergeWithDefaultConfig ( CsvConfiguration csvConfiguration , ImportContext context )
196237 {
197238 var defaultCsvConfiguration = GetConfiguration ( context ) ;
198- var result = csvConfiguration ;
199- result . Delimiter = result . Delimiter ?? defaultCsvConfiguration . Delimiter ;
200- result . PrepareHeaderForMatch = result . PrepareHeaderForMatch ?? defaultCsvConfiguration . PrepareHeaderForMatch ;
201- result . BadDataFound = result . BadDataFound ?? defaultCsvConfiguration . BadDataFound ;
202- result . ReadingExceptionOccurred = result . ReadingExceptionOccurred ?? defaultCsvConfiguration . ReadingExceptionOccurred ;
203- result . MissingFieldFound = result . MissingFieldFound ?? defaultCsvConfiguration . MissingFieldFound ;
204-
205- return result ;
239+
240+ csvConfiguration . Delimiter ??= defaultCsvConfiguration . Delimiter ;
241+ csvConfiguration . PrepareHeaderForMatch ??= defaultCsvConfiguration . PrepareHeaderForMatch ;
242+ csvConfiguration . BadDataFound ??= defaultCsvConfiguration . BadDataFound ;
243+ csvConfiguration . ReadingExceptionOccurred ??= defaultCsvConfiguration . ReadingExceptionOccurred ;
244+ csvConfiguration . MissingFieldFound ??= defaultCsvConfiguration . MissingFieldFound ;
245+
246+ return csvConfiguration ;
206247 }
207248 }
208249}
0 commit comments