I wrote about using SqlBulkCopy to fast load data from .NET into SQL Server database. This class needs a DataTable or IDataReader instance as a source. You can convert a C# List to DataTable (look here Converting List To DataTable). Now I’ll show a couple of examples how to convert List to IDataReader.
1. Straight implementation of IDataReader interface
I’ve seen a couple of good examples from Bruce Dunwiddie https://www.csvreader.com/posts/GenericListDataReader.cs and Venu Gopal http://technico.qnownow.com/custom-data-reader-to-bulk-copy-data-from-object-collection-to-sql-server/. Based on these two solutions I’ve made my one.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 |
public class ListDataReader<T> : IDataReader where T : class { #region Private fields private IEnumerator<T> _iterator; private List<PropertyInfo> _properties = new List<PropertyInfo>(); #endregion #region Public properties #endregion #region Constructors public ListDataReader(IEnumerable<T> list) { this._iterator = list.GetEnumerator(); _properties.AddRange( typeof(T).GetProperties( BindingFlags.GetProperty | BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly ) ); } #endregion #region IDataReader implementation public int Depth { get; } public bool IsClosed { get; } public int RecordsAffected { get; } public void Close() { _iterator.Dispose(); } public DataTable GetSchemaTable() { throw new NotImplementedException(); } public bool NextResult() { throw new NotImplementedException(); } public bool Read() { return _iterator.MoveNext(); } #endregion #region IDisposable implementation public void Dispose() { Close(); } #endregion #region IDataRecord public string GetName(int i) { return _properties[i].Name; } public string GetDataTypeName(int i) { throw new NotImplementedException(); } public Type GetFieldType(int i) { return _properties[i].PropertyType; } public object GetValue(int i) { return _properties[i].GetValue(_iterator.Current, null); } public int GetValues(object[] values) { int numberOfCopiedValues = Math.Max(_properties.Count, values.Length); for (int i = 0; i < numberOfCopiedValues; i++) { values[i] = GetValue(i); } return numberOfCopiedValues; } public int GetOrdinal(string name) { var index = _properties.FindIndex(p => p.Name == name); return index; } public bool GetBoolean(int i) { return (bool) GetValue(i); } public byte GetByte(int i) { return (byte) GetValue(i); } public long GetBytes(int i, long fieldOffset, byte[] buffer, int bufferoffset, int length) { throw new NotImplementedException(); } public char GetChar(int i) { return (char) GetValue(i); } public long GetChars(int i, long fieldoffset, char[] buffer, int bufferoffset, int length) { throw new NotImplementedException(); } public Guid GetGuid(int i) { return (Guid) GetValue(i); } public short GetInt16(int i) { return (short) GetValue(i); } public int GetInt32(int i) { return (int) GetValue(i); } public long GetInt64(int i) { return (long) GetValue(i); } public float GetFloat(int i) { return (float) GetValue(i); } public double GetDouble(int i) { return (double) GetValue(i); } public string GetString(int i) { return (string) GetValue(i); } public decimal GetDecimal(int i) { return (decimal) GetValue(i); } public DateTime GetDateTime(int i) { return (DateTime) GetValue(i); } public IDataReader GetData(int i) { throw new NotImplementedException(); } public bool IsDBNull(int i) { return GetValue(i) == null; } public int FieldCount { get { return _properties.Count; } } object IDataRecord.this[int i] { get { return GetValue(i); } } object IDataRecord.this[string name] { get { return GetValue(GetOrdinal(name)); } } #endregion } |
Now you can iterate through IDataReader like you do with SqlDataReader.
1 2 3 4 5 6 7 |
using (IDataReader reader = new ListDataReader<Client>(clients)) { while (reader.Read()) { ... } } |
2. FastMember NuGet package
Thank you, Marc! it’s a good job https://github.com/mgravell/fast-member
You need to install NuGet package FastMember, add using FastMember; statement, and run this code.
1 2 3 4 5 6 7 |
using (var reader = ObjectReader.Create(clients)) { while (reader.Read()) { ... } } |