Esempio n. 1
0
        /// <summary>
        /// Specifies additional related entities to include in serialized output of a Coalesce custom data source.
        ///     The specified entities must have already been loaded into the context that was provided for the data source.
        ///     to be included is specified starting with the type of entity being queried (TEntity).
        ///     If you wish to include additional types based on the navigation properties of
        ///     the type being included, then chain a call to .ThenIncluded(prop => prop.Child)
        ///     after this call.
        ///
        ///     The purpose of this is to allow loading subsets of navigation properties without neccesarily loading the entire contents of the navigation property.
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <typeparam name="TProperty"></typeparam>
        /// <param name="query"></param>
        /// <param name="expr">An expression which returns the navigation property that should be included in the data source's output.</param>
        /// <returns></returns>
        public static IIncludedSeparatelyQueryable <TEntity, TProperty> IncludedSeparately <TEntity, TProperty>(
            this IQueryable <TEntity> query,
            Expression <Func <TEntity, TProperty> > expr) where TEntity : class
        {
            var provider = query.Provider as IncludableQueryProvider;

            if (provider == null)
            {
                query    = query.CaptureSeparateIncludes();
                provider = query.Provider as IncludableQueryProvider;
            }

            IncludeTree tail;
            var         body = ((MemberExpression)expr.Body);
            var         head = IncludeTree.ParseMemberExpression(body, out tail);

            // Add the head of this expression to the root includetree for the query.
            tail = provider.IncludeTree.AddLinearChild(head);


            return(new IncludedSeparatelyQueryable <TEntity, TProperty>(query)
            {
                IncludeTree = tail
            });
        }
Esempio n. 2
0
        public static IncludeTree GetIncludeTree(this IQueryable queryable, string rootName = null)
        {
            if (!(queryable.Provider is IncludableQueryProvider) && !(queryable.Provider is EntityQueryProvider))
            {
                throw new ArgumentException(
                          $"Can't get an IncludeTree from an IQueryable with an IQueryProvider of type {queryable.Provider.GetType().Name}. "
                          + "Try using .IncludedSeparately(...) and .ThenIncluded(...) to capture includes.");
            }

            var         expression = queryable.Expression;
            IncludeTree root       = (queryable.Provider as IncludableQueryProvider)?.IncludeTree ?? new IncludeTree {
                PropertyName = rootName
            };
            IncludeTree currentNode = null;

            // When we get to the root of the queryable, it won't be a MethodCallExpression.
            while (expression is MethodCallExpression)
            {
                // callExpr.Arguments[0] is the entire previous query.
                // callExpr.Arguments[1] is the lambda for the property specifier
                var callExpr = expression as MethodCallExpression;

                if (callExpr.Method.Name == nameof(EntityFrameworkQueryableExtensions.Include) ||
                    callExpr.Method.Name == nameof(EntityFrameworkQueryableExtensions.ThenInclude))
                {
                    // I'm like a wizard with all these casts.
                    var body = ((MemberExpression)((LambdaExpression)((UnaryExpression)callExpr.Arguments[1]).Operand).Body);

                    IncludeTree tail;
                    var         head = IncludeTree.ParseMemberExpression(body, out tail);

                    // If we had a child from a ThenInclude, add it to the tail of this node.
                    if (currentNode != null)
                    {
                        tail.AddChild(currentNode);
                    }

                    // Save the head of this expression in case we're parsing a ThenInclude.
                    currentNode = head;

                    if (callExpr.Method.Name == nameof(EntityFrameworkQueryableExtensions.Include))
                    {
                        // Finally, add the current node to the root, since a .Include doesn't have parents.
                        root.AddChild(head);
                        currentNode = null;
                    }
                }

                expression = callExpr.Arguments[0];
            }

            return(root);
        }
Esempio n. 3
0
        public static IIncludedSeparatelyQueryable <TEntity, TProperty> ThenIncluded <TEntity, TPreviousProperty, TProperty>(
            this IIncludedSeparatelyQueryable <TEntity, IEnumerable <TPreviousProperty> > source,
            Expression <Func <TPreviousProperty, TProperty> > navigationPropertyPath)
            where TEntity : class
        {
            IncludeTree tail;
            var         body = ((MemberExpression)navigationPropertyPath.Body);
            var         head = IncludeTree.ParseMemberExpression(body, out tail);

            // Merge in the head of this tree as a child of the parent tree.
            tail = source.IncludeTree.AddLinearChild(head);

            return(new IncludedSeparatelyQueryable <TEntity, TProperty>(source)
            {
                IncludeTree = tail
            });
        }