Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get an IDataReader from a typed List

I have a List<MyObject> with a million elements. (It is actually a SubSonic Collection but it is not loaded from the database).

I'm currently using SqlBulkCopy as follows:

private string FastInsertCollection(string tableName, DataTable tableData)
{
    string sqlConn = ConfigurationManager.ConnectionStrings[SubSonicConfig.DefaultDataProvider.ConnectionStringName].ConnectionString;
    using (SqlBulkCopy s = new SqlBulkCopy(sqlConn, SqlBulkCopyOptions.TableLock))
    {
        s.DestinationTableName = tableName;
        s.BatchSize = 5000;
        s.WriteToServer(tableData);
        s.BulkCopyTimeout = SprocTimeout;
        s.Close();
    }
    return sqlConn;
}

I use SubSonic's MyObjectCollection.ToDataTable() to build the DataTable from my collection. However, this duplicates objects in memory and is inefficient. I'd like to use the SqlBulkCopy.WriteToServer method that uses an IDataReader instead of a DataTable so that I don't duplicate my collection in memory.

What's the easiest way to get an IDataReader from my list? I suppose I could implement a custom data reader (like here http://blogs.microsoft.co.il/blogs/aviwortzel/archive/2008/05/06/implementing-sqlbulkcopy-in-linq-to-sql.aspx) , but there must be something simpler I can do without writing a bunch of generic code.

Edit: It does not appear that one can easily generate an IDataReader from a collection of objects. Accepting current answer even though I was hoping for something built into the framework.

like image 376
Jason Kealey Avatar asked Feb 13 '10 16:02

Jason Kealey


People also ask

How to create IDataReader In c#?

IDataReader reader = ObjectReader. Create(myEnumerable); //all columns IDataReader reader = ObjectReader. Create(myEnumerable, "Id", "Name", "Description"); In addition to abstracting away the creation logic, it also uses IL to get the property/field values, which is faster than reflection.

How to Get values from DataReader In c#?

To retrieve data using a DataReader, create an instance of the Command object, and then create a DataReader by calling Command. ExecuteReader to retrieve rows from a data source.

What is IDataReader In c#?

The IDataReader and IDataRecord interfaces allow an inheriting class to implement a DataReader class, which provides a means of reading one or more forward-only streams of result sets. For more information about DataReader classes, see Retrieving Data Using a DataReader.


3 Answers

Get the latest version from the code on this post

Nothing like code churn in plain sight: Here is a pretty complete implementation. You can instantiate an IDataReader over IList IEnumerable, IEnumerable (ergo IQueryable). There is no compelling reason to expose a generic type parameter on the reader and by omitting it, I can allow IEnumerable<'a> (anonymous types). See tests.

The source, less xmldocs, is short enough to include here with a couple tests. The rest of the source, with xmldocs, and tests is here under Salient.Data.


using System; using System.Linq; using NUnit.Framework;  namespace Salient.Data.Tests {     [TestFixture]     public class EnumerableDataReaderEFFixture     {         [Test]         public void TestEnumerableDataReaderWithIQueryableOfAnonymousType()         {             var ctx = new NorthwindEntities();              var q =                 ctx.Orders.Where(o => o.Customers.CustomerID == "VINET").Select(                     o =>                     new                         {                             o.OrderID,                             o.OrderDate,                             o.Customers.CustomerID,                             Total =                         o.Order_Details.Sum(                         od => od.Quantity*((float) od.UnitPrice - ((float) od.UnitPrice*od.Discount)))                         });              var r = new EnumerableDataReader(q);             while (r.Read())             {                 var values = new object[4];                 r.GetValues(values);                 Console.WriteLine("{0} {1} {2} {3}", values);             }         }     } } 

