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); }
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)); }