Get an IDataReader from a typed List

前端 未结 3 1491
攒了一身酷
攒了一身酷 2020-11-27 14:46

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

I\'m currently us

3条回答
  •  悲哀的现实
    2020-11-27 15:02

    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 :)

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

提交回复
热议问题