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
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:
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
}
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.
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<> 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<>.
///
/// Note: anonymous type arguments are acceptable.
///
/// Use other constructor for IEnumerable.
/// </summary>
/// <param name="collection">IEnumerable<>. 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<>
/// </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();
//}
}
}