using System.Collections;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Globalization;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq.Expressions;
using CsvHelper.Configuration;
using CsvHelper.Configuration.Attributes;
using CsvHelper.Expressions;
using CsvHelper.TypeConversion;
public string A { get; set; }
public string BString { get; set; }
public int? C { get; set; }
public string D { get; set; }
public string E { get; set; }
public DateTime? F { get; set; }
public decimal? G { get; set; }
public decimal? H { get; set; }
public decimal? I { get; set; }
public decimal? J { get; set; }
public int? K { get; set; }
public string L { get; set; }
public DateTime? M { get; set; }
public sealed class BMap : ClassMap<B>
Map(m => m.BString).Index(1);
Map(m => m.F).Index(5).TypeConverterOption.Format("yyyyMMdd");
Map(m => m.M).Index(12).TypeConverterOption.Format("yyyy-MM-dd-hh.mm.ss.ffffff");
public static partial class UtilExtensions
public static Func<TSource, object> CreatePropertyGetter<TSource>(PropertyInfo propertyInfo)
var parameter = Expression.Parameter(typeof(TSource), "obj");
var property = Expression.Property(parameter, propertyInfo);
var convert = Expression.Convert(property, typeof(object));
var lambda = Expression.Lambda(typeof(Func<TSource, object>), convert, parameter);
return (Func<TSource, object>)lambda.Compile();
public static ReadOnlyDictionary<string, Func<TSource, object>> PropertyGetters<TSource>() => PropertyExpressionsCache<TSource>.PropertyGetters;
static ReadOnlyDictionary<string, Func<TSource, object>> CreatePropertyGetters<TSource>() =>
.GetSerializableProperties()
.ToDictionary(p => p.Name,
p => CreatePropertyGetter<TSource>(p))
static class PropertyExpressionsCache<TSource>
public static ReadOnlyDictionary<string, Func<TSource, object>> PropertyGetters { get; } = UtilExtensions.CreatePropertyGetters<TSource>();
public static ReadOnlyDictionary<TKey, TValue> ToReadOnly<TKey, TValue>(this IDictionary<TKey, TValue> dictionary) =>
new ReadOnlyDictionary<TKey, TValue>(dictionary ?? throw new ArgumentNullException());
public System.Diagnostics.Stopwatch csvTableTimer { get; } = new();
public long Load<TClass, TClassMap>(string rFile, int dtbatchSize) where TClassMap : ClassMap<TClass>, new()
bool isBadRecord = false;
var rconfig = CreateCsvConfiguration(
var dt = UtilExtensions.CreateDataTable(typeof(TClass));
var properties = UtilExtensions.PropertyGetters<TClass>();
using (var reader = new StreamReader(rFile))
using (var csv = new CsvReader(reader, rconfig))
csv.Context.RegisterClassMap<TClassMap>();
var record = csv.GetRecord<TClass>();
DataRow dr = dt.NewRow();
foreach (var p in properties)
var value = p.Value(record);
if (++batchCount >= dtbatchSize)
protected virtual void FlushTable(DataTable dt) => dt.Clear();
protected virtual void Commit() {}
public static CsvConfiguration CreateCsvConfiguration(BadDataFound badDataFound) =>
new CsvHelper.Configuration.CsvConfiguration(CultureInfo.InvariantCulture)
MissingFieldFound = null,
TrimOptions = TrimOptions.Trim,
BadDataFound = badDataFound,
public static partial class UtilExtensions
static IEnumerable<PropertyInfo> GetSerializableProperties(this Type type) =>
type.GetProperties().Where(p => p.GetIndexParameters().Length == 0 && p.CanRead && p.CanWrite && p.GetGetMethod() != null && p.GetSetMethod() != null);
public static DataTable CreateDataTable(Type type)
var dt = new DataTable();
foreach (var p in type.GetSerializableProperties())
dt.Columns.Add(p.Name, Nullable.GetUnderlyingType(p.PropertyType) ?? p.PropertyType);
public static void Test()
var rFile = "Question68028093.csv";
var dateTimeStart = new DateTime(2021, 6, 18);
var records = Enumerable.Range(0, count).Select(i => new B
M = dateTimeStart.AddSeconds(i),
var config = FileLoader.CreateCsvConfiguration(x => { });
using (var writer = new StreamWriter(rFile))
using (var csv = new CsvWriter(writer, config))
csv.Context.RegisterClassMap<BMap>();
csv.WriteRecords(records);
var loader = new FileLoader();
foreach (var i in Enumerable.Range(0, repeats))
var nRead = loader.Load<B, BMap>(rFile, 1000);
Assert.AreEqual(nRead, count);
Console.WriteLine("Number of rows={0}, Repeats={1}, total ElapsedMilliseconds={2}", count, repeats, loader.csvTableTimer.ElapsedMilliseconds);
public static void Main()
Console.WriteLine("Environment version: {0} ({1})", System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription , GetNetCoreVersion());
Console.WriteLine("OS Version: {0}, NewLine: {1}", System.Environment.OSVersion, JsonConvert.SerializeObject(Environment.NewLine));
Console.WriteLine("{0} version: {1}", typeof(CsvReader).Assembly.GetName().Name, typeof(CsvReader).Assembly.FullName);
Console.WriteLine("Failed with unhandled exception: ");
public static string GetNetCoreVersion()
var assembly = typeof(System.Runtime.GCSettings).GetTypeInfo().Assembly;
var assemblyPath = assembly.Location.Split(new[] { '/', '\\' }, StringSplitOptions.RemoveEmptyEntries);
int netCoreAppIndex = Array.IndexOf(assemblyPath, "Microsoft.NETCore.App");
if (netCoreAppIndex > 0 && netCoreAppIndex < assemblyPath.Length - 2)
return assemblyPath[netCoreAppIndex + 1];