コード例 #1
0
        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()));
        }
コード例 #2
0
        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);
        }
コード例 #3
0
        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()));
        }
コード例 #4
0
 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));
     }
 }
コード例 #5
0
 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[]));
 }
コード例 #6
0
        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));
        }
コード例 #7
0
        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));
        }
コード例 #8
0
        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);
        }
コード例 #9
0
        private void AssertNoInference(FetchNode fetchNode)
        {
            if (fetchNode == null)
            {
                return;
            }

            Assert.False(fetchNode.InferredInnerJoin);
            foreach (var fetchNodeChild in fetchNode.Children)
            {
                this.AssertNoInference(fetchNodeChild.Value);
            }
        }
コード例 #10
0
        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
        }
コード例 #11
0
        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
        }
コード例 #12
0
        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
        }
コード例 #13
0
        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);
        }
コード例 #14
0
        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
        }
コード例 #15
0
        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));
        }
コード例 #16
0
ファイル: ColumnElement.cs プロジェクト: Polylytics/dashing
 public ColumnElement(FetchNode node, string columnName, bool isRoot) {
     this.Node = node;
     this.columnName = columnName;
     this.IsRoot = isRoot;
 }
コード例 #17
0
        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);
                    }
                }
            }
        }
コード例 #18
0
 public ColumnElement(FetchNode node, string columnName, bool isRoot)
 {
     this.Node       = node;
     this.columnName = columnName;
     this.IsRoot     = isRoot;
 }
コード例 #19
0
        /// <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));
        }
コード例 #20
0
        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()));
        }
コード例 #21
0
        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);
        }