public ODataUriResolver CreateResolver(IEdmModel model) { ODataUriResolver resolver; if (UnqualifiedNameCall && EnumPrefixFree) { resolver = new UnqualifiedCallAndEnumPrefixFreeResolver(); } else if (UnqualifiedNameCall) { resolver = new UnqualifiedODataUriResolver(); } else if (EnumPrefixFree) { resolver = new StringAsEnumResolver(); } else if (AlternateKeys) { resolver = new AlternateKeysODataUriResolver(model); } else { resolver = new ODataUriResolver(); } resolver.EnableCaseInsensitive = CaseInsensitive; return resolver; }
/// <summary> /// Constructs a new SelectBinder. /// </summary> /// <param name="model">The model used for binding.</param> /// <param name="edmType">The entity type that the $select is being applied to.</param> /// <param name="maxDepth">the maximum recursive depth.</param> /// <param name="expandClauseToDecorate">The already built expand clause to decorate</param> /// <param name="resolver">Resolver for uri parser.</param> public SelectBinder(IEdmModel model, IEdmStructuredType edmType, int maxDepth, SelectExpandClause expandClauseToDecorate, ODataUriResolver resolver = null) { ExceptionUtils.CheckArgumentNotNull(model, "tokenIn"); ExceptionUtils.CheckArgumentNotNull(edmType, "entityType"); this.visitor = new SelectPropertyVisitor(model, edmType, maxDepth, expandClauseToDecorate, resolver); }
/// <summary>Tries to create a key segment for the given filter if it is non empty.</summary> /// <param name="previous">Segment on which to compose.</param> /// <param name="previousKeySegment">The parent node's key segment.</param> /// <param name="parenthesisExpression">Parenthesis expression of segment.</param> /// <param name="keySegment">The key segment that was created if the key was non-empty.</param> /// <param name="enableUriTemplateParsing">Whether Uri template parsing is enabled.</param> /// <param name="resolver">The resolver to use.</param> /// <returns>Whether the key was non-empty.</returns> internal static bool TryCreateKeySegmentFromParentheses(ODataPathSegment previous, KeySegment previousKeySegment, string parenthesisExpression, out ODataPathSegment keySegment, bool enableUriTemplateParsing = false, ODataUriResolver resolver = null) { Debug.Assert(parenthesisExpression != null, "parenthesisExpression != null"); Debug.Assert(previous != null, "segment!= null"); if (resolver == null) { resolver = ODataUriResolver.Default; } if (previous.SingleResult) { throw ExceptionUtil.CreateSyntaxError(); } SegmentArgumentParser key; if (!SegmentArgumentParser.TryParseKeysFromUri(parenthesisExpression, out key, enableUriTemplateParsing)) { throw ExceptionUtil.CreateSyntaxError(); } // People/NS.Employees() is OK, just like People() is OK if (key.IsEmpty) { keySegment = null; return false; } keySegment = CreateKeySegment(previous, previousKeySegment, key, resolver); return true; }
/// <summary> /// Build a property visitor to visit the select tree and decorate a SelectExpandClause /// </summary> /// <param name="model">The model used for binding.</param> /// <param name="edmType">The entity type that the $select is being applied to.</param> /// <param name="maxDepth">the maximum recursive depth.</param> /// <param name="expandClauseToDecorate">The already built expand clause to decorate</param> /// <param name="resolver">Resolver for uri parser.</param> public SelectPropertyVisitor(IEdmModel model, IEdmStructuredType edmType, int maxDepth, SelectExpandClause expandClauseToDecorate, ODataUriResolver resolver) { this.model = model; this.edmType = edmType; this.maxDepth = maxDepth; this.expandClauseToDecorate = expandClauseToDecorate; this.resolver = resolver ?? ODataUriResolver.Default; }
public static IEdmSchemaType FindTypeFromModel(IEdmModel model, string qualifiedName, ODataUriResolver resolver) { if (resolver == null) { resolver = ODataUriResolver.Default; } return resolver.ResolveType(model, qualifiedName); }
/// <summary> /// Given a property name, if the associated type reference is strucutred, then this returns /// the property of the structured type. Otherwise, it returns null. /// </summary> /// <param name="parentReference">The parent type to be used to find binding options.</param> /// <param name="propertyName">The string designated the property name to be bound.</param> /// <param name="resolver">Resolver for uri parser.</param> /// <returns>The property associated with string and parent type.</returns> internal static IEdmProperty BindProperty(IEdmTypeReference parentReference, string propertyName, ODataUriResolver resolver = null) { if (resolver == null) { resolver = ODataUriResolver.Default; } IEdmStructuredTypeReference structuredParentType = parentReference == null ? null : parentReference.AsStructuredOrNull(); return structuredParentType == null ? null : resolver.ResolveProperty(structuredParentType.StructuredDefinition(), propertyName); }
/// <summary> /// Tries to handle the current segment as a key property value. /// </summary> /// <param name="segmentText">The segment text.</param> /// <param name="previous">The previous segment.</param> /// <param name="previousKeySegment">The parent node's key segment.</param> /// <param name="urlConvention">The current url convention for the server.</param> /// <param name="keySegment">The key segment that was created if the segment could be interpreted as a key.</param> /// <param name="enableUriTemplateParsing">Whether Uri template parsing is enabled.</param> /// <param name="resolver">The resolver to use.</param> /// <returns>Whether or not the segment was interpreted as a key.</returns> internal static bool TryHandleSegmentAsKey(string segmentText, ODataPathSegment previous, KeySegment previousKeySegment, UrlConvention urlConvention, out KeySegment keySegment, bool enableUriTemplateParsing = false, ODataUriResolver resolver = null) { Debug.Assert(previous != null, "previous != null"); Debug.Assert(urlConvention != null, "urlConvention != null"); if (resolver == null) { resolver = ODataUriResolver.Default; } keySegment = null; // If the current convention is not keys-as-segments, then this does not apply. if (!urlConvention.GenerateKeyAsSegment) { return false; } // Keys only apply to collections, so if the prior segment is already a singleton, do not treat this segment as a key. if (previous.SingleResult) { return false; } // System segments (ie '$count') are never keys. if (IsSystemSegment(segmentText)) { return false; } // If the previous type is not an entity collection type // TODO: collapse this and SingleResult. IEdmEntityType targetEntityType; if (previous.TargetEdmType == null || !previous.TargetEdmType.IsEntityOrEntityCollectionType(out targetEntityType)) { return false; } // Previously KeyAsSegment only allows single key, but we can also leverage related key finder to auto fill // missed key value from referential constraint information, which would be done in CreateKeySegment. // CreateKeySegment method will check whether key properties are missing after taking in related key values. keySegment = CreateKeySegment(previous, previousKeySegment, SegmentArgumentParser.FromSegment(segmentText, enableUriTemplateParsing), resolver); return true; }
public override IDictionary <IEdmOperationParameter, SingleValueNode> ResolveOperationParameters(IEdmOperation operation, IDictionary <string, SingleValueNode> input) { Dictionary <IEdmOperationParameter, SingleValueNode> result = new Dictionary <IEdmOperationParameter, SingleValueNode>(EqualityComparer <IEdmOperationParameter> .Default); foreach (var item in input) { IEdmOperationParameter functionParameter = null; if (EnableCaseInsensitive) { functionParameter = ODataUriResolver.ResolveOpearationParameterNameCaseInsensitive(operation, item.Key); } else { functionParameter = operation.FindParameter(item.Key); } // ensure parameter name existis if (functionParameter == null) { throw new ODataException(Strings.ODataParameterWriterCore_ParameterNameNotFoundInOperation(item.Key, operation.Name)); } SingleValueNode newVal = item.Value; if (functionParameter.Type.IsEnum() && newVal is ConstantNode && newVal.TypeReference != null && newVal.TypeReference.IsString()) { string text = ((ConstantNode)item.Value).Value as string; ODataEnumValue val; IEdmTypeReference typeRef = functionParameter.Type; if (TryParseEnum(typeRef.Definition as IEdmEnumType, text, out val)) { newVal = new ConstantNode(val, text, typeRef); } } result.Add(functionParameter, newVal); } return(result); }
/// <summary> /// Follow any type segments on the path, stopping at the first segment that isn't a type token. /// </summary> /// <param name="firstTypeToken">the first type segment</param> /// <param name="model">the model these types are contained in.</param> /// <param name="maxDepth">the maximum recursive depth</param> /// <param name="resolver">Resolver for uri parser.</param> /// <param name="currentLevelType">the top level type, will be overwritten with the last entity type in the chain</param> /// <param name="firstNonTypeToken">the first non type token in the path</param> /// <returns>A path with type segments added to it.</returns> public static IEnumerable<ODataPathSegment> FollowTypeSegments(PathSegmentToken firstTypeToken, IEdmModel model, int maxDepth, ODataUriResolver resolver, ref IEdmStructuredType currentLevelType, out PathSegmentToken firstNonTypeToken) { ExceptionUtils.CheckArgumentNotNull(firstTypeToken, "firstTypeToken"); ExceptionUtils.CheckArgumentNotNull(model, "model"); if (!firstTypeToken.IsNamespaceOrContainerQualified()) { throw new ODataException(ODataErrorStrings.SelectExpandPathBinder_FollowNonTypeSegment(firstTypeToken.Identifier)); } int index = 0; List<ODataPathSegment> pathToReturn = new List<ODataPathSegment>(); PathSegmentToken currentToken = firstTypeToken; while (currentToken.IsNamespaceOrContainerQualified() && currentToken.NextToken != null) { IEdmType previousLevelEntityType = currentLevelType; currentLevelType = UriEdmHelpers.FindTypeFromModel(model, currentToken.Identifier, resolver) as IEdmStructuredType; if (currentLevelType == null) { // TODO: fix this error message? throw new ODataException(ODataErrorStrings.ExpandItemBinder_CannotFindType(currentToken.Identifier)); } UriEdmHelpers.CheckRelatedTo(previousLevelEntityType, currentLevelType); pathToReturn.Add(new TypeSegment(currentLevelType, /*entitySet*/null)); index++; currentToken = currentToken.NextToken; if (index >= maxDepth) { throw new ODataException(ODataErrorStrings.ExpandItemBinder_PathTooDeep); } } firstNonTypeToken = currentToken; return pathToReturn; }
/// <summary> /// Build a segment from a token. /// </summary> /// <param name="tokenIn">the token to bind</param> /// <param name="model">The model.</param> /// <param name="edmType">the type of the current scope based on type segments.</param> /// <param name="resolver">Resolver for uri parser.</param> /// <returns>The segment created from the token.</returns> public static ODataPathSegment ConvertNonTypeTokenToSegment(PathSegmentToken tokenIn, IEdmModel model, IEdmStructuredType edmType, ODataUriResolver resolver = null) { if (resolver == null) { resolver = ODataUriResolver.Default; } ODataPathSegment nextSegment; if (TryBindAsDeclaredProperty(tokenIn, edmType, resolver, out nextSegment)) { return nextSegment; } // Operations must be container-qualified, and because the token type indicates it was not a .-seperated identifier, we should not try to look up operations. if (tokenIn.IsNamespaceOrContainerQualified()) { if (TryBindAsOperation(tokenIn, model, edmType, out nextSegment)) { return nextSegment; } // If an action or function is requested in a selectItem using a qualifiedActionName or a qualifiedFunctionName // and that operation cannot be bound to the entities requested, the service MUST ignore the selectItem. if (!edmType.IsOpen) { return null; } } if (edmType.IsOpen) { return new OpenPropertySegment(tokenIn.Identifier); } throw new ODataException(ODataErrorStrings.MetadataBinder_PropertyNotDeclared(edmType.ODataFullName(), tokenIn.Identifier)); }
public static IEdmSchemaType FindTypeFromModel(IEdmModel model, string qualifiedName, ODataUriResolver resolver) { if (resolver == null) { resolver = ODataUriResolver.Default; } return(resolver.ResolveType(model, qualifiedName)); }
private static bool TryBindAsDeclaredProperty(PathSegmentToken tokenIn, IEdmStructuredType edmType, ODataUriResolver resolver, out ODataPathSegment segment) { IEdmProperty prop = resolver.ResolveProperty(edmType, tokenIn.Identifier); if (prop == null) { segment = null; return false; } if (prop.PropertyKind == EdmPropertyKind.Structural) { segment = new PropertySegment((IEdmStructuralProperty)prop); return true; } if (prop.PropertyKind == EdmPropertyKind.Navigation) { segment = new NavigationPropertySegment((IEdmNavigationProperty)prop, null /*TODO: set*/); return true; } throw new ODataException(ODataErrorStrings.SelectExpandBinder_UnknownPropertyType(prop.Name)); }
internal static bool ResolveOperationImportFromList(string identifier, IList<string> parameterNames, IEdmModel model, out IEdmOperationImport matchingOperationImport, ODataUriResolver resolver) { IList<IEdmOperationImport> candidateMatchingOperationImports = null; IList<IEdmActionImport> foundActionImportsWhenLookingForFunctions = new List<IEdmActionImport>(); try { if (parameterNames.Count > 0) { // In this case we have to return a function so filter out actions because the number of parameters > 0. candidateMatchingOperationImports = resolver.ResolveOperationImports(model, identifier).RemoveActionImports(out foundActionImportsWhenLookingForFunctions).FilterFunctionsByParameterNames(parameterNames, resolver.EnableCaseInsensitive).Cast<IEdmOperationImport>().ToList(); } else { candidateMatchingOperationImports = resolver.ResolveOperationImports(model, identifier).ToList(); } } catch (Exception exc) { if (!ExceptionUtils.IsCatchableExceptionType(exc)) { throw; } throw new ODataException(ODataErrorStrings.FunctionOverloadResolver_FoundInvalidOperationImport(identifier), exc); } if (foundActionImportsWhenLookingForFunctions.Count > 0) { throw ExceptionUtil.CreateBadRequestError(ODataErrorStrings.RequestUriProcessor_SegmentDoesNotSupportKeyPredicates(identifier)); } // If any of the things returned are an action, it better be the only thing returned, and there can't be parameters in the URL if (candidateMatchingOperationImports.Any(f => f.IsActionImport())) { if (candidateMatchingOperationImports.Count > 1) { if (candidateMatchingOperationImports.Any(o => o.IsFunctionImport())) { throw new ODataException(ODataErrorStrings.FunctionOverloadResolver_MultipleOperationImportOverloads(identifier)); } else { throw new ODataException(ODataErrorStrings.FunctionOverloadResolver_MultipleActionImportOverloads(identifier)); } } if (parameterNames.Count() != 0) { throw ExceptionUtil.CreateBadRequestError(ODataErrorStrings.RequestUriProcessor_SegmentDoesNotSupportKeyPredicates(identifier)); } matchingOperationImport = candidateMatchingOperationImports.Single(); return true; } // If parameter count is zero and there is one function import whoese parameter count is zero, return this function import. if (candidateMatchingOperationImports.Count > 1 && parameterNames.Count == 0) { candidateMatchingOperationImports = candidateMatchingOperationImports.Where(operationImport => operationImport.Operation.Parameters.Count() == 0).ToList(); } if (candidateMatchingOperationImports.Count == 0) { matchingOperationImport = null; return false; } if (candidateMatchingOperationImports.Count > 1) { throw new ODataException(ODataErrorStrings.FunctionOverloadResolver_MultipleOperationImportOverloads(identifier)); } matchingOperationImport = candidateMatchingOperationImports.Single(); return matchingOperationImport != null; }
internal static bool ResolveOperationFromList(string identifier, IEnumerable<string> parameterNames, IEdmType bindingType, IEdmModel model, out IEdmOperation matchingOperation, ODataUriResolver resolver) { // TODO: update code that is duplicate between operation and operation import, add more tests. // If the previous segment is an open type, the service action name is required to be fully qualified or else we always treat it as an open property name. if (bindingType != null) { // TODO: look up actual container names here? if (bindingType.IsOpenType() && !identifier.Contains(".")) { matchingOperation = null; return false; } } IList<IEdmOperation> candidateMatchingOperations = null; // The extension method FindBoundOperations & FindOperations call IEdmModel.FindDeclaredBoundOperations which can be implemented by anyone and it could throw any type of exception // so catching all of them and simply putting it in the inner exception. try { if (bindingType != null) { candidateMatchingOperations = resolver.ResolveBoundOperations(model, identifier, bindingType).ToList(); } else { candidateMatchingOperations = resolver.ResolveUnboundOperations(model, identifier).ToList(); } } catch (Exception exc) { if (ExceptionUtils.IsCatchableExceptionType(exc)) { throw new ODataException(ODataErrorStrings.FunctionOverloadResolver_FoundInvalidOperation(identifier), exc); } throw; } IList<IEdmAction> foundActionsWhenLookingForFunctions = new List<IEdmAction>(); int parameterNamesCount = parameterNames.Count(); if (bindingType != null) { candidateMatchingOperations = candidateMatchingOperations.EnsureOperationsBoundWithBindingParameter().ToList(); } // If the number of parameters > 0 then this has to be a function as actions can't have parameters on the uri only in the payload. Filter further by parameters in this case, otherwise don't. if (parameterNamesCount > 0) { // can only be a function as only functions have parameters on the uri. candidateMatchingOperations = candidateMatchingOperations.RemoveActions(out foundActionsWhenLookingForFunctions).FilterFunctionsByParameterNames(parameterNames, resolver.EnableCaseInsensitive).Cast<IEdmOperation>().ToList(); } else if (bindingType != null) { // Filter out functions with more than one parameter. Actions should not be filtered as the parameters are in the payload not the uri candidateMatchingOperations = candidateMatchingOperations.Where(o => (o.IsFunction() && o.Parameters.Count() == 1) || o.IsAction()).ToList(); } else { // Filter out functions with any parameters candidateMatchingOperations = candidateMatchingOperations.Where(o => (o.IsFunction() && !o.Parameters.Any()) || o.IsAction()).ToList(); } // Only filter if there is more than one and its needed. if (candidateMatchingOperations.Count > 1) { candidateMatchingOperations = candidateMatchingOperations.FilterBoundOperationsWithSameTypeHierarchyToTypeClosestToBindingType(bindingType).ToList(); } // If any of the things returned are an action, it better be the only thing returned, and there can't be parameters in the URL if (candidateMatchingOperations.Any(f => f.IsAction())) { if (candidateMatchingOperations.Count > 1) { if (candidateMatchingOperations.Any(o => o.IsFunction())) { throw new ODataException(ODataErrorStrings.FunctionOverloadResolver_MultipleOperationOverloads(identifier)); } else { throw new ODataException(ODataErrorStrings.FunctionOverloadResolver_MultipleActionOverloads(identifier)); } } if (parameterNames.Count() != 0) { throw ExceptionUtil.CreateBadRequestError(ODataErrorStrings.RequestUriProcessor_SegmentDoesNotSupportKeyPredicates(identifier)); } matchingOperation = candidateMatchingOperations.Single(); return true; } if (foundActionsWhenLookingForFunctions.Count > 0) { throw ExceptionUtil.CreateBadRequestError(ODataErrorStrings.RequestUriProcessor_SegmentDoesNotSupportKeyPredicates(identifier)); } if (candidateMatchingOperations.Count > 1) { throw new ODataException(ODataErrorStrings.FunctionOverloadResolver_NoSingleMatchFound(identifier, string.Join(",", parameterNames.ToArray()))); } matchingOperation = candidateMatchingOperations.SingleOrDefault(); return matchingOperation != null; }
/// <summary> /// Try to get an IEdmTypeReference for a given type as a string, returns null if none exists /// </summary> /// <param name="model">the model for validation</param> /// <param name="fullTypeName">the type name to find</param> /// <param name="resolver">Resolver for this func.</param> /// <returns>an IEdmTypeReference for this type string.</returns> private static IEdmTypeReference TryGetTypeReference(IEdmModel model, string fullTypeName, ODataUriResolver resolver) { IEdmTypeReference typeReference = UriEdmHelpers.FindTypeFromModel(model, fullTypeName, resolver).ToTypeReference(); if (typeReference == null) { if (fullTypeName.StartsWith("Collection", StringComparison.Ordinal)) { string[] tokenizedString = fullTypeName.Split('('); string baseElementType = tokenizedString[1].Split(')')[0]; return EdmCoreModel.GetCollection(UriEdmHelpers.FindTypeFromModel(model, baseElementType, resolver).ToTypeReference()); } else { return null; } } return typeReference; }
/// <summary> /// Creates an instance of this class to parse options. /// </summary> /// <param name="resolver">The URI resolver which will resolve different kinds of Uri parsing context</param> /// <param name="parentEntityType">The parent entity type for expand option</param> /// <param name="maxRecursionDepth">Max recursion depth left.</param> /// <param name="enableCaseInsensitiveBuiltinIdentifier">Whether to allow case insensitive for builtin identifier.</param> internal ExpandOptionParser(ODataUriResolver resolver, IEdmStructuredType parentEntityType, int maxRecursionDepth, bool enableCaseInsensitiveBuiltinIdentifier = false) : this(maxRecursionDepth, enableCaseInsensitiveBuiltinIdentifier) { this.resolver = resolver; this.parentEntityType = parentEntityType; }
/// <summary> /// Constructs a BinaryOperatorBinder with the given method to be used binding the parent token if needed. /// </summary> /// <param name="bindMethod">Method to use for binding the parent token, if needed.</param> /// <param name="resolver">Resolver for parsing.</param> internal BinaryOperatorBinder(Func<QueryToken, QueryNode> bindMethod, ODataUriResolver resolver) { this.bindMethod = bindMethod; this.resolver = resolver ?? ODataUriResolver.Default; }
/// <summary> /// Parses the key properties based on the segment's target type, then creates a new segment for the key. /// </summary> /// <param name="segment">The segment to apply the key to.</param> /// <param name="previousKeySegment">The parent node's key segment.</param> /// <param name="key">The key to apply.</param> /// <param name="resolver">The resolver to use.</param> /// <returns>The newly created key segment.</returns> private static KeySegment CreateKeySegment(ODataPathSegment segment, KeySegment previousKeySegment, SegmentArgumentParser key, ODataUriResolver resolver) { Debug.Assert(segment != null, "segment != null"); Debug.Assert(key != null && !key.IsEmpty, "key != null && !key.IsEmpty"); Debug.Assert(segment.SingleResult == false, "segment.SingleResult == false"); IEdmEntityType targetEntityType = null; if (!(segment.TargetEdmType != null && segment.TargetEdmType.IsEntityOrEntityCollectionType(out targetEntityType))) { throw ExceptionUtil.CreateSyntaxError(); } Debug.Assert(targetEntityType != null, "targetEntityType != null"); // Make sure the keys specified in the uri matches with the number of keys in the metadata var keyProperties = targetEntityType.Key().ToList(); if (keyProperties.Count != key.ValueCount) { NavigationPropertySegment currentNavPropSegment = segment as NavigationPropertySegment; if (currentNavPropSegment != null) { key = KeyFinder.FindAndUseKeysFromRelatedSegment(key, keyProperties, currentNavPropSegment.NavigationProperty, previousKeySegment); } // if we still didn't find any keys, then throw an error. if (keyProperties.Count != key.ValueCount && resolver.GetType() == typeof(ODataUriResolver)) { throw ExceptionUtil.CreateBadRequestError(ErrorStrings.BadRequest_KeyCountMismatch(targetEntityType.FullName())); } } if (!key.AreValuesNamed && key.ValueCount > 1 && resolver.GetType() == typeof(ODataUriResolver)) { throw ExceptionUtil.CreateBadRequestError(ErrorStrings.RequestUriProcessor_KeysMustBeNamed); } IEnumerable<KeyValuePair<string, object>> keyPairs; if (!key.TryConvertValues(targetEntityType, out keyPairs, resolver)) { throw ExceptionUtil.CreateSyntaxError(); } IEdmEntityType entityType; bool isEntity = segment.TargetEdmType.IsEntityOrEntityCollectionType(out entityType); Debug.Assert(isEntity, "Key target type should be an entity type."); var keySegment = new KeySegment(keyPairs, entityType, segment.TargetEdmNavigationSource); keySegment.CopyValuesFrom(segment); keySegment.SingleResult = true; return keySegment; }
protected void TestCurrentExtension(ODataUriParser parser, ODataUriResolver resolver) { this.TestExtension(parser, resolver, p => p.ParsePath(), QueryNodeToStringVisitor.ToString, this.ApprovalVerify); }
/// <summary> /// Build the ExpandOption strategy (SelectOption build does not need resolover and parentEntityType now). /// </summary> /// <param name="resolver">the URI resolver which will resolve different kinds of Uri parsing context</param> /// <param name="clauseToParse">the clause to parse</param> /// <param name="parentEntityType">the parent entity type for expand option</param> /// <param name="maxRecursiveDepth">max recursive depth</param> /// <param name="enableCaseInsensitiveBuiltinIdentifier">Whether to allow case insensitive for builtin identifier.</param> public SelectExpandParser(ODataUriResolver resolver, string clauseToParse, IEdmStructuredType parentEntityType, int maxRecursiveDepth, bool enableCaseInsensitiveBuiltinIdentifier = false) : this(clauseToParse, maxRecursiveDepth, enableCaseInsensitiveBuiltinIdentifier) { this.resolver = resolver; this.parentEntityType = parentEntityType; }