Ejemplo n.º 1
0
        /// <summary>
        /// Add the Fetch clauses to the query according to the given expand paths
        /// </summary>
        /// <param name="queryable">The query to expand</param>
        /// <param name="expandsQueryString">Comma-separated list of properties to expand.  May include nested paths of the form "Property/SubProperty"</param>
        /// <param name="sessionFactory">Provides the NHibernate metadata for the classes</param>
        /// <param name="expandMap">Will be populated with the names of the expanded properties for each type.</param>
        /// <param name="expandCollections">If true, eagerly fetch collections. Caution: this causes problems with $skip and $top operations.  
        ///     Default is false.  expandMap will still be populated with the collection property, so it will be lazy loaded.
        ///     Be sure to set default_batch_fetch_size in the configuration for lazy loaded collections.</param>
        /// <returns></returns>
        public IQueryable ApplyExpansions(IQueryable queryable, string expandsQueryString, ExpandTypeMap expandMap, bool expandCollections = false)
        {
            string[] expandPaths = expandsQueryString.Split(',').Select(s => s.Trim()).ToArray();
            if (!expandPaths.Any()) throw new Exception("Expansion Paths cannot be null");
            if (queryable == null) throw new Exception("Query cannot be null");

            return ApplyExpansions(queryable, expandPaths, expandMap, expandCollections);
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Recursively forces loading of each NHibernate proxy in the tree that matches an entry in the map.
        /// </summary>
        /// <param name="list">Top-level collection of objects</param>
        /// <param name="expandMap">Properties to initialize for each type</param>
        public static void InitializeList(IEnumerable list, ExpandTypeMap expandMap)
        {
            if (expandMap == null)
            {
                return;
            }

            var map   = expandMap.map;
            var depth = expandMap.maxDepth;

            foreach (var el in list)
            {
                InitializeWithCascade(el, map, depth);
            }
        }
Ejemplo n.º 3
0
        /// <summary>
        /// Create an ExpandTypeMap populated according to the expandPaths.
        /// </summary>
        /// <param name="type">The type of the root element.</param>
        /// <param name="expandPaths">The names of the properties to expand.  May include nested paths of the form "Property/SubProperty"</param>
        /// <param name="expandMap">Will be populated with the names of the expanded properties for each type.  If null, a new one is created.</param>
        /// <returns>expandMap</returns>
        public static ExpandTypeMap MapExpansions(Type type, string[] expandPaths, ExpandTypeMap expandMap = null)
        {
            if (!expandPaths.Any())
            {
                throw new ArgumentException("Expansion Paths cannot be null");
            }

            if (expandMap == null)
            {
                expandMap = new ExpandTypeMap();
            }
            foreach (string expand in expandPaths)
            {
                // We always start with the resulting element type
                var currentType = type;

                // split on '/' or '.'
                var segments = expand.Split('/', '.');
                expandMap.Deepen(segments.Length);
                foreach (string seg in segments)
                {
                    if (expandMap != null && !expandMap.map.ContainsKey(currentType))
                    {
                        expandMap.map.Add(currentType, new List <string>());
                    }

                    // Gather information about the property
                    var propInfo = currentType.GetProperty(seg);
                    if (propInfo == null)
                    {
                        throw new ArgumentException("Type '" + currentType.Name + "' does not have property '" + seg + "'");
                    }
                    if (expandMap != null && !expandMap.map[currentType].Contains(seg))
                    {
                        expandMap.map[currentType].Add(seg);
                    }

                    var propType = propInfo.PropertyType;

                    currentType = propType;
                }
            }

            return(expandMap);
        }
Ejemplo n.º 4
0
        /// <summary>
        /// Add the Fetch clauses to the query according to the given expand paths
        /// </summary>
        /// <param name="queryable">The query to expand</param>
        /// <param name="expandPaths">The names of the properties to expand.  May include nested paths of the form "Property/SubProperty"</param>
        /// <param name="sessionFactory">Provides the NHibernate metadata for the classes</param>
        /// <param name="expandMap">Will be populated with the names of the expanded properties for each type.</param>
        /// <param name="expandCollections">If true, eagerly fetch collections. Caution: this causes problems with $skip and $top operations.  
        ///     Default is false.  expandMap will still be populated with the collection property, so it will be lazy loaded.
        ///     Be sure to set default_batch_fetch_size in the configuration for lazy loaded collections.</param>
        /// <returns></returns>
        public IQueryable ApplyExpansions(IQueryable queryable, string[] expandPaths, ExpandTypeMap expandMap, bool expandCollections = false)
        {
            if (queryable == null) throw new ArgumentException("Query cannot be null");

            var nHibQuery = queryable.Provider as DefaultQueryProvider;
            if (nHibQuery == null) throw new ArgumentException("Expansion only supported on INHibernateQueryable queries");

            if (!expandPaths.Any()) throw new ArgumentException("Expansion Paths cannot be null");

            var currentQueryable = queryable;
            foreach (string expand in expandPaths)
            {
                // We always start with the resulting element type
                var currentType = currentQueryable.ElementType;
                var isFirstFetch = true;
                var isInvoking = true;

                // split on '/' or '.'
                var segments = expand.Split('/', '.');
                expandMap.Deepen(segments.Length);
                foreach (string seg in segments)
                {
                    if (expandMap != null && !expandMap.map.ContainsKey(currentType))
                        expandMap.map.Add(currentType, new List<string>());

                    IClassMetadata metadata = sessionFactory.GetClassMetadata(currentType);
                    if (metadata == null)
                    {
                        throw new ArgumentException("Type '" + currentType + "' not recognized as a valid type for this Context");
                    }

                    // Gather information about the property
                    var propInfo = currentType.GetProperty(seg);

                    if (propInfo == null)
                    {
                        throw new ArgumentException("Type '" + currentType.Name + "' does not have property '" + seg + "'");
                    }
                    if (expandMap != null && !expandMap.map[currentType].Contains(seg)) 
                        expandMap.map[currentType].Add(seg);

                    var propType = propInfo.PropertyType;
                    var metaPropType = metadata.GetPropertyType(seg);

                    // When this is the first segment of a path, we have to use Fetch instead of ThenFetch
                    var propFetchFunctionName = (isFirstFetch ? "Fetch" : "ThenFetch");

                    // The delegateType is a type for the lambda creation to create the correct return value
                    System.Type delegateType;

                    if (metaPropType.IsCollectionType)
                    {
                        // We have to use "FetchMany" or "ThenFetchMany" when the target property is a collection
                        propFetchFunctionName += "Many";

                        // We only support IList<T> or something similar
                        propType = propType.GetGenericArguments().Single();
                        delegateType = typeof(Func<,>).MakeGenericType(currentType,
                                                                        typeof(IEnumerable<>).MakeGenericType(propType));
                        if (!expandCollections)
                        {
                            // if we don't expand this collection, we won't invoke any sub-expansions of it either
                            // but we still need to traverse the tree top populate expandMap
                            isInvoking = false;
                        }
                    }
                    else
                    {
                        delegateType = typeof(Func<,>).MakeGenericType(currentType, propType);
                    }

                    if (isInvoking)
                    {
                        // Get the correct extension method (Fetch, FetchMany, ThenFetch, or ThenFetchMany)
                        var fetchMethodInfo = typeof(EagerFetchingExtensionMethods).GetMethod(propFetchFunctionName,
                                                                                          BindingFlags.Static |
                                                                                          BindingFlags.Public |
                                                                                          BindingFlags.InvokeMethod);
                        var fetchMethodTypes = new List<System.Type>();
                        fetchMethodTypes.AddRange(currentQueryable.GetType().GetGenericArguments().Take(isFirstFetch ? 1 : 2));
                        fetchMethodTypes.Add(propType);
                        fetchMethodInfo = fetchMethodInfo.MakeGenericMethod(fetchMethodTypes.ToArray());

                        // Create an expression of type new delegateType(x => x.{seg.Name})
                        var exprParam = System.Linq.Expressions.Expression.Parameter(currentType, "x");
                        var exprProp = System.Linq.Expressions.Expression.Property(exprParam, seg);
                        var exprLambda = System.Linq.Expressions.Expression.Lambda(delegateType, exprProp,
                                                                                   new System.Linq.Expressions.
                                                                                       ParameterExpression[] { exprParam });

                        // Call the *Fetch* function
                        var args = new object[] { currentQueryable, exprLambda };
                        currentQueryable = (IQueryable)fetchMethodInfo.Invoke(null, args) as IQueryable;
                    }
                    currentType = propType;
                    isFirstFetch = false;
                }
            }

            return currentQueryable;
        }
Ejemplo n.º 5
0
        /// <summary>
        /// Create an ExpandTypeMap populated according to the expandPaths.
        /// </summary>
        /// <param name="type">The type of the root element.</param>
        /// <param name="expandPaths">The names of the properties to expand.  May include nested paths of the form "Property/SubProperty"</param>
        /// <param name="expandMap">Will be populated with the names of the expanded properties for each type.  If null, a new one is created.</param>
        /// <returns>expandMap</returns>
        public static ExpandTypeMap MapExpansions(Type type, string[] expandPaths, ExpandTypeMap expandMap = null)
        {
            if (!expandPaths.Any()) throw new ArgumentException("Expansion Paths cannot be null");

            if (expandMap == null) expandMap = new ExpandTypeMap();
            foreach (string expand in expandPaths)
            {
                // We always start with the resulting element type
                var currentType = type;

                // split on '/' or '.'
                var segments = expand.Split('/','.');
                expandMap.Deepen(segments.Length);
                foreach (string seg in segments)
                {
                    if (expandMap != null && !expandMap.map.ContainsKey(currentType))
                        expandMap.map.Add(currentType, new List<string>());

                    // Gather information about the property
                    var propInfo = currentType.GetProperty(seg);
                    if (propInfo == null)
                    {
                        throw new ArgumentException("Type '" + currentType.Name + "' does not have property '" + seg + "'");
                    }
                    if (expandMap != null && !expandMap.map[currentType].Contains(seg))
                        expandMap.map[currentType].Add(seg);

                    var propType = propInfo.PropertyType;

                    currentType = propType;
                }
            }

            return expandMap;
        }
Ejemplo n.º 6
0
        /// <summary>
        /// Add the Fetch clauses to the query according to the given expand paths
        /// </summary>
        /// <param name="queryable">The query to expand</param>
        /// <param name="expandPaths">The names of the properties to expand.  May include nested paths of the form "Property/SubProperty"</param>
        /// <param name="sessionFactory">Provides the NHibernate metadata for the classes</param>
        /// <param name="expandMap">Will be populated with the names of the expanded properties for each type.</param>
        /// <param name="expandCollections">If true, eagerly fetch collections. Caution: this causes problems with $skip and $top operations.
        ///     Default is false.  expandMap will still be populated with the collection property, so it will be lazy loaded.
        ///     Be sure to set default_batch_fetch_size in the configuration for lazy loaded collections.</param>
        /// <returns></returns>
        public IQueryable ApplyExpansions(IQueryable queryable, string[] expandPaths, ExpandTypeMap expandMap, bool expandCollections = false)
        {
            if (queryable == null)
            {
                throw new ArgumentException("Query cannot be null");
            }

            var nHibQuery = queryable.Provider as DefaultQueryProvider;

            if (nHibQuery == null)
            {
                throw new ArgumentException("Expansion only supported on INHibernateQueryable queries");
            }

            if (!expandPaths.Any())
            {
                throw new ArgumentException("Expansion Paths cannot be null");
            }

            var currentQueryable = queryable;

            foreach (string expand in expandPaths)
            {
                // We always start with the resulting element type
                var currentType  = currentQueryable.ElementType;
                var isFirstFetch = true;
                var isInvoking   = true;

                // split on '/' or '.'
                var segments = expand.Split('/', '.');
                expandMap.Deepen(segments.Length);
                foreach (string seg in segments)
                {
                    if (expandMap != null && !expandMap.map.ContainsKey(currentType))
                    {
                        expandMap.map.Add(currentType, new List <string>());
                    }

                    IClassMetadata metadata = sessionFactory.GetClassMetadata(currentType);
                    if (metadata == null)
                    {
                        throw new ArgumentException("Type '" + currentType + "' not recognized as a valid type for this Context");
                    }

                    // Gather information about the property
                    var propInfo = currentType.GetProperty(seg);

                    if (propInfo == null)
                    {
                        throw new ArgumentException("Type '" + currentType.Name + "' does not have property '" + seg + "'");
                    }
                    if (expandMap != null && !expandMap.map[currentType].Contains(seg))
                    {
                        expandMap.map[currentType].Add(seg);
                    }

                    var propType     = propInfo.PropertyType;
                    var metaPropType = metadata.GetPropertyType(seg);

                    // When this is the first segment of a path, we have to use Fetch instead of ThenFetch
                    var propFetchFunctionName = (isFirstFetch ? "Fetch" : "ThenFetch");

                    // The delegateType is a type for the lambda creation to create the correct return value
                    System.Type delegateType;

                    if (metaPropType.IsCollectionType)
                    {
                        // We have to use "FetchMany" or "ThenFetchMany" when the target property is a collection
                        propFetchFunctionName += "Many";

                        // We only support IList<T> or something similar
                        propType     = propType.GetGenericArguments().Single();
                        delegateType = typeof(Func <,>).MakeGenericType(currentType,
                                                                        typeof(IEnumerable <>).MakeGenericType(propType));
                        if (!expandCollections)
                        {
                            // if we don't expand this collection, we won't invoke any sub-expansions of it either
                            // but we still need to traverse the tree top populate expandMap
                            isInvoking = false;
                        }
                    }
                    else
                    {
                        delegateType = typeof(Func <,>).MakeGenericType(currentType, propType);
                    }

                    if (isInvoking)
                    {
                        // Get the correct extension method (Fetch, FetchMany, ThenFetch, or ThenFetchMany)
                        var fetchMethodInfo = typeof(EagerFetchingExtensionMethods).GetMethod(propFetchFunctionName,
                                                                                              BindingFlags.Static |
                                                                                              BindingFlags.Public |
                                                                                              BindingFlags.InvokeMethod);
                        var fetchMethodTypes = new List <System.Type>();
                        fetchMethodTypes.AddRange(currentQueryable.GetType().GetGenericArguments().Take(isFirstFetch ? 1 : 2));
                        fetchMethodTypes.Add(propType);
                        fetchMethodInfo = fetchMethodInfo.MakeGenericMethod(fetchMethodTypes.ToArray());

                        // Create an expression of type new delegateType(x => x.{seg.Name})
                        var exprParam  = System.Linq.Expressions.Expression.Parameter(currentType, "x");
                        var exprProp   = System.Linq.Expressions.Expression.Property(exprParam, seg);
                        var exprLambda = System.Linq.Expressions.Expression.Lambda(delegateType, exprProp,
                                                                                   new System.Linq.Expressions.
                                                                                   ParameterExpression[] { exprParam });

                        // Call the *Fetch* function
                        var args = new object[] { currentQueryable, exprLambda };
                        currentQueryable = (IQueryable)fetchMethodInfo.Invoke(null, args) as IQueryable;
                    }
                    currentType  = propType;
                    isFirstFetch = false;
                }
            }

            return(currentQueryable);
        }
Ejemplo n.º 7
0
        /// <summary>
        /// Add the Fetch clauses to the query according to the given expand paths
        /// </summary>
        /// <param name="queryable">The query to expand</param>
        /// <param name="expandsQueryString">Comma-separated list of properties to expand.  May include nested paths of the form "Property/SubProperty"</param>
        /// <param name="sessionFactory">Provides the NHibernate metadata for the classes</param>
        /// <param name="expandMap">Will be populated with the names of the expanded properties for each type.</param>
        /// <param name="expandCollections">If true, eagerly fetch collections. Caution: this causes problems with $skip and $top operations.
        ///     Default is false.  expandMap will still be populated with the collection property, so it will be lazy loaded.
        ///     Be sure to set default_batch_fetch_size in the configuration for lazy loaded collections.</param>
        /// <returns></returns>
        public IQueryable ApplyExpansions(IQueryable queryable, string expandsQueryString, ExpandTypeMap expandMap, bool expandCollections = false)
        {
            string[] expandPaths = expandsQueryString.Split(',').Select(s => s.Trim()).ToArray();
            if (!expandPaths.Any())
            {
                throw new Exception("Expansion Paths cannot be null");
            }
            if (queryable == null)
            {
                throw new Exception("Query cannot be null");
            }

            return(ApplyExpansions(queryable, expandPaths, expandMap, expandCollections));
        }