34


17

型付きリストからIDataReaderを取得します

100万個の要素を持つ「リスト」があります。 (実際にはSubSonicコレクションですが、データベースからはロードされません)。

私は現在、次のように* SqlBulkCopy *を使用しています。

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;
}

SubSonicのMyObjectCollection.ToDataTable()を使用して、コレクションからDataTableを構築します。 ただし、これはメモリ内のオブジェクトを複製し、非効率的です。 コレクションをメモリに複製しないように、DataTableではなくIDataReaderを使用するSqlBulkCopy.WriteToServerメソッドを使用したいと思います。

リストからIDataReaderを取得する最も簡単な方法は何ですか? カスタムデータリーダーを実装できると思います(http://blogs.microsoft.co.il/blogs/aviwortzel/archive/2008/05/06/implementing-sqlbulkcopy-in-linq-to-sql.aspxなど) 、しかし、多くの汎用コードを書かずにもっと簡単なことができるはずです。

編集:オブジェクトのコレクションからIDataReaderを簡単に生成できるようには見えません。 フレームワークに組み込まれた何かを望んでいたにもかかわらず、現在の答えを受け入れました。

3 Answer


19


見た目ではコードチャーンのようなものは何もありません。これは非常に完全な実装です。 IList IEnumerable、IEnumerable(Igoable IQueryable)でIDataReaderをインスタンス化できます。 リーダーでジェネリック型パラメーターを公開する説得力のある理由はなく、省略してIEnumerable <'a>(匿名型)を許可できます。 テストを参照してください。

ソース(xmldocsを除く)は、ここにいくつかのテストを含めるのに十分短いです。 xmldocs、およびテストを含むソースの残りは、Salient.Dataの下のhttp://spikes.codeplex.com/SourceControl/list/changesets[here]です。

'' '' '

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 DataSource
        {
            get
            {
                return new List
                           {
                               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 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
{
    ///
    /// Creates an IDataReader over an instance of IEnumerable<> or IEnumerable.
    /// Anonymous type arguments are acceptable.
    ///
    public class EnumerableDataReader : ObjectDataReader
    {
        private readonly IEnumerator _enumerator;
        private readonly Type _type;
        private object _current;

        ///
        /// Create an IDataReader over an instance of IEnumerable<>.
        ///
        /// Note: anonymous type arguments are acceptable.
        ///
        /// Use other constructor for IEnumerable.
        ///
        /// IEnumerable<>. For IEnumerable use other constructor and specify type.
        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();

        }

        ///
        /// Create an IDataReader over an instance of IEnumerable.
        /// Use other constructor for IEnumerable<>
        ///
        ///
        ///
        public EnumerableDataReader(IEnumerable collection, Type elementType)
            : base(elementType)
        {
            _type = elementType;
            _enumerator = collection.GetEnumerator();
        }

        ///
        /// Helper method to create generic lists from anonymous type
        ///
        ///
        ///
        public static IList ToGenericList(Type type)
        {
            return (IList) Activator.CreateInstance(typeof (List<>).MakeGenericType(new[] {type}));
        }

        ///
        /// Return the value of the specified field.
        ///
        ///
        /// The  which will contain the field value upon return.
        ///
        /// The index of the field to find.
        /// The index passed was outside the range of 0 through .
        /// 2
        public override object GetValue(int i)
        {
            if (i < 0 || i >= Fields.Count)
            {
                throw new IndexOutOfRangeException();
            }

            return Fields[i].Getter(_current);
        }

        ///
        /// Advances the  to the next record.
        ///
        ///
        /// true if there are more rows; otherwise, false.
        ///
        /// 2
        public override bool Read()
        {
            bool returnValue = _enumerator.MoveNext();
            _current = returnValue ? _enumerator.Current : _type.IsValueType ? Activator.CreateInstance(_type) : null;
            return returnValue;
        }
    }
}

'' '' '

//
// This source is a Public Domain Dedication.
// Please see http://spikes.codeplex.com/ for details.
// Attribution is appreciated
//
// 1.0


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 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);
        }
    }
}

'' '' '

//
// This source is a Public Domain Dedication.
// Please see http://spikes.codeplex.com/ for details.
// Attribution is appreciated
//
// 1.0
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;

namespace Salient.Reflection
{
    ///
    /// Gets IL setters and getters for a property.
    ///
    /// started with http://jachman.wordpress.com/2006/08/22/2000-faster-using-dynamic-method-calls/
    ///
    public static class DynamicProperties
    {
        #region Delegates

        public delegate object GenericGetter(object target);

        public delegate void GenericSetter(object target, object value);

        #endregion

        public static IList CreatePropertyMethods(Type T)
        {
            var returnValue = new List();

            foreach (PropertyInfo prop in T.GetProperties())
            {
                returnValue.Add(new Property(prop));
            }
            return returnValue;
        }


        public static IList CreatePropertyMethods()
        {
            var returnValue = new List();

            foreach (PropertyInfo prop in typeof (T).GetProperties())
            {
                returnValue.Add(new Property(prop));
            }
            return returnValue;
        }


        ///
        /// Creates a dynamic setter for the property
        ///
        ///
        ///
        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));
        }


        ///
        /// Creates a dynamic getter for the property
        ///
        ///
        ///
        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

        /////
        ///// An expression based Getter getter found in comments. untested.
        ///// Q: i don't see a reciprocal setter expression?
        /////
        /////
        /////
        /////
        //public static Func CreateGetPropValue(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)func.Compile();
        //}
    }
}

'' '' '


13


Marc Gravellのhttps://github.com/mgravell/fast-member/[FastMember]を使用できます。

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

作成ロジックを抽象化することに加えて、ILを使用してプロパティ/フィールド値を取得します。これはリフレクションよりも高速です。


10


最初の回答をしてくれたSky Sandersに感謝します。

Sky SandersのEnumerableDataReaderの汎用バージョンを作成しましたが、次の変更を加えました。

  • リーダーは、パブリックプロパティとパブリックフィールドの両方をサポートするようになりました。

  • コンストラクターは、フィールド/プロパティ名のリストを取得します。 DataReaderで利用可能です。 これにより、テーブルのレイアウトに一致するように、プロパティやフィールドの順序を制限および定義できます。 フィールドのリストが指定されていない場合、使用できるフィールドは1つだけです。つまり、オブジェクト自体です。これにより、たとえばListでリーダーを使用できます。

  • DynamicMethodおよびILGeneratorを使用した元のコードは インターフェイスを操作し、構造体のコードを変更する必要があったため、すべてのケースで機能するように見えるLinq.Expressionsを使用して変更しました(おまけに、よりコンパクトになりました)。 フィールドのパフォーマンスはILGeneratorの場合と同じです。プロパティのパフォーマンスはILGeneratorの場合よりもわずかに遅いように見えますが、リフレクションを使用する場合よりも大幅に高速です。

  • たぶんこれはプラスポイントではありませんが、私はすべてのコードを1つのクラスに入れます (ヘルパーサブクラスを使用)、不要なコードをすべて削除します。 (コードが役に立たなかったと言っているわけではありません。私の場合は必要ありませんでした。)

これがお役に立てば幸いです。ご意見、修正、改善がありましたら、その旨お伝えください:)

///
/// IDataReader that can be used for "reading" an IEnumerable collection
///
public class EnumerableDataReader : IDataReader
{
    ///
    /// Constructor
    ///
    /// The collection to be read
    /// 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
    public EnumerableDataReader(IEnumerable 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 m_Enumerator;
    private T m_Current = default(T);
    private bool m_EnumeratorState = false;

    private void SetFields(ICollection 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: Missing property or field '{0}' in Type '{1}'.", field, type.Name));
                }
            }
        }
        else
            m_Fields.Add(new Self());
    }
    private List m_Fields = new List();

    #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 getter)
        {
            m_GetterDictionary.Add(string.Concat(classType.FullName, fieldName), getter);
        }

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

    private class Property : BaseField
    {
        public Property(PropertyInfo info)
        {
            m_Info = info;
            m_DynamicGetter = CreateGetMethod(info);
        }
        private PropertyInfo m_Info;
        private Func 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 CreateGetMethod(PropertyInfo propertyInfo)
        {
            Type classType = typeof(T);
            Func 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)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 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 CreateGetMethod(FieldInfo fieldInfo)
        {
            Type classType = typeof(T);
            Func 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)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
}