using System; using System.Collections; using System.Collections.Generic; using System.Data; using NUnit.Framework;  namespace Salient.Data.Tests {     public class DataObj     {         public string Name { get; set; }         public int Age { get; set; }     }      [TestFixture]     public class EnumerableDataReaderFixture     {          private static IEnumerable<DataObj> DataSource         {             get             {                 return new List<DataObj>                            {                                new DataObj {Name = "1", Age = 16},                                new DataObj {Name = "2", Age = 26},                                new DataObj {Name = "3", Age = 36},                                new DataObj {Name = "4", Age = 46}                            };             }         }          [Test]         public void TestIEnumerableCtor()         {             var r = new EnumerableDataReader(DataSource, typeof(DataObj));             while (r.Read())             {                 var values = new object[2];                 int count = r.GetValues(values);                 Assert.AreEqual(2, count);                  values = new object[1];                 count = r.GetValues(values);                 Assert.AreEqual(1, count);                  values = new object[3];                 count = r.GetValues(values);                 Assert.AreEqual(2, count);                  Assert.IsInstanceOf(typeof(string), r.GetValue(0));                 Assert.IsInstanceOf(typeof(int), r.GetValue(1));                  Console.WriteLine("Name: {0}, Age: {1}", r.GetValue(0), r.GetValue(1));             }         }          [Test]         public void TestIEnumerableOfAnonymousType()         {             // create generic list             Func<Type, IList> toGenericList =                 type => (IList)Activator.CreateInstance(typeof(List<>).MakeGenericType(new[] { type }));              // create generic list of anonymous type             IList listOfAnonymousType = toGenericList(new { Name = "1", Age = 16 }.GetType());              listOfAnonymousType.Add(new { Name = "1", Age = 16 });             listOfAnonymousType.Add(new { Name = "2", Age = 26 });             listOfAnonymousType.Add(new { Name = "3", Age = 36 });             listOfAnonymousType.Add(new { Name = "4", Age = 46 });              var r = new EnumerableDataReader(listOfAnonymousType);             while (r.Read())             {                 var values = new object[2];                 int count = r.GetValues(values);                 Assert.AreEqual(2, count);                  values = new object[1];                 count = r.GetValues(values);                 Assert.AreEqual(1, count);                  values = new object[3];                 count = r.GetValues(values);                 Assert.AreEqual(2, count);                  Assert.IsInstanceOf(typeof(string), r.GetValue(0));                 Assert.IsInstanceOf(typeof(int), r.GetValue(1));                  Console.WriteLine("Name: {0}, Age: {1}", r.GetValue(0), r.GetValue(1));             }         }          [Test]         public void TestIEnumerableOfTCtor()         {             var r = new EnumerableDataReader(DataSource);             while (r.Read())             {                 var values = new object[2];                 int count = r.GetValues(values);                 Assert.AreEqual(2, count);                  values = new object[1];                 count = r.GetValues(values);                 Assert.AreEqual(1, count);                  values = new object[3];                 count = r.GetValues(values);                 Assert.AreEqual(2, count);                  Assert.IsInstanceOf(typeof(string), r.GetValue(0));                 Assert.IsInstanceOf(typeof(int), r.GetValue(1));                  Console.WriteLine("Name: {0}, Age: {1}", r.GetValue(0), r.GetValue(1));             }         }          // remaining tests omitted for brevity     } } 

/*!  * Project: Salient.Data  * File   : EnumerableDataReader.cs  * http://spikes.codeplex.com  *  * Copyright 2010, Sky Sanders  * Dual licensed under the MIT or GPL Version 2 licenses.  * See LICENSE.TXT  * Date: Sat Mar 28 2010   */   using System; using System.Collections; using System.Collections.Generic;  namespace Salient.Data {     /// <summary>     /// Creates an IDataReader over an instance of IEnumerable&lt;> or IEnumerable.     /// Anonymous type arguments are acceptable.     /// </summary>     public class EnumerableDataReader : ObjectDataReader     {         private readonly IEnumerator _enumerator;         private readonly Type _type;         private object _current;          /// <summary>         /// Create an IDataReader over an instance of IEnumerable&lt;>.         ///          /// Note: anonymous type arguments are acceptable.         ///          /// Use other constructor for IEnumerable.         /// </summary>         /// <param name="collection">IEnumerable&lt;>. For IEnumerable use other constructor and specify type.</param>         public EnumerableDataReader(IEnumerable collection)         {             // THANKS DANIEL!             foreach (Type intface in collection.GetType().GetInterfaces())             {                 if (intface.IsGenericType && intface.GetGenericTypeDefinition() == typeof (IEnumerable<>))                 {                     _type = intface.GetGenericArguments()[0];                  }             }              if (_type ==null && collection.GetType().IsGenericType)             {                 _type = collection.GetType().GetGenericArguments()[0];              }               if (_type == null )             {                 throw new ArgumentException(                     "collection must be IEnumerable<>. Use other constructor for IEnumerable and specify type");             }              SetFields(_type);              _enumerator = collection.GetEnumerator();          }          /// <summary>         /// Create an IDataReader over an instance of IEnumerable.         /// Use other constructor for IEnumerable&lt;>         /// </summary>         /// <param name="collection"></param>         /// <param name="elementType"></param>         public EnumerableDataReader(IEnumerable collection, Type elementType)             : base(elementType)         {             _type = elementType;             _enumerator = collection.GetEnumerator();         }          /// <summary>         /// Helper method to create generic lists from anonymous type         /// </summary>         /// <param name="type"></param>         /// <returns></returns>         public static IList ToGenericList(Type type)         {             return (IList) Activator.CreateInstance(typeof (List<>).MakeGenericType(new[] {type}));         }          /// <summary>         /// Return the value of the specified field.         /// </summary>         /// <returns>         /// The <see cref="T:System.Object"/> which will contain the field value upon return.         /// </returns>         /// <param name="i">The index of the field to find.          /// </param><exception cref="T:System.IndexOutOfRangeException">The index passed was outside the range of 0 through <see cref="P:System.Data.IDataRecord.FieldCount"/>.          /// </exception><filterpriority>2</filterpriority>         public override object GetValue(int i)         {             if (i < 0 || i >= Fields.Count)             {                 throw new IndexOutOfRangeException();             }              return Fields[i].Getter(_current);         }          /// <summary>         /// Advances the <see cref="T:System.Data.IDataReader"/> to the next record.         /// </summary>         /// <returns>         /// true if there are more rows; otherwise, false.         /// </returns>         /// <filterpriority>2</filterpriority>         public override bool Read()         {             bool returnValue = _enumerator.MoveNext();             _current = returnValue ? _enumerator.Current : _type.IsValueType ? Activator.CreateInstance(_type) : null;             return returnValue;         }     } } 

// <copyright project="Salient.Data" file="ObjectDataReader.cs" company="Sky Sanders"> // This source is a Public Domain Dedication. // Please see http://spikes.codeplex.com/ for details.    // Attribution is appreciated // </copyright>  // <version>1.0</version>   using System; using System.Collections.Generic; using System.Data; using Salient.Reflection;  namespace Salient.Data {     public abstract class ObjectDataReader : IDataReader     {         protected bool Closed;         protected IList<DynamicProperties.Property> Fields;          protected ObjectDataReader()         {         }          protected ObjectDataReader(Type elementType)         {             SetFields(elementType);             Closed = false;         }          #region IDataReader Members          public abstract object GetValue(int i);         public abstract bool Read();          #endregion          #region Implementation of IDataRecord          public int FieldCount         {             get { return Fields.Count; }         }          public virtual int GetOrdinal(string name)         {             for (int i = 0; i < Fields.Count; i++)             {                 if (Fields[i].Info.Name == name)                 {                     return i;                 }             }              throw new IndexOutOfRangeException("name");         }          object IDataRecord.this[int i]         {             get { return GetValue(i); }         }          public virtual bool GetBoolean(int i)         {             return (Boolean) GetValue(i);         }         public virtual byte GetByte(int i)         {             return (Byte) GetValue(i);         }          public virtual char GetChar(int i)         {             return (Char) GetValue(i);         }          public virtual DateTime GetDateTime(int i)         {             return (DateTime) GetValue(i);         }          public virtual decimal GetDecimal(int i)         {             return (Decimal) GetValue(i);         }          public virtual double GetDouble(int i)         {             return (Double) GetValue(i);         }          public virtual Type GetFieldType(int i)         {             return Fields[i].Info.PropertyType;         }          public virtual float GetFloat(int i)         {             return (float) GetValue(i);         }          public virtual Guid GetGuid(int i)         {             return (Guid) GetValue(i);         }          public virtual short GetInt16(int i)         {             return (Int16) GetValue(i);         }          public virtual int GetInt32(int i)         {             return (Int32) GetValue(i);         }          public virtual long GetInt64(int i)         {             return (Int64) GetValue(i);         }          public virtual string GetString(int i)         {             return (string) GetValue(i);         }          public virtual bool IsDBNull(int i)         {             return GetValue(i) == null;         }          object IDataRecord.this[string name]         {             get { return GetValue(GetOrdinal(name)); }         }           public virtual string GetDataTypeName(int i)         {             return GetFieldType(i).Name;         }           public virtual string GetName(int i)         {             if (i < 0 || i >= Fields.Count)             {                 throw new IndexOutOfRangeException("name");             }             return Fields[i].Info.Name;         }          public virtual int GetValues(object[] values)         {             int i = 0;             for (; i < Fields.Count; i++)             {                 if (values.Length <= i)                 {                     return i;                 }                 values[i] = GetValue(i);             }             return i;         }          public virtual IDataReader GetData(int i)         {             // need to think about this one             throw new NotImplementedException();         }          public virtual long GetBytes(int i, long fieldOffset, byte[] buffer, int bufferoffset, int length)         {             // need to keep track of the bytes got for each record - more work than i want to do right now             // http://msdn.microsoft.com/en-us/library/system.data.idatarecord.getbytes.aspx             throw new NotImplementedException();         }          public virtual long GetChars(int i, long fieldoffset, char[] buffer, int bufferoffset, int length)         {             // need to keep track of the bytes got for each record - more work than i want to do right now             // http://msdn.microsoft.com/en-us/library/system.data.idatarecord.getchars.aspx             throw new NotImplementedException();         }          #endregion          #region Implementation of IDataReader          public virtual void Close()         {             Closed = true;         }           public virtual DataTable GetSchemaTable()         {             var dt = new DataTable();             foreach (DynamicProperties.Property field in Fields)             {                 dt.Columns.Add(new DataColumn(field.Info.Name, field.Info.PropertyType));             }             return dt;         }          public virtual bool NextResult()         {             throw new NotImplementedException();         }           public virtual int Depth         {             get { throw new NotImplementedException(); }         }          public virtual bool IsClosed         {             get { return Closed; }         }          public virtual int RecordsAffected         {             get             {                 // assuming select only?                 return -1;             }         }          #endregion          #region Implementation of IDisposable          public virtual void Dispose()         {             Fields = null;         }          #endregion          protected void SetFields(Type elementType)         {             Fields = DynamicProperties.CreatePropertyMethods(elementType);         }     } } 

// <copyright project="Salient.Reflection" file="DynamicProperties.cs" company="Sky Sanders"> // This source is a Public Domain Dedication. // Please see http://spikes.codeplex.com/ for details.    // Attribution is appreciated // </copyright>  // <version>1.0</version> using System; using System.Collections.Generic; using System.Reflection; using System.Reflection.Emit;  namespace Salient.Reflection {     /// <summary>     /// Gets IL setters and getters for a property.     ///      /// started with http://jachman.wordpress.com/2006/08/22/2000-faster-using-dynamic-method-calls/     /// </summary>     public static class DynamicProperties     {         #region Delegates          public delegate object GenericGetter(object target);          public delegate void GenericSetter(object target, object value);          #endregion          public static IList<Property> CreatePropertyMethods(Type T)         {             var returnValue = new List<Property>();              foreach (PropertyInfo prop in T.GetProperties())             {                 returnValue.Add(new Property(prop));             }             return returnValue;         }           public static IList<Property> CreatePropertyMethods<T>()         {             var returnValue = new List<Property>();              foreach (PropertyInfo prop in typeof (T).GetProperties())             {                 returnValue.Add(new Property(prop));             }             return returnValue;         }           /// <summary>         /// Creates a dynamic setter for the property         /// </summary>         /// <param name="propertyInfo"></param>         /// <returns></returns>         public static GenericSetter CreateSetMethod(PropertyInfo propertyInfo)         {             /*             * If there's no setter return null             */             MethodInfo setMethod = propertyInfo.GetSetMethod();             if (setMethod == null)                 return null;              /*             * Create the dynamic method             */             var arguments = new Type[2];             arguments[0] = arguments[1] = typeof (object);              var setter = new DynamicMethod(                 String.Concat("_Set", propertyInfo.Name, "_"),                 typeof (void), arguments, propertyInfo.DeclaringType);             ILGenerator generator = setter.GetILGenerator();             generator.Emit(OpCodes.Ldarg_0);             generator.Emit(OpCodes.Castclass, propertyInfo.DeclaringType);             generator.Emit(OpCodes.Ldarg_1);              if (propertyInfo.PropertyType.IsClass)                 generator.Emit(OpCodes.Castclass, propertyInfo.PropertyType);             else                 generator.Emit(OpCodes.Unbox_Any, propertyInfo.PropertyType);              generator.EmitCall(OpCodes.Callvirt, setMethod, null);             generator.Emit(OpCodes.Ret);              /*             * Create the delegate and return it             */             return (GenericSetter) setter.CreateDelegate(typeof (GenericSetter));         }           /// <summary>         /// Creates a dynamic getter for the property         /// </summary>         /// <param name="propertyInfo"></param>         /// <returns></returns>         public static GenericGetter CreateGetMethod(PropertyInfo propertyInfo)         {             /*             * If there's no getter return null             */             MethodInfo getMethod = propertyInfo.GetGetMethod();             if (getMethod == null)                 return null;              /*             * Create the dynamic method             */             var arguments = new Type[1];             arguments[0] = typeof (object);              var getter = new DynamicMethod(                 String.Concat("_Get", propertyInfo.Name, "_"),                 typeof (object), arguments, propertyInfo.DeclaringType);             ILGenerator generator = getter.GetILGenerator();             generator.DeclareLocal(typeof (object));             generator.Emit(OpCodes.Ldarg_0);             generator.Emit(OpCodes.Castclass, propertyInfo.DeclaringType);             generator.EmitCall(OpCodes.Callvirt, getMethod, null);              if (!propertyInfo.PropertyType.IsClass)                 generator.Emit(OpCodes.Box, propertyInfo.PropertyType);              generator.Emit(OpCodes.Ret);              /*             * Create the delegate and return it             */             return (GenericGetter) getter.CreateDelegate(typeof (GenericGetter));         }          #region Nested type: Property          public class Property         {             public GenericGetter Getter;             public PropertyInfo Info;             public GenericSetter Setter;              public Property(PropertyInfo info)             {                 Info = info;                 Setter = CreateSetMethod(info);                 Getter = CreateGetMethod(info);             }         }          #endregion          ///// <summary>         ///// An expression based Getter getter found in comments. untested.         ///// Q: i don't see a reciprocal setter expression?         ///// </summary>         ///// <typeparam name="T"></typeparam>         ///// <param name="propName"></param>         ///// <returns></returns>         //public static Func<T> CreateGetPropValue<T>(string propName)         //{         //    var param = Expression.Parameter(typeof(object), "container");         //    var func = Expression.Lambda(         //    Expression.Convert(Expression.PropertyOrField(Expression.Convert(param, typeof(T)), propName), typeof(object)), param);         //    return (Func<T>)func.Compile();         //}     } } 

like image 79
14 revs Avatar answered Oct 08 '22 16:10

14 revs


You can use Marc Gravell's FastMember:

IDataReader reader = ObjectReader.Create(myEnumerable); //all columns IDataReader reader = ObjectReader.Create(myEnumerable, "Id", "Name", "Description"); 

In addition to abstracting away the creation logic, it also uses IL to get the property/field values, which is faster than reflection.

like image 24
Arithmomaniac Avatar answered Oct 08 '22 15:10

Arithmomaniac


Thanks to Sky Sanders for his initial answer, it was a big help.

I wrote a generic version of Sky Sanders' EnumerableDataReader but I made the following changes:

  • The reader now support both public properties and public fields.
  • The constructor now takes a list of field/property names that should be available in the DataReader. This allows limiting and defining the order of the properties and/or fields so they match the layout of your table. If not list of fields is given, only one field is available, namely the object itself, this way the reader can be used with, for example, a List.
  • The original code using the DynamicMethod and the ILGenerator did not work with interfaces and required a code change for structs, so I changed it using Linq.Expressions which seems to work in all cases (and as a bonus it's more compact). The performance for fields is the same as with the ILGenerator, the performance for properties seems marginally slower than with the ILGenerator, but still massively faster then using reflection.
  • Maybe this is not a plus-point, but I put all the code in one class (with some helper sub-classes), removing all the unnecessary code. (I'm not saying the code was not useful, I just didn't need it in my case.)

I hope this helps and if you have any remarks, corrections or improvements, please say so :)

/// <summary>
/// IDataReader that can be used for "reading" an IEnumerable<T> collection
/// </summary>
public class EnumerableDataReader<T> : IDataReader
{
    /// <summary>
    /// Constructor
    /// </summary>
    /// <param name="collection">The collection to be read</param>
    /// <param name="fields">The list of public field/properties to read from each T (in order), OR if no fields are given only one field will be available: T itself</param>
    public EnumerableDataReader(IEnumerable<T> collection, params string[] fields)
    {
        if (collection == null)
            throw new ArgumentNullException("collection");

        m_Enumerator = collection.GetEnumerator();

        if (m_Enumerator == null)
            throw new NullReferenceException("collection does not implement GetEnumerator");

        SetFields(fields);
    }
    private IEnumerator<T> m_Enumerator;
    private T m_Current = default(T);
    private bool m_EnumeratorState = false;

    private void SetFields(ICollection<string> fields)
    {
        if (fields.Count > 0)
        {
            Type type = typeof(T);
            foreach (string field in fields)
            {
                PropertyInfo pInfo = type.GetProperty(field);
                if (pInfo != null)
                    m_Fields.Add(new Property(pInfo));
                else
                {
                    FieldInfo fInfo = type.GetField(field);
                    if (fInfo != null)
                        m_Fields.Add(new Field(fInfo));
                    else
                        throw new NullReferenceException(string.Format("EnumerableDataReader<T>: Missing property or field '{0}' in Type '{1}'.", field, type.Name));
                }
            }
        }
        else
            m_Fields.Add(new Self());
    }
    private List<BaseField> m_Fields = new List<BaseField>();

    #region IDisposable Members
    public void Dispose()
    {
        if (m_Enumerator != null)
        {
            m_Enumerator.Dispose();
            m_Enumerator = null;
            m_Current = default(T);
            m_EnumeratorState = false;
        }
        m_Closed = true;
    }
    #endregion

    #region IDataReader Members
    public void Close()
    {
        m_Closed = true;
    }
    private bool m_Closed = false;

    public int Depth
    {
        get { return 0; }
    }

    public DataTable GetSchemaTable()
    {
        var dt = new DataTable();
        foreach (BaseField field in m_Fields)
        {
            dt.Columns.Add(new DataColumn(field.Name, field.Type));
        }
        return dt;
    }

    public bool IsClosed
    {
        get { return m_Closed; }
    }

    public bool NextResult()
    {
        return false;
    }

    public bool Read()
    {
        if (IsClosed)
            throw new InvalidOperationException("DataReader is closed");
        m_EnumeratorState = m_Enumerator.MoveNext();
        m_Current = m_EnumeratorState ? m_Enumerator.Current : default(T);
        return m_EnumeratorState;
    }

    public int RecordsAffected
    {
        get { return -1; }
    }
    #endregion

    #region IDataRecord Members
    public int FieldCount
    {
        get { return m_Fields.Count; }
    }

    public Type GetFieldType(int i)
    {
        if (i < 0 || i >= m_Fields.Count)
            throw new IndexOutOfRangeException();
        return m_Fields[i].Type;
    }

    public string GetDataTypeName(int i)
    {
        return GetFieldType(i).Name;
    }

    public string GetName(int i)
    {
        if (i < 0 || i >= m_Fields.Count)
            throw new IndexOutOfRangeException();
        return m_Fields[i].Name;
    }

    public int GetOrdinal(string name)
    {
        for (int i = 0; i < m_Fields.Count; i++)
            if (m_Fields[i].Name == name)
                return i;
        throw new IndexOutOfRangeException("name");
    }

    public bool IsDBNull(int i)
    {
        return GetValue(i) == null;
    }

    public object this[string name]
    {
        get { return GetValue(GetOrdinal(name)); }
    }

    public object this[int i]
    {
        get { return GetValue(i); }
    }

    public object GetValue(int i)
    {
        if (IsClosed || !m_EnumeratorState)
            throw new InvalidOperationException("DataReader is closed or has reached the end of the enumerator");
        if (i < 0 || i >= m_Fields.Count)
            throw new IndexOutOfRangeException();
        return m_Fields[i].GetValue(m_Current);
    }

    public int GetValues(object[] values)
    {
        int length = Math.Min(m_Fields.Count, values.Length);
        for (int i = 0; i < length; i++)
            values[i] = GetValue(i);
        return length;
    }

    public bool GetBoolean(int i) { return (bool)GetValue(i); }
    public byte GetByte(int i) { return (byte)GetValue(i); }
    public char GetChar(int i) { return (char)GetValue(i); }
    public DateTime GetDateTime(int i) { return (DateTime)GetValue(i); }
    public decimal GetDecimal(int i) { return (decimal)GetValue(i); }
    public double GetDouble(int i) { return (double)GetValue(i); }
    public float GetFloat(int i) { return (float)GetValue(i); }
    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 string GetString(int i) { return (string)GetValue(i); }

    public long GetBytes(int i, long fieldOffset, byte[] buffer, int bufferoffset, int length) { throw new NotSupportedException(); }
    public long GetChars(int i, long fieldoffset, char[] buffer, int bufferoffset, int length) { throw new NotSupportedException(); }
    public IDataReader GetData(int i) { throw new NotSupportedException(); }
    #endregion

    #region Helper Classes
    private abstract class BaseField
    {
        public abstract Type Type { get; }
        public abstract string Name { get; }
        public abstract object GetValue(T instance);

        protected static void AddGetter(Type classType, string fieldName, Func<T, object> getter)
        {
            m_GetterDictionary.Add(string.Concat(classType.FullName, fieldName), getter);
        }

        protected static Func<T, object> GetGetter(Type classType, string fieldName)
        {
            Func<T, object> getter = null;
            if (m_GetterDictionary.TryGetValue(string.Concat(classType.FullName, fieldName), out getter))
                return getter;
            return null;
        }
        private static Dictionary<string, Func<T, object>> m_GetterDictionary = new Dictionary<string, Func<T, object>>();
    }

    private class Property : BaseField
    {
        public Property(PropertyInfo info)
        {
            m_Info = info;
            m_DynamicGetter = CreateGetMethod(info);
        }
        private PropertyInfo m_Info;
        private Func<T, object> m_DynamicGetter;

        public override Type Type { get { return m_Info.PropertyType; } }
        public override string Name { get { return m_Info.Name; } }

        public override object GetValue(T instance)
        {
            //return m_Info.GetValue(instance, null); // Reflection is slow
            return m_DynamicGetter(instance);
        }

        // Create dynamic method for faster access instead via reflection
        private Func<T, object> CreateGetMethod(PropertyInfo propertyInfo)
        {
            Type classType = typeof(T);
            Func<T, object> dynamicGetter = GetGetter(classType, propertyInfo.Name);
            if (dynamicGetter == null)
            {
                ParameterExpression instance = Expression.Parameter(classType);
                MemberExpression property = Expression.Property(instance, propertyInfo);
                UnaryExpression convert = Expression.Convert(property, typeof(object));
                dynamicGetter = (Func<T, object>)Expression.Lambda(convert, instance).Compile();
                AddGetter(classType, propertyInfo.Name, dynamicGetter);
            }

            return dynamicGetter;
        }
    }

    private class Field : BaseField
    {
        public Field(FieldInfo info)
        {
            m_Info = info;
            m_DynamicGetter = CreateGetMethod(info);
        }
        private FieldInfo m_Info;
        private Func<T, object> m_DynamicGetter;

        public override Type Type { get { return m_Info.FieldType; } }
        public override string Name { get { return m_Info.Name; } }

        public override object GetValue(T instance)
        {
            //return m_Info.GetValue(instance); // Reflection is slow
            return m_DynamicGetter(instance);
        }

        // Create dynamic method for faster access instead via reflection
        private Func<T, object> CreateGetMethod(FieldInfo fieldInfo)
        {
            Type classType = typeof(T);
            Func<T, object> dynamicGetter = GetGetter(classType, fieldInfo.Name);
            if (dynamicGetter == null)
            {
                ParameterExpression instance = Expression.Parameter(classType);
                MemberExpression property = Expression.Field(instance, fieldInfo);
                UnaryExpression convert = Expression.Convert(property, typeof(object));
                dynamicGetter = (Func<T, object>)Expression.Lambda(convert, instance).Compile();
                AddGetter(classType, fieldInfo.Name, dynamicGetter);
            }

            return dynamicGetter;
        }
    }

    private class Self : BaseField
    {
        public Self()
        {
            m_Type = typeof(T);
        }
        private Type m_Type;

        public override Type Type { get { return m_Type; } }
        public override string Name { get { return string.Empty; } }
        public override object GetValue(T instance) { return instance; }
    }
    #endregion
}
like image 37
Marc Avatar answered Oct 08 '22 17:10

Marc