@@ -20,8 +20,9 @@ internal class DataTable<T>
2020{
2121 private readonly IEnumerable < PropertyData > _properties ;
2222 private readonly IEnumerable < T > _records ;
23+ private readonly Dictionary < string , object ? > _defaults = new ( ) ;
2324
24- internal DataTable ( )
25+ private DataTable ( )
2526 {
2627 _records = [ ] ;
2728 _properties = [ ] ;
@@ -64,39 +65,24 @@ internal DataTable(IEnumerable<T> data, string name, IEnumerable<IPropertyRule>
6465 var modelProperties = typeof ( T ) . GetProperties ( )
6566 . ToDictionary ( x => x . Name , x => x ) ;
6667
67- _properties = rules
68- . Select ( x => new PropertyData
69- {
70- Property = modelProperties [ x . PropertyName ( ) ] ,
71- HasBaseConverter = false ,
72- Name = x . ColumnName ( )
73- } )
74- . ToList ( ) ;
68+ _properties = rules . Select ( r => new PropertyData
69+ {
70+ Property = modelProperties [ r . PropertyName ( ) ] ,
71+ HasBaseConverter = false ,
72+ Name = r . ColumnName ( )
73+ } )
74+ . ToList ( ) ;
7575
76- Headers = _properties . Select ( x => x . Name )
76+ Headers = _properties . Select ( p => p . Name )
7777 . ToList ( ) ;
7878
79- var ruleByDefaultValue = rules
80- . Where ( x => x . DefaultColumnValue ( ) != null )
81- . ToDictionary ( x => x . PropertyName ( ) , x => x . DefaultColumnValue ( ) ) ;
82-
83- foreach ( var model in data )
79+ // collect defaults per *model* property name
80+ foreach ( var r in rules )
8481 {
85- var properties = model ! . GetType ( )
86- . GetProperties ( ) ;
87- foreach ( var property in properties )
82+ var def = r . DefaultColumnValue ( ) ;
83+ if ( def != null )
8884 {
89- if ( ruleByDefaultValue . TryGetValue ( property . Name , out var value ) )
90- {
91- if ( ! model . GetType ( )
92- . IsGenericType || model . GetType ( )
93- . Name
94- . Contains ( "String" ) )
95- {
96- model . GetType ( )
97- . GetProperty ( property . Name ) ! . SetValue ( model , value ) ;
98- }
99- }
85+ _defaults [ r . PropertyName ( ) ] = def ;
10086 }
10187 }
10288
@@ -114,10 +100,15 @@ internal IEnumerable<IDictionary<string, string>> GetRecordsForExport()
114100
115101 foreach ( var property in _properties )
116102 {
117- var value = property . Property . GetValue ( dataRow ) ;
103+ var raw = property . Property . GetValue ( dataRow ) ;
118104
119- var toString = ConvertDataToString ( value , property . HasBaseConverter ) ;
105+ // If the model value is null and we have a default for that model property, use it
106+ if ( raw is null && _defaults . TryGetValue ( property . ModelPropertyName , out var def ) )
107+ {
108+ raw = def ;
109+ }
120110
111+ var toString = ConvertDataToString ( raw , property . HasBaseConverter ) ;
121112 row . Add ( property . Name , toString ) ;
122113 }
123114
@@ -128,78 +119,81 @@ internal IEnumerable<IDictionary<string, string>> GetRecordsForExport()
128119 internal List < byte [ ] > ToCsv ( )
129120 {
130121 var records = GetRecordsForExport ( ) ;
131-
132- var recordsChunks = records . Chunk ( Constants . CsvLinesCount ) ;
122+ var chunks = records . Chunk ( Constants . CsvLinesCount ) ;
133123
134124 var files = new List < byte [ ] > ( ) ;
135125
136- var csv = new StringBuilder ( ) ;
137- csv . AppendLine ( string . Join ( "," , Headers ) ) ;
138-
139- foreach ( var chunk in recordsChunks )
126+ foreach ( var chunk in chunks )
140127 {
128+ var sb = new StringBuilder ( ) ;
129+ sb . AppendLine ( string . Join ( "," , Headers ) ) ;
130+
141131 foreach ( var record in chunk )
142132 {
143- csv . AppendLine ( string . Join ( "," , record . Values . Select ( Encapsulate ) ) ) ;
133+ sb . AppendLine ( string . Join ( "," , record . Values . Select ( Encapsulate ) ) ) ;
144134 }
145- }
146-
147- var data = Encoding . UTF8
148- . GetBytes ( csv . ToString ( )
149- . Trim ( ) ) ;
150135
151- var file = Encoding . UTF8
152- . GetPreamble ( )
153- . Concat ( data )
154- . ToArray ( ) ;
155-
156- files . Add ( file ) ;
136+ var data = Encoding . UTF8 . GetBytes ( sb . ToString ( )
137+ . TrimEnd ( ) ) ;
138+ var file = Encoding . UTF8
139+ . GetPreamble ( )
140+ . Concat ( data )
141+ . ToArray ( ) ;
142+ files . Add ( file ) ;
143+ }
157144
158145 return files ;
159146 }
160147
161148 internal List < byte [ ] > ToXlsx ( )
162149 {
163150 var records = GetRecordsForExport ( ) ;
164-
165151 var recordsChunks = records . Chunk ( Constants . ExcelLinesCount ) ;
166152
167153 var files = new List < byte [ ] > ( ) ;
168154
169- var workbook = new XLWorkbook ( ) ;
170- var worksheet = workbook . Worksheets . Add ( Name . ToValidName ( ) ) ;
171-
172- for ( var i = 0 ; i < Headers . Count ; i ++ )
155+ foreach ( var chunk in recordsChunks )
173156 {
174- worksheet . Cell ( 1 , i + 1 )
175- . Value = Headers [ i ] ;
176- worksheet . Cell ( 1 , i + 1 )
177- . Style . Font . Bold = true ;
178- }
157+ using var workbook = new XLWorkbook ( ) ;
158+ var ws = workbook . Worksheets . Add ( Name . ToValidName ( ) ) ;
179159
180- worksheet . SheetView . FreezeRows ( 1 ) ;
181- worksheet . RangeUsed ( ) !
182- . SetAutoFilter ( true ) ;
183- worksheet . Columns ( )
184- . AdjustToContents ( ) ;
160+ // header
161+ for ( var c = 0 ; c < Headers . Count ; c ++ )
162+ {
163+ var cell = ws . Cell ( 1 , c + 1 ) ;
164+ cell . Value = Headers [ c ] ;
165+ cell . Style . Font . Bold = true ;
166+ }
185167
186- foreach ( var chunk in recordsChunks )
187- {
188- for ( var i = 0 ; i < chunk . Length ; i ++ )
168+ ws . SheetView . FreezeRows ( 1 ) ;
169+ ws . Range ( 1 , 1 , 1 , Headers . Count )
170+ . SetAutoFilter ( ) ;
171+
172+ // data
173+ var row = 2 ;
174+ foreach ( var t in chunk )
189175 {
190176 for ( var j = 0 ; j < Headers . Count ; j ++ )
191177 {
192- worksheet . Cell ( i + 2 , j + 1 )
193- . Value = chunk [ i ] [ Headers [ j ] ] ;
194- worksheet . Cell ( i + 2 , j + 1 )
195- . Style . NumberFormat . Format = "@" ;
178+ var cell = ws . Cell ( row , j + 1 ) ;
179+ cell . Value = t [ Headers [ j ] ] ;
180+ cell . Style . NumberFormat . Format = "@" ;
196181 }
182+
183+ row ++ ;
197184 }
198- }
199185
200- using var stream = new MemoryStream ( ) ;
201- workbook . SaveAs ( stream ) ;
202- files . Add ( stream . ToArray ( ) ) ;
186+ // cheap width based on header only
187+ for ( var c = 1 ; c <= Headers . Count ; c ++ )
188+ {
189+ ws . Column ( c )
190+ . Width = Math . Max ( Headers [ c - 1 ] . Length + 2 , 10 ) ;
191+ }
192+
193+ using var stream = new MemoryStream ( ) ;
194+ workbook . SaveAs ( stream ) ;
195+ files . Add ( stream . ToArray ( ) ) ;
196+ }
203197
204198 return files ;
205199 }
0 commit comments