I hope this is not to complicated. I do have it working, but I wonder if that is the best it can be:
I want to use the SQL Bulk Copy class, and more specific the overload that takes an IDataReader. I have my data in several List<> so I need a "translator". I found one that does it by using extension methods - very elegant, I think! It is listed here: http://www.differentpla.net/content/2011/01/converting-ienumerablet-idatareader I think this is an excellent base to build on?!
The code shown there is not complete, some unnecessary overloads that are never called are missing. I added them as NotImplementedException. The completed version can be found at the end.
You use that class by calling the AsDataReader method on your IEnumerable<> or List<>, and pass it the number of columns in your data, and a function with which to step over the columns, like this:
List<NodeDB> myList = new List<NodeDB>();
myList.Add(myNode1);
myBulk.WriteToServer(myList.AsDataReader(3, NodeDB.GetValue));
The class NodeDB has some fields, and a static(!?) method GetValue where it gets the currently iterated position (!?) and column, and returns the appropriate item:
static object GetValue(NodeDB nodeDB, int i) {
object obj = null;
switch (i) {
case 0:
obj = nodeDB.ID;
break;
case 1:
obj = nodeDB.Latitude;
break;
case 2:
obj = nodeDB.Longitude;
break;
}
return obj;
}
Having a static method in the class that takes an instance of itself sounds strange, I wonder if there is a better way. But even more important: I do have several classes like NodeDB, all of them with their specific GetValues, organized into List<>, one List<> for each type.
I would like to pass down all of them to ONE generic bulk write method.
Currently, I do that by passing the different Lists as a IEnumberable, and then do a cast back to what it was after comparing them one by one:
if (list is List<NodeDB>) {
} else if ( ....
Within each of these different cases I need to address the correct function too, that is repetive and duplicate and tedious - is there a way to derive the correct Func argument from the List passed?
How could I change my classes (to derive from a common class which has an abstract GetValues)? All List<> could be given as List then, I think, and all calls to GetValues would reach it's targets. I tried to do that, but the static method cannot be abstract in a base class.
Or I can I add another parameter, a TYPE probably to then call the Func against that? Or change the IDataReader Extension?
As I said, I am lost... Thanks for your help! Ralf
Here the complete IDataReader Extension to IEnumerable with all interface implementations:
using System;
using System.Collections.Generic;
using System.Data;
static class DataReaderExtensions {
public static IDataReader AsDataReader<TSource>(this IEnumerable<TSource> source, int fieldCount, Func<TSource, int, object> getValue) {
return new EnumerableDataReader<TSource>(source.GetEnumerator(), fieldCount, getValue);
//return EnumerableDataReader.Create(source, fieldCount, getValue);
}
}
//internal static class EnumerableDataReader {
// public static IDataReader Create<TSource>(IEnumerable<TSource> source, int fieldCount, Func<TSource, int, object> getValue) {
// return new EnumerableDataReader<TSource>(source.GetEnumerator(), fieldCount, getValue);
// }
//}
internal class EnumerableDataReader<TSource> : IDataReader {
private readonly IEnumerator<TSource> _source;
private readonly int _fieldCount;
private readonly Func<TSource, int, object> _getValue;
internal EnumerableDataReader(IEnumerator<TSource> source, int fieldCount, Func<TSource, int, object> getValue) {
_source = source;
_getValue = getValue;
_fieldCount = fieldCount;
}
public void Dispose() {
// Nothing.
}
public string GetName(int i) {
throw new NotImplementedException();
}
public string GetDataTypeName(int i) {
throw new NotImplementedException();
}
public Type GetFieldType(int i) {
throw new NotImplementedException();
}
public object GetValue(int i) {
return _getValue(_source.Current, i);
}
public int GetValues(object[] values) {
throw new NotImplementedException();
}
public int GetOrdinal(string name) {
throw new NotImplementedException();
}
public int FieldCount {
get { return _fieldCount; }
}
object IDataRecord.this[int i] {
get { throw new NotImplementedException(); }
}
object IDataRecord.this[string name] {
get { throw new NotImplementedException(); }
}
public void Close() {
throw new NotImplementedException();
}
public DataTable GetSchemaTable() {
throw new NotImplementedException();
}
public bool NextResult() {
throw new NotImplementedException();
}
public bool Read() {
return _source.MoveNext();
}
public int Depth { get; private set; }
public bool IsClosed { get; private set; }
public int RecordsAffected { get; private set; }
public bool IsDBNull(int i) {
throw new NotImplementedException();
}
public IDataReader GetData(int i) {
throw new NotImplementedException();
}
public DateTime GetDateTime(int i) {
throw new NotImplementedException();
}
public Decimal GetDecimal(int i) {
throw new NotImplementedException();
}
public String GetString(int i) {
throw new NotImplementedException();
}
public Double GetDouble(int i) {
throw new NotImplementedException();
}
public float GetFloat(int i) {
throw new NotImplementedException();
}
public Int64 GetInt64(int i) {
throw new NotImplementedException();
}
public Int32 GetInt32(int i) {
throw new NotImplementedException();
}
public Int16 GetInt16(int i) {
throw new NotImplementedException();
}
public long GetChars(int i, long fieldoffset, char[] buffer, int bufferoffset, int length) {
throw new NotImplementedException();
}
public Guid GetGuid(int i) {
throw new NotImplementedException();
}
public long GetBytes(int i, long fieldOffset, byte[] buffer, int bufferoffset, int length) {
throw new NotImplementedException();
}
public Char GetChar(int i) {
throw new NotImplementedException();
}
public Byte GetByte(int i) {
throw new NotImplementedException();
}
public Boolean GetBoolean(int i) {
throw new NotImplementedException();
}
}
One possible solution is to make
GetValue
non-static and pass it like this:The
GetValue
method will look like this: