/// <summary> /// Try to get the mapped type from the given expression. When the <paramref name="expression"/> type is /// <see cref="ExpressionType.Convert"/>, the <paramref name="mappedType"/> will be set based on the expression type /// only when the mapping for <see cref="UnaryExpression.Operand"/> was found, otherwise <see langword="null"/> /// will be returned. /// </summary> /// <param name="sessionFactory">The session factory to retrieve <see cref="IEntityPersister"/> types.</param> /// <param name="expression">The expression to evaluate.</param> /// <param name="mappedType">Output parameter that represents the mapped type of <paramref name="memberPath"/>.</param> /// <param name="entityPersister"> /// Output parameter that represents the entity persister of the entity where <paramref name="memberPath"/> is defined. /// This parameter will not be set when <paramref name="memberPath"/> represents a property in a collection composite element. /// </param> /// <param name="component"> /// Output parameter that represents the component type where <paramref name="memberPath"/> is defined. /// This parameter will not be set when <paramref name="memberPath"/> does not represent a property in a component. /// </param> /// <param name="memberPath"> /// Output parameter that represents the path of the mapped member, which in most cases is the member name. In case /// when the mapped member is defined inside a component the path will be prefixed with the name of the component member and a dot. /// (e.g. Component.Property).</param> /// <returns>Whether the mapped type was found.</returns> /// <remarks> /// When the <paramref name="expression"/> contains an expression of type <see cref="ExpressionType.Convert"/>, the /// result may not be correct when casting to an entity that is mapped with multiple entity names. /// When the <paramref name="expression"/> is polymorphic, the first implementor will be returned. /// When the <paramref name="expression"/> contains a <see cref="ConditionalExpression"/>, the first found entity name /// will be returned from <see cref="ConditionalExpression.IfTrue"/> or <see cref="ConditionalExpression.IfFalse"/>. /// When the <paramref name="expression"/> contains a <see cref="ExpressionType.Coalesce"/> expression, the first found entity name /// will be returned from <see cref="BinaryExpression.Left"/> or <see cref="BinaryExpression.Right"/>. /// </remarks> internal static bool TryGetMappedType( ISessionFactoryImplementor sessionFactory, Expression expression, out IType mappedType, out IEntityPersister entityPersister, out IAbstractComponentType component, out string memberPath) { // In order to get the correct entity name from the expression we first have to find the constant expression that contains the // IEntityNameProvider instance, from which we can retrieve the starting entity name. Once we have it, we have to traverse all // expressions that we had to traverse in order to find the IEntityNameProvider instance, but in reverse order (bottom to top) // and keep tracking the entity name until we reach to top. memberPath = null; mappedType = null; entityPersister = null; component = null; // Try to retrieve the starting entity name with all members that were traversed in that process. if (!MemberMetadataExtractor.TryGetAllMemberMetadata(expression, out var metadataResults)) { // Failed to find the starting entity name, due to: // - Unsupported expression // - The expression didn't contain the IEntityNameProvider instance return(false); } // Due to coalesce and conditional expressions we can have multiple paths to traverse, in that case find the first path // for which we are able to determine the mapped type. foreach (var metadataResult in metadataResults) { if (ProcessMembersMetadataResult( metadataResult, sessionFactory, out mappedType, out entityPersister, out component, out memberPath)) { return(true); } } return(false); }
private static bool TryGetAllMemberMetadata( Expression expression, Stack <MemberMetadata> memberPaths, System.Type convertType, bool hasIndexer, out MemberMetadataResult results) { var extractor = new MemberMetadataExtractor(memberPaths, convertType, hasIndexer); extractor.Accept(expression); results = extractor._entityName != null || extractor._childrenResults.Count > 0 ? new MemberMetadataResult( extractor._childrenResults, extractor._memberPaths, extractor._entityName, extractor._convertType) : null; return(results != null); }