public Tuple <Delegate, Type[]> GenerateNonCollectionMapper <T>(FetchNode fetchTree) { // this is simple, just take the arguments, map them // params go in order of fetch tree var tt = typeof(T); var objectsParam = Expression.Parameter(typeof(object[])); var rootVar = Expression.Variable(tt); var statements = new List <Expression>(); var mappedTypes = new List <Type> { tt }; // var rootVar = (RootType)objects[0]; GetRootAssignment <T>(statements, rootVar, objectsParam, tt); // go through the tree int i = 1; statements.AddRange(this.VisitNonCollectionTree <T>(fetchTree, objectsParam, rootVar, ref i, mappedTypes)); // add in the return statement and parameter statements.Add(Expression.Call(rootVar, tt.GetMethod("EnableTracking"))); statements.Add(rootVar); return(Tuple.Create(Expression.Lambda(Expression.Block(new[] { rootVar }, statements), objectsParam).Compile(), mappedTypes.ToArray())); }
private IEnumerable <Expression> VisitNonCollectionTree <T>( FetchNode fetchTree, ParameterExpression objectsParam, Expression parent, ref int i, IList <Type> mappedTypes) { var statements = new List <Expression>(); foreach (var child in fetchTree.Children) { if (child.Value.IsFetched) { var propExpr = Expression.Property(parent, child.Value.Column.Name); var indexExpr = Expression.ArrayIndex(objectsParam, Expression.Constant(i)); var ifExpr = Expression.NotEqual(indexExpr, Expression.Constant(null)); var mappedType = child.Value.Column.Type; var assignExpr = Expression.Assign(propExpr, Expression.Convert(indexExpr, mappedType)); ++i; mappedTypes.Add(mappedType); var innerStatements = this.VisitNonCollectionTree <T>(child.Value, objectsParam, propExpr, ref i, mappedTypes); var thenExpr = new List <Expression> { assignExpr }; thenExpr.AddRange(innerStatements); thenExpr.Add( Expression.Call(Expression.Convert(indexExpr, typeof(ITrackedEntity)), typeof(ITrackedEntity).GetMethod("EnableTracking"))); statements.Add(Expression.IfThen(ifExpr, Expression.Block(thenExpr))); } } return(statements); }
public Tuple <Delegate, Type[]> GenerateCollectionMapper <T>(FetchNode fetchTree) { var rootType = typeof(T); // closure variables var currentRootParam = Expression.Parameter(rootType, "currentRoot"); var resultsParam = Expression.Parameter(typeof(IList <>).MakeGenericType(rootType), "results"); // set up some stuff var objectsParam = Expression.Parameter(typeof(object[])); var rootVar = Expression.Variable(rootType, "root"); var mappedTypes = new List <Type> { rootType }; var statements = new List <Expression>(); var newRootStatements = new List <Expression> { Expression.Assign(currentRootParam, rootVar), Expression.Call( resultsParam, typeof(ICollection <>).MakeGenericType(rootType).GetMethod("Add"), currentRootParam) }; var insideCollectionStatements = new List <Expression>(); // assign root variable statements.Add(Expression.Assign(rootVar, Expression.Convert(Expression.ArrayIndex(objectsParam, Expression.Constant(0)), rootType))); // iterate through the tree var objectParamArrayIdx = 1; bool hasVisitedCollection = false; var innerStatements = this.VisitTree( fetchTree, currentRootParam, objectsParam, ref hasVisitedCollection, false, mappedTypes, ref objectParamArrayIdx); newRootStatements.AddRange(innerStatements.Item1); newRootStatements.Add(Expression.Call(currentRootParam, rootType.GetMethod("EnableTracking"))); insideCollectionStatements.AddRange(innerStatements.Item2); // if currentRoomParam == null || currentRootParam.Pk != rootVar.Pk { currentRootParam = rootVar; currentRootParam.EnabledTracking(); results.Add(currentRootParam); } var primaryKeyName = this.configuration.GetMap <T>().PrimaryKey.Name; statements.Add( Expression.IfThen( Expression.OrElse( Expression.Equal(currentRootParam, Expression.Constant(null)), Expression.NotEqual(Expression.Property(currentRootParam, primaryKeyName), Expression.Property(rootVar, primaryKeyName))), Expression.Block(newRootStatements))); statements.AddRange(insideCollectionStatements); statements.Add(rootVar); var innerLambda = Expression.Lambda(Expression.Block(new[] { rootVar }, statements), objectsParam); return(Tuple.Create(Expression.Lambda(innerLambda, currentRootParam, resultsParam).Compile(), mappedTypes.ToArray())); }
public static IEnumerable <T> Fetch <T>(this IEnumerable <T> enumerable, FetchNode fetchNode, Dictionary <Type, object> tables) { foreach (var entity in enumerable) { yield return((T)Expand(entity, fetchNode, tables)); } }
public ProjectionExpressionRewriter(IConfiguration configuration, ProjectedSelectQuery <TBase, TProjection> query, FetchNode rootNode) { this.configuration = configuration; this.query = query; this.rootNode = rootNode; this.fetchNodeLookup = new Dictionary <FetchNode, FetchNodeLookupValue>(); this.parameterExpression = Expression.Parameter(typeof(object[])); }
public void RelationshipExcludeThrows() { var parser = new IncludeExcludeParser(this.GetConfig <Blog, string>(blog => blog.Description)); Expression <Func <Post, Blog> > pred = post => post.Blog; var fetchNode = new FetchNode(); Assert.Throws <NotSupportedException>(() => parser.ParseExpression <Post>(pred, fetchNode, false)); }
public void NonFetchedExcludeThrows() { var parser = new IncludeExcludeParser(this.GetConfig <Blog, string>(blog => blog.Description)); Expression <Func <Post, string> > pred = post => post.Blog.Description; var fetchNode = new FetchNode(); Assert.Throws <InvalidOperationException>(() => parser.ParseExpression <Post>(pred, fetchNode, false)); }
public void SimpleExcludeAddedToTree() { var parser = new IncludeExcludeParser(this.GetConfig <Post, string>(post => post.Title)); Expression <Func <Post, string> > pred = post => post.Title; var fetchNode = new FetchNode(); parser.ParseExpression <Post>(pred, fetchNode, false); Assert.Single(fetchNode.ExcludedColumns); Assert.Null(fetchNode.IncludedColumns); Assert.Equal(nameof(Post.Title), fetchNode.ExcludedColumns.First().Name); }
private void AssertNoInference(FetchNode fetchNode) { if (fetchNode == null) { return; } Assert.False(fetchNode.InferredInnerJoin); foreach (var fetchNodeChild in fetchNode.Children) { this.AssertNoInference(fetchNodeChild.Value); } }
public void SelectBaseWorks() { var parser = GetParser <Post>(); var rootNode = new FetchNode(); parser.ParseExpression( p => new Post { Title = p.Title }, rootNode); // @formatter:off Assert.Single(rootNode.IncludedColumns); Assert.Equal(nameof(Post.Title), rootNode.IncludedColumns.Single().Name); // @formatter:on }
public void IncludedManyToOneRelationshipWorks() { var parser = GetParser <Post>(); var rootNode = new FetchNode(); parser.ParseExpression( p => new Post { Title = p.Title, Blog = p.Blog }, rootNode); // @formatter:off Assert.Single(rootNode.Children); Assert.Single(rootNode.IncludedColumns); Assert.True(rootNode.Children[nameof(Post.Blog)].IsFetched); // @formatter:on }
public void MultipleBaseWorks() { var parser = GetParser <Post>(); var rootNode = new FetchNode(); parser.ParseExpression( p => new Post { Title = p.Title, PostId = p.PostId }, rootNode); // @formatter:off Assert.Equal(2, rootNode.IncludedColumns.Count); Assert.Equal(nameof(Post.Title), rootNode.IncludedColumns.ElementAt(0).Name); Assert.Equal(nameof(Post.PostId), rootNode.IncludedColumns.ElementAt(1).Name); // @formatter:on }
public void FetchedIncludeWorks() { var config = this.GetConfig <Blog, string>(blog => blog.Description); var parser = new IncludeExcludeParser(config); Expression <Func <Post, string> > pred = post => post.Blog.Description; var rootNode = new FetchNode(); var blogNode = rootNode.AddChild(config.GetMap <Post>().Property(p => p.Blog), true); rootNode.Children = new OrderedDictionary <string, FetchNode> { { nameof(Post.Blog), blogNode } }; parser.ParseExpression <Post>(pred, rootNode, true); Assert.Null(rootNode.IncludedColumns); Assert.Null(rootNode.ExcludedColumns); Assert.Single(blogNode.IncludedColumns); Assert.Null(blogNode.ExcludedColumns); Assert.Equal(nameof(Blog.Description), blogNode.IncludedColumns.First().Name); }
public void SelectParentWorks() { var parser = GetParser <Post>(); var rootNode = new FetchNode(); parser.ParseExpression( p => new { Title = p.Title, BlogTitle = p.Blog.Title }, rootNode); // @formatter:off Assert.Single(rootNode.IncludedColumns); Assert.Equal(nameof(Post.Title), rootNode.IncludedColumns.Single().Name); Assert.Single(rootNode.Children); Assert.Single(rootNode.Children[nameof(Post.Blog)].IncludedColumns); Assert.Equal(nameof(Blog.Title), rootNode.Children[nameof(Post.Blog)].IncludedColumns.Single().Name); // @formatter:on }
private Tuple <IEnumerable <Expression>, IEnumerable <Expression> > VisitTree( FetchNode node, Expression currentBranchExpression, ParameterExpression objectsParam, ref bool hasVisitedCollection, bool isInsideCollection, List <Type> mappedTypes, ref int objectParamArrayIdx) { var newRootStatements = new List <Expression>(); var insideCollectionStatements = new List <Expression>(); foreach (var childNode in node.Children) { if (childNode.Value.IsFetched) { // create a parameter Type childType; var isOneToMany = childNode.Value.Column.Relationship == RelationshipType.OneToMany; if (isOneToMany) { if (hasVisitedCollection) { throw new InvalidOperationException("I only support a single collection fetch!"); } childType = childNode.Value.Column.Type.GetGenericArguments().First(); hasVisitedCollection = true; } else { childType = childNode.Value.Column.Type; } mappedTypes.Add(childType); var arrayIndexExpr = Expression.ArrayIndex(objectsParam, Expression.Constant(objectParamArrayIdx)); var ifExpr = Expression.NotEqual(arrayIndexExpr, Expression.Constant(null)); var enableTrackingExpr = Expression.Call( Expression.Convert(arrayIndexExpr, typeof(ITrackedEntity)), typeof(ITrackedEntity).GetMethod("EnableTracking")); var convertExpr = Expression.Convert(arrayIndexExpr, childType); var propExpr = Expression.Property(currentBranchExpression, childNode.Value.Column.Name); Expression ex; switch (childNode.Value.Column.Relationship) { case RelationshipType.OneToMany: ex = Expression.IfThen( Expression.Equal(propExpr, Expression.Constant(null)), Expression.Assign(propExpr, Expression.New(typeof(List <>).MakeGenericType(childType)))); break; case RelationshipType.ManyToOne: case RelationshipType.OneToOne: ex = Expression.Assign(propExpr, convertExpr); break; default: throw new InvalidOperationException( string.Format("Unexpected RelationshipType: {0}", childNode.Value.Column.Relationship)); } // now visit the next fetch ++objectParamArrayIdx; var innerStatements = this.VisitTree( childNode.Value, isOneToMany ? (Expression)convertExpr : propExpr, objectsParam, ref hasVisitedCollection, isInsideCollection || isOneToMany, mappedTypes, ref objectParamArrayIdx); if (isInsideCollection) { var thenExpr = new List <Expression> { ex }; thenExpr.AddRange(innerStatements.Item2); thenExpr.Add(enableTrackingExpr); insideCollectionStatements.Add(Expression.IfThen(ifExpr, Expression.Block(thenExpr))); } else if (isOneToMany) { var thenExpr = new List <Expression> { ex }; var topOfInsideCollectionStatements = new List <Expression> { Expression.Call( propExpr, typeof(ICollection <>).MakeGenericType(childType) .GetMethod("Add"), convertExpr) }; topOfInsideCollectionStatements.AddRange(innerStatements.Item2); topOfInsideCollectionStatements.Add(enableTrackingExpr); insideCollectionStatements.Add(Expression.IfThen(ifExpr, Expression.Block(topOfInsideCollectionStatements))); newRootStatements.Add(Expression.IfThen(ifExpr, Expression.Block(thenExpr))); } else { var thenExpr = new List <Expression> { ex }; thenExpr.AddRange(innerStatements.Item1); thenExpr.Add(enableTrackingExpr); newRootStatements.Add(Expression.IfThen(ifExpr, Expression.Block(thenExpr))); insideCollectionStatements.AddRange(innerStatements.Item2); } } } return(new Tuple <IEnumerable <Expression>, IEnumerable <Expression> >(newRootStatements, insideCollectionStatements)); }
public ColumnElement(FetchNode node, string columnName, bool isRoot) { this.Node = node; this.columnName = columnName; this.IsRoot = isRoot; }
private void Clone(object entity, object result, FetchNode fetchNode) { var entityType = entity.GetType(); foreach (var column in this.configuration.GetMap(entityType).Columns) { var prop = entityType.GetProperty(column.Key); if (column.Value.Type.IsValueType) { prop.SetValue(result, prop.GetValue(entity)); } else if (column.Value.Type == typeof(string)) { var val = prop.GetValue(entity) as string; if (val != null) { val = string.Copy(val); } prop.SetValue(result, val); } else if (column.Value.Relationship == RelationshipType.ManyToOne || column.Value.Relationship == RelationshipType.OneToOne) { var val = prop.GetValue(entity); if (val != null) { if (fetchNode != null && fetchNode.Children.ContainsKey(column.Key)) { // fetched, we need to deep clone this var fetchedResult = Activator.CreateInstance(column.Value.Type); this.Clone(val, fetchedResult, fetchNode.Children[column.Key]); prop.SetValue(result, fetchedResult); } else { // if not fetched, then this is null but set the backing field value if necessary var map = column.Value.Relationship == RelationshipType.ManyToOne ? column.Value.ParentMap : column.Value.OppositeColumn.Map; var primaryKey = map.GetPrimaryKeyValue(val); var field = entityType.GetField(column.Value.DbName); field.SetValue(result, primaryKey); } } else { if (!column.Value.IsNullable) { throw new InvalidOperationException( string.Format( "The property {0} on {1} is marked as not nullable. You must add some data for it", column.Key, result.GetType())); } } } else if (column.Value.Relationship == RelationshipType.OneToMany) { var val = prop.GetValue(entity) as ICollection; if (val != null && val.Count > 0) { var listEntityType = column.Value.ChildColumn.Map.Type; var listType = typeof(List <>).MakeGenericType(listEntityType); var listResult = Activator.CreateInstance(listType); foreach (var collectionEntity in val) { var collectionResult = Activator.CreateInstance(listEntityType); this.Clone( collectionEntity, collectionResult, fetchNode != null && fetchNode.Children.ContainsKey(column.Key) ? fetchNode.Children[column.Key] : null); listType.GetMethod("Add").Invoke(listResult, new[] { collectionResult }); } prop.SetValue(result, listResult); } } } }
/// <returns> /// Item1 is the expressions for mapping the current root var, Item2 is all the other expressions from this branch /// </returns> private Tuple <IEnumerable <Expression>, IEnumerable <Expression> > VisitMultiCollectionTree <T>( FetchNode node, ref int collectionFetchParamCounter, Expression parentExpression, Type currentRootPrimaryKeyType, Expression currentRootPrimaryKeyExpr, ParameterExpression objectsParam, IList <Type> mappedTypes, List <Type> mapperClosureTypes, List <ParameterExpression> collectionVariables, List <ParameterExpression> newVariables, ref int objectParamArrayIdx) { var rootStatements = new List <Expression>(); var collectionStatements = new List <Expression>(); foreach (var child in node.Children) { if (child.Value.IsFetched) { // create a param var isOneToMany = child.Value.Column.Relationship == RelationshipType.OneToMany; Type childType = isOneToMany ? child.Value.Column.Type.GetGenericArguments().First() : child.Value.Column.Type; mappedTypes.Add(childType); // common vars var arrayIndexExpr = Expression.ArrayIndex(objectsParam, Expression.Constant(objectParamArrayIdx)); var ifExpr = Expression.NotEqual(arrayIndexExpr, Expression.Constant(null)); var thisVar = Expression.Parameter(childType, childType.Name + objectParamArrayIdx); newVariables.Add(thisVar); var convertExpr = Expression.Convert(arrayIndexExpr, childType); var thisInit = Expression.Assign(thisVar, convertExpr); var propExpr = Expression.Property(parentExpression, child.Value.Column.Name); // if one to many then we start a new root if (isOneToMany) { var pk = child.Value.Column.ChildColumn.Map.PrimaryKey; var pkType = pk.Type; var collectionVariableIdx = mapperClosureTypes.Count / 2; var pkExpr = Expression.Property(thisVar, pk.Name); // dictionary for storing entities by id for this type var dictType = typeof(IDictionary <,>).MakeGenericType(pkType, childType); mapperClosureTypes.Add(dictType); var dictVar = Expression.Variable(dictType, "dict" + collectionVariableIdx); collectionVariables.Add(dictVar); // Hashset<Tuple<ParentPkType, ChildPkType>> for indicating if the entity has been added to this entity // tuple variable for checking whether this entity has been added to this collection var tupleType = typeof(Tuple <,>).MakeGenericType(currentRootPrimaryKeyType, pkType); var tupleVar = Expression.Variable(tupleType, "tuple" + collectionVariableIdx); var hashsetPairType = typeof(HashSet <>).MakeGenericType(tupleType); mapperClosureTypes.Add(hashsetPairType); var hashsetPairVar = Expression.Variable(hashsetPairType, "hashsetPair" + collectionVariableIdx); newVariables.Add(tupleVar); collectionVariables.Add(hashsetPairVar); ++objectParamArrayIdx; var innerStatements = this.VisitMultiCollectionTree <T>( child.Value, ref collectionFetchParamCounter, thisVar, pkType, pkExpr, objectsParam, mappedTypes, mapperClosureTypes, collectionVariables, newVariables, ref objectParamArrayIdx); // instantiate the collection if necessary rootStatements.Add( Expression.IfThen( Expression.Equal(propExpr, Expression.Constant(null)), Expression.Assign(propExpr, Expression.New(typeof(List <>).MakeGenericType(childType))))); // create the many to one code var initRootExpr = Expression.IfThen( ifExpr, Expression.Block( thisInit, Expression.IfThenElse( Expression.Call(dictVar, dictType.GetMethod("ContainsKey"), pkExpr), Expression.Assign(thisVar, Expression.Property(dictVar, "Item", pkExpr)), Expression.Block( new[] { Expression.Call( dictVar, dictType.GetMethods().First(m => m.Name == "Add" && m.GetParameters().Count() == 2), pkExpr, thisVar) }.Union(innerStatements.Item1) .Union( new[] { Expression.Call(thisVar, typeof(ITrackedEntity).GetMethod("EnableTracking")) }))), Expression.Assign( tupleVar, Expression.New( tupleType.GetConstructor(new[] { currentRootPrimaryKeyType, pkType }), currentRootPrimaryKeyExpr, pkExpr)), Expression.IfThen( Expression.Not(Expression.Call(hashsetPairVar, hashsetPairType.GetMethod("Contains"), tupleVar)), Expression.Block( Expression.Call(propExpr, typeof(ICollection <>).MakeGenericType(childType).GetMethod("Add"), thisVar), Expression.Call(hashsetPairVar, hashsetPairType.GetMethod("Add"), tupleVar))))); collectionStatements.Add(initRootExpr); collectionStatements.AddRange(innerStatements.Item2); } else { ++objectParamArrayIdx; var innerStatements = this.VisitMultiCollectionTree <T>( child.Value, ref collectionFetchParamCounter, propExpr, currentRootPrimaryKeyType, currentRootPrimaryKeyExpr, objectsParam, mappedTypes, mapperClosureTypes, collectionVariables, newVariables, ref objectParamArrayIdx); var thisStatements = new List <Expression> { thisInit, Expression.Assign(propExpr, thisVar) }; thisStatements.AddRange(innerStatements.Item1); thisStatements.Add(Expression.Call(thisVar, typeof(ITrackedEntity).GetMethod("EnableTracking"))); var expr = Expression.IfThen(ifExpr, Expression.Block(thisStatements)); rootStatements.Add(expr); collectionStatements.AddRange(innerStatements.Item2); } } } return(Tuple.Create <IEnumerable <Expression>, IEnumerable <Expression> >(rootStatements, collectionStatements)); }
public Tuple <Delegate, Type[], Type[]> GenerateMultiCollectionMapper <T>(FetchNode fetchTree) { var rootType = typeof(T); var currentRootParam = Expression.Parameter(rootType, "currentRoot"); var rootVar = Expression.Variable(rootType, "rootVar"); var resultsParam = Expression.Parameter(typeof(IList <>).MakeGenericType(rootType), "results"); var objectsParam = Expression.Parameter(typeof(object[])); var statements = new List <Expression>(); var mappedTypes = new List <Type> { rootType }; var mapperClosureTypes = new List <Type>(); var collectionVariables = new List <ParameterExpression>(); var newVariables = new List <ParameterExpression>(); // var rootVar = (RootType)objects[0]; statements.Add(Expression.Assign(rootVar, Expression.Convert(Expression.ArrayIndex(objectsParam, Expression.Constant(0)), rootType))); var primaryKey = this.configuration.GetMap <T>().PrimaryKey; var pkName = primaryKey.Name; var currentRootPrimaryKeyExpr = Expression.Property(currentRootParam, pkName); var objectParamArrayIdx = 1; var collectionFetchParamCounter = 0; var innerStatements = this.VisitMultiCollectionTree <T>( fetchTree, ref collectionFetchParamCounter, currentRootParam, primaryKey.Type, currentRootPrimaryKeyExpr, objectsParam, mappedTypes, mapperClosureTypes, collectionVariables, newVariables, ref objectParamArrayIdx); var newRootStatements = new List <Expression> { Expression.Assign(currentRootParam, rootVar), Expression.Call( resultsParam, typeof(ICollection <>).MakeGenericType(rootType).GetMethod("Add"), currentRootParam) }; newRootStatements.AddRange(innerStatements.Item1); newRootStatements.Add(Expression.Call(currentRootParam, rootType.GetMethod("EnableTracking"))); // check to see if rootVar different to currentRoot // if currentRoomParam == null || currentRootParam.Pk != rootVar.Pk { results.Add(rootVar); currentRootParam = rootVar; } statements.Add( Expression.IfThen( Expression.OrElse( Expression.Equal(currentRootParam, Expression.Constant(null)), Expression.NotEqual(currentRootPrimaryKeyExpr, Expression.Property(rootVar, pkName))), Expression.Block(newRootStatements))); statements.AddRange(innerStatements.Item2); // add in the return statement and parameter statements.Add(rootVar); var innerLambda = Expression.Lambda(Expression.Block(new[] { rootVar }.Union(newVariables), statements), objectsParam); return(Tuple.Create( Expression.Lambda(innerLambda, new[] { currentRootParam, resultsParam }.Union(collectionVariables)).Compile(), mappedTypes.ToArray(), mapperClosureTypes.ToArray())); }
private static object Expand(object entity, FetchNode fetchNode, Dictionary <Type, object> tables) { foreach (var node in fetchNode.Children) { var prop = entity.GetType().GetProperty(node.Key); if (node.Value.Column.Relationship == RelationshipType.ManyToOne || node.Value.Column.Relationship == RelationshipType.OneToOne) { // this value should just contain the pk, so we fetch the entity from its table, expand all it's properties and then set var value = prop.GetValue(entity); if (value != null) { var tableType = node.Value.Column.Type; var relatedMap = node.Value.Column.Relationship == RelationshipType.ManyToOne ? node.Value.Column.ParentMap : node.Value.Column.OppositeColumn.Map; var table = tables[tableType]; var tableValue = typeof(InMemoryTable <,>).MakeGenericType(tableType, relatedMap.PrimaryKey.Type) .GetMethod("Get") .Invoke(table, new[] { relatedMap.GetPrimaryKeyValue(value) }); if (tableValue == null) { throw new Exception(string.Format("You've specified a non-existant relationship for property {0} on {1}", node.Key, entity)); } value = Expand(tableValue, node.Value, tables); prop.SetValue(entity, value); } } else if (node.Value.Column.Relationship == RelationshipType.OneToMany) { // for collections we need to query the related table for all referenced entities // expand them and then match them back var childColumn = node.Value.Column.ChildColumn; var param = Expression.Parameter(childColumn.Map.Type); var whereClause = Expression.Lambda( Expression.Equal(Expression.Property(Expression.Property(param, childColumn.Name), node.Value.Column.Map.PrimaryKey.Name), Expression.Constant(node.Value.Column.Map.GetPrimaryKeyValue(entity))), param).Compile(); var tableType = childColumn.Map.Type; var table = tables[tableType]; var enumerable = typeof(InMemoryTable <,>).MakeGenericType(tableType, childColumn.Map.PrimaryKey.Type) .GetMethod("Query") .Invoke(table, new object[0]) as IEnumerable; var wheredEnumerable = typeof(Enumerable).GetMethods() .Where(m => m.Name == "Where" && m.GetParameters().Last().ParameterType.GenericTypeArguments.Length == 2) .Single() .MakeGenericMethod(tableType) .Invoke(null, new object[] { enumerable, whereClause }) as IEnumerable; var list = Activator.CreateInstance(typeof(List <>).MakeGenericType(childColumn.Map.Type)) as IList; foreach (var child in wheredEnumerable) { list.Add(Expand(child, node.Value, tables)); } prop.SetValue(entity, list); } } return(entity); }