private ExpressionInfo FetchFromPath(ExpressionInfo expressionInfo, string path, bool root)
        {
            var expression  = expressionInfo.Expression;
            var currentType = Type;
            var metas       = Session.Factory.GetAllClassMetadata()
                              .Select(o => o.Value)
                              .Where(o => Type.IsAssignableFrom(o.MappedClass))
                              .ToList();

            if (!metas.Any())
            {
                throw new HibernateException($"Metadata for type '{Type}' was not found");
            }

            var meta = metas.Count == 1 ? metas.First() : metas.First(x => x.MappedClass == Type);

            var paths = path.Split('.');
            var index = 0;

            foreach (var propName in paths)
            {
                if (meta == null)
                {
                    throw new InvalidOperationException($"Unable to fetch property {propName} from path '{path}', no class metadata found");
                }
                var propType = meta.GetPropertyType(propName);
                if (propType == null)
                {
                    throw new Exception($"Property '{propName}' does not exist in the type '{meta.MappedClass.FullName}'");
                }

                if (!(propType is IAssociationType assocType))
                {
                    throw new Exception($"Property '{propName}' does not implement IAssociationType interface");
                }

                if (assocType.IsCollectionType)
                {
                    var collectionType      = assocType as CollectionType;
                    var collectionPersister = (IQueryableCollection)Session.Factory.GetCollectionPersister(collectionType.Role);
                    meta = collectionPersister.ElementType.IsEntityType
                        ? Session.Factory.GetClassMetadata(collectionPersister.ElementPersister.EntityName)
                        : null;

                    var collPath = string.Join(".", paths.Take(index + 1));

                    //Check if we can fetch the collection without create a cartesian product
                    //Fetch can occur only for nested collection
                    if (!string.IsNullOrEmpty(expressionInfo.CollectionPath) &&
                        !collPath.StartsWith(expressionInfo.CollectionPath + "."))
                    {
                        //We have to continue fetching within a new base query
                        var nextQueryInfo = expressionInfo.GetOrCreateNext();
                        return(FetchFromPath(nextQueryInfo, path, true));
                    }

                    expressionInfo.CollectionPath = collPath;
                }
                else
                {
                    meta = Session.Factory.GetClassMetadata(assocType.GetAssociatedEntityName(Session.Factory));
                }

                MethodInfo fetchMethod;

                //Try to get the actual property type (so we can skip casting as relinq will throw an exception)
                var relatedProp = currentType.GetProperty(propName);

                var relatedType = relatedProp != null ? relatedProp.PropertyType : meta?.MappedClass;
                if (propType.IsCollectionType && relatedProp != null && relatedType.IsGenericType)
                {
                    relatedType = propType.GetType().IsAssignableToGenericType(typeof(GenericMapType <,>))
                        ? typeof(KeyValuePair <,>).MakeGenericType(relatedType.GetGenericArguments())
                        : relatedType.GetGenericArguments()[0];
                }

                var convertToType = propType.IsCollectionType
                    ? typeof(IEnumerable <>).MakeGenericType(relatedType)
                    : null;

                var propertyExpression = CreatePropertyExpression(currentType, propName, convertToType);
                //No fetch before
                if (root)
                {
                    fetchMethod = propType.IsCollectionType
                        ? FetchManyMethod.MakeGenericMethod(Type, relatedType)
                        : FetchMethod.MakeGenericMethod(Type, relatedType);
                }
                else
                {
                    fetchMethod = propType.IsCollectionType
                        ? ThenFetchManyMethod.MakeGenericMethod(Type, currentType, relatedType)
                        : ThenFetchMethod.MakeGenericMethod(Type, currentType, relatedType);
                }


                expression  = Expression.Call(fetchMethod, expression, propertyExpression);
                currentType = relatedType;
                index++;
                root = false;
            }
            expressionInfo.Expression = expression;
            return(expressionInfo);
        }
