Skip to content

Instantly share code, notes, and snippets.

@FransBouma
Last active August 29, 2015 13:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save FransBouma/10972512 to your computer and use it in GitHub Desktop.
Save FransBouma/10972512 to your computer and use it in GitHub Desktop.
Creating Expression<Func<T>> at runtime.
// these lambda's are used in e.g. projections in typed queries to materialize object arrays into typed objects.
// running this 100000 times at runtime takes 20551ms
System.Linq.Expressions.Expression<Func<OrderPocoQsRow>> l = () => new NW26.Adapter.TypedViewClasses.OrderPocoQsRow()
{
CustomerId = OrderPocoQsFields.CustomerId.ToValue<System.String>(),
EmployeeId = OrderPocoQsFields.EmployeeId.ToValue<Nullable<System.Int32>>(),
Freight = OrderPocoQsFields.Freight.ToValue<Nullable<System.Decimal>>(),
OrderDate = OrderPocoQsFields.OrderDate.ToValue<Nullable<System.DateTime>>(),
OrderId = OrderPocoQsFields.OrderId.ToValue<System.Int32>(),
RequiredDate = OrderPocoQsFields.RequiredDate.ToValue<Nullable<System.DateTime>>(),
ShipAddress = OrderPocoQsFields.ShipAddress.ToValue<System.String>(),
ShipCity = OrderPocoQsFields.ShipCity.ToValue<System.String>(),
ShipCountry = OrderPocoQsFields.ShipCountry.ToValue<System.String>(),
ShipName = OrderPocoQsFields.ShipName.ToValue<System.String>(),
ShippedDate = OrderPocoQsFields.ShippedDate.ToValue<Nullable<System.DateTime>>(),
ShipPostalCode = OrderPocoQsFields.ShipPostalCode.ToValue<System.String>(),
ShipRegion = OrderPocoQsFields.ShipRegion.ToValue<System.String>(),
ShipVia = OrderPocoQsFields.ShipVia.ToValue<Nullable<System.Int32>>()
};
// this class creates the same lambda as above, but caches its results after the first time
// running this code 100000 times takes: 98ms.
var l = ProjectionLambdaCreator.Create<OrderPocoQsRow, OrderPocoQsFields>();
// the method above simply does:
// LinqExpression is an alias of System.Linq.Expressions.Expression,
// TimedLock is: http://www.interact-sw.co.uk/iangblog/2004/04/26/yetmoretimedlocking
/// <summary>
/// Creates the lambda which instantiates a new T instance from fields produced by U. Each property of T which has a similarly named field
/// in U gets a projection call in the returned lambda.
/// </summary>
/// <typeparam name="T">type of the element the lambda has to create instances of, e.g. CustomerOrderRow</typeparam>
/// <typeparam name="U">class which contains properties to create fields which are the source of the projection, e.g. CustomerFields.</typeparam>
/// <returns>
/// ready to use lambda for Select(Of T)
/// </returns>
/// <remarks>If there has already been created a lambda for T, U and it's found in the internal cache, that lambda is returned instead
/// of creating a new one.</remarks>
public static System.Linq.Expressions.Expression<Func<T>> Create<T, U>()
where T : new()
where U : class
{
using(TimedLock.Lock(_semaphore))
{
var cachedLambdasForT = _lambdaCache.GetValue(typeof(T));
if(cachedLambdasForT != null)
{
var cachedLambdaForU = cachedLambdasForT.GetValue(typeof(U));
if(cachedLambdaForU != null)
{
// found a cached one
return (System.Linq.Expressions.Expression<Func<T>>)cachedLambdaForU;
}
}
else
{
cachedLambdasForT = new Dictionary<Type, LambdaExpression>();
_lambdaCache.Add(typeof(T), cachedLambdasForT);
}
// not cached, create a new one.
var projectorBindings = new List<MemberBinding>();
var propertiesOfT = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.SetProperty);
var propertiesOfU = typeof(U).GetProperties(BindingFlags.Public | BindingFlags.Static | BindingFlags.GetProperty).ToDictionary(p => p.Name);
foreach(var p in propertiesOfT)
{
PropertyInfo fieldProperty = propertiesOfU.GetValue(p.Name);
if(fieldProperty==null)
{
continue;
}
projectorBindings.Add(LinqExpression.Bind(p, LinqExpression.Call(_toValueMethodInfo.MakeGenericMethod(p.PropertyType),
LinqExpression.MakeMemberAccess(null, fieldProperty))));
}
var toReturn = LinqExpression.Lambda(LinqExpression.MemberInit(LinqExpression.New(typeof(T)),
projectorBindings.ToArray()));
cachedLambdasForT[typeof(U)] = toReturn;
return (System.Linq.Expressions.Expression<Func<T>>)toReturn;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment