Last active
August 29, 2015 13:59
-
-
Save FransBouma/10972512 to your computer and use it in GitHub Desktop.
Creating Expression<Func<T>> at runtime.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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