Example #2
0
        private ExpressionInfo FetchFromPath(ExpressionInfo expressionInfo, string path)
        {
            var    meta           = expressionInfo.Metadata;
            var    root           = true;
            var    expression     = expressionInfo.Expression;
            var    currentType    = Type;
            string collectionPath = null;
            var    includedPaths  = new List <string>();

            var paths = path.Split('.');
            var index = 0;

            foreach (var propName in paths)
            {
                if (meta == null)
                {
                    throw new InvalidOperationException($"Unable to fetch property {propName} from path '{path}', no class metadata found");
                }

                var propType = meta.GetPropertyType(propName);
                if (propType == null)
                {
                    throw new Exception($"Property '{propName}' does not exist in the type '{meta.MappedClass.FullName}'");
                }

                if (!(propType is IAssociationType assocType))
                {
                    throw new Exception($"Property '{propName}' does not implement IAssociationType interface");
                }

                if (_includeOptions.IgnoreIncludedRelationFunction?.Invoke(Session.Factory, assocType) == true)
                {
                    break;
                }

                IQueryableCollection collectionPersister = null;
                if (assocType.IsCollectionType)
                {
                    var collectionType = (CollectionType)assocType;
                    collectionPersister = (IQueryableCollection)Session.Factory.GetCollectionPersister(collectionType.Role);
                    meta = collectionPersister.ElementType.IsEntityType
                        ? Session.Factory.GetClassMetadata(collectionPersister.ElementPersister.EntityName)
                        : null; // Will happen for dictionaries
                    var collPath = string.Join(".", paths.Take(index + 1));
                    //Check if we can fetch the collection without create a cartesian product
                    //Fetch can occur only for nested collection
                    if (!string.IsNullOrEmpty(expressionInfo.CollectionPath) &&
                        !collPath.StartsWith(expressionInfo.CollectionPath + "."))
                    {
                        //We have to continue fetching within a new base query
                        return(FetchFromPath(expressionInfo.GetOrCreateNext(), path));
                    }

                    collectionPath = collPath;
                }
                else
                {
                    meta = Session.Factory.GetClassMetadata(assocType.GetAssociatedEntityName(Session.Factory));
                }

                var includedPath = expressionInfo.AddIncludedProperty(propName, meta, collectionPersister, root);
                if (includedPath != null)
                {
                    includedPaths.Add(includedPath);
                }

                MethodInfo fetchMethod;

                //Try to get the actual property type (so we can skip casting as relinq will throw an exception)
                var relatedProp = currentType.GetProperty(propName);

                var relatedType = relatedProp != null ? relatedProp.PropertyType : meta?.MappedClass;
                if (propType.IsCollectionType && relatedProp != null && relatedType.IsGenericType)
                {
                    relatedType = propType.GetType().IsAssignableToGenericType(typeof(GenericMapType <,>))
                        ? typeof(KeyValuePair <,>).MakeGenericType(relatedType.GetGenericArguments())
                        : relatedType.GetGenericArguments()[0];
                }

                var convertToType = propType.IsCollectionType
                    ? typeof(IEnumerable <>).MakeGenericType(relatedType)
                    : null;

                var propertyExpression = CreatePropertyExpression(currentType, propName, convertToType);
                //No fetch before
                if (root)
                {
                    fetchMethod = propType.IsCollectionType
                        ? FetchManyMethod.MakeGenericMethod(Type, relatedType)
                        : FetchMethod.MakeGenericMethod(Type, relatedType);
                }
                else
                {
                    fetchMethod = propType.IsCollectionType
                        ? ThenFetchManyMethod.MakeGenericMethod(Type, currentType, relatedType)
                        : ThenFetchMethod.MakeGenericMethod(Type, currentType, relatedType);
                }

                expression  = Expression.Call(fetchMethod, expression, propertyExpression);
                currentType = relatedType;
                index++;
                root = false;
            }

            if (_includeOptions.MaximumColumnsPerQuery.HasValue &&
                expressionInfo.TotalColumns > _includeOptions.MaximumColumnsPerQuery.Value &&
                expressionInfo.IsExpressionModified)
            {
                // Remove the included paths as we have to rebuild the expression from start
                foreach (var includedPath in includedPaths)
                {
                    expressionInfo.RemoveIncludedProperty(includedPath);
                }

                return(FetchFromPath(expressionInfo.GetOrCreateNext(), path));
            }

            if (!string.IsNullOrEmpty(collectionPath))
            {
                expressionInfo.CollectionPath = collectionPath;
            }

            expressionInfo.Expression = expression;

            return(expressionInfo);
        }
 private ExpressionInfo FetchFromPath(ExpressionInfo expressionInfo, string path)
 {
     return(FetchFromPath(expressionInfo, path, true));
 }