2010-02-13 44 views
32

我有一個List<MyObject>有一百萬個元素。 (它實際上是一個SubSonic集合,但它不從數據庫加載)。我目前使用SqlBulkCopy的作爲從一個輸入列表中獲取一個IDataReader

如下:

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

我用亞音速的MyObjectCollection.ToDataTable()從我的收藏建立數據表。但是,這會在內存中複製對象並且效率低下。我想使用SqlBulkCopy.WriteToServer方法使用IDataReader而不是DataTable,這樣我就不會在內存中複製我的集合。

從列表中獲取IDataReader的最簡單方法是什麼?我想我可以實現一個自定義數據讀取器(如http://blogs.microsoft.co.il/blogs/aviwortzel/archive/2008/05/06/implementing-sqlbulkcopy-in-linq-to-sql.aspx),但是我必須做一些簡單的工作,而不需要編寫大量的通用代碼。

編輯: 似乎沒有人可以從一組對象中輕鬆生成IDataReader。 即使我希望在框架中內置某些東西,也能接受當前的答案。

+0

引起我的好奇心,所以我尖刺IList datareader。看看能否幫助你。 – 2010-02-13 18:12:20

+0

看到我最近的評論... – 2010-02-14 23:51:46

+0

IEnumerable 很容易。經過輕微測試。 – 2010-02-15 19:24:08

回答

18

獲取有關this post

沒有像在衆目睽睽代碼改動從代碼的最新版本:這是一個非常完整的實現。您可以通過IList IEnumerable,IEnumerable(ergo IQueryable)實例化一個IDataReader。在閱讀器上公開一個泛型類型參數並省略它是沒有說服力的理由,我可以允許IEnumerable <'a>(匿名類型)。見測試。

xmldocs的源代碼很短,足以包含幾個測試。其餘的來源,xmldocs和測試是在Salient.Data下的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<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(); 
     //} 
    } 
} 

+2

看起來這個代碼完全符合我的需求,但我很驚訝,沒有一個解決方案不需要創建自己的數據讀取器。 – 2010-02-13 23:30:52

+0

好讓我知道它是否適用於你。順便說一下亞音速集合;他們確實實施IList ? – 2010-02-13 23:50:18

+0

是的;你可以做.GetList()在亞音速集合到內部IList 2010-02-14 15:24:15

10

感謝Sky Sanders對他的初步答覆,這是一個很大的幫助。

我寫了天空桑德斯的EnumerableDataReader的通用版本,但我做了以下修改:

  • 讀者現在支持公共屬性和公共領域。
  • 構造函數現在獲取DataReader中應該可用的字段/屬性名稱的列表。這允許限制和定義屬性和/或字段的順序,以便它們匹配表格的佈局。如果沒有給出字段列表,則只有一個字段可用,即對象本身,這樣讀者可以與例如List一起使用。
  • 使用DynamicMethod和ILGenerator的原始代碼不能與接口一起工作,並且需要對結構進行代碼更改,所以我使用Linq.Expressions對其進行了更改,似乎在所有情況下都可以工作(並且它更加緊湊)。字段的性能與ILGenerator的性能相同,屬性的性能似乎比ILGenerator稍慢,但仍然大大快於使用反射。
  • 也許這不是一個加點,但我把所有的代碼放在一個類中(帶有一些輔助子類),刪除所有不必要的代碼。 (我不是說代碼沒有用,我只是在我的情況下不需要它。)

我希望這會有所幫助,如果您有任何評論,更正或改進,請說出來:)

/// <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 
} 
+0

偉大的實施!這一定是需要一些時間和耐心的。感謝分享 – 2015-02-02 15:19:32

10

您可以使用馬克·Gravell的FastMember

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

除了抽象掉的創作邏輯,它也使用IL獲取屬性/字段值,它比反射快。

+0

這是SqlBulkCopy的現代版伴侶。爲什麼在NuGet上有一個對象數據讀取器時自動滾動。只需在NuGet上搜索「FastMember」即可。謝謝! – wezzix 2016-03-03 11:59:16

+0

這是最好和最簡單的使用庫之一。我將它添加到ObjectReader構造函數中,以便保留原始類的順序:'members = type.GetProperties()。Select(p => p.Name).Union(typeMembers.Select(m => m.Name))。 。不同的()ToArray的();' – nh43de 2016-04-07 14:58:32

相關問題