From acd66423f4c10cdab8548abf930f0d3d285be864 Mon Sep 17 00:00:00 2001 From: Garrett DeBruin Date: Tue, 2 Nov 2021 21:35:52 -0700 Subject: [PATCH 1/4] optimized some counting so that we don't enumerate multiple times --- .../CsdlSemanticsVocabularyAnnotation.cs | 106 ++++++++++++++++-- 1 file changed, 94 insertions(+), 12 deletions(-) diff --git a/src/Microsoft.OData.Edm/Csdl/Semantics/CsdlSemanticsVocabularyAnnotation.cs b/src/Microsoft.OData.Edm/Csdl/Semantics/CsdlSemanticsVocabularyAnnotation.cs index 6431b7078b..ba85ad14c5 100644 --- a/src/Microsoft.OData.Edm/Csdl/Semantics/CsdlSemanticsVocabularyAnnotation.cs +++ b/src/Microsoft.OData.Edm/Csdl/Semantics/CsdlSemanticsVocabularyAnnotation.cs @@ -456,19 +456,18 @@ private IEdmVocabularyAnnotatable ComputeTarget() private static IEdmOperationImport FindParameterizedOperationImport(string parameterizedName, Func> findFunctions, Func, IEdmOperationImport> ambiguityCreator) { IEnumerable matchingFunctions = findFunctions(parameterizedName); - if (!matchingFunctions.Any()) + var length = TryCount(matchingFunctions, out var value); + if (length == 0) { return null; } - - if (matchingFunctions.Count() == 1) + else if (length == 1) { - return matchingFunctions.First(); + return value; } else { - IEdmOperationImport ambiguous = ambiguityCreator(matchingFunctions); - return ambiguous; + return ambiguityCreator(matchingFunctions); } } @@ -484,19 +483,18 @@ private IEdmOperation FindParameterizedOperation(string parameterizedName, Func< string name = parameterizedName.Substring(0, openParen); string[] parameters = parameterizedName.Substring(openParen + 1, closeParen - (openParen + 1)).Split(new string[] { ", " }, StringSplitOptions.RemoveEmptyEntries); IEnumerable matchingFunctions = this.FindParameterizedOperationFromList(findFunctions(name).Cast(), parameters); - if (!matchingFunctions.Any()) + var length = TryCount(matchingFunctions, out var value); + if (length == 0) { return null; } - - if (matchingFunctions.Count() == 1) + else if (length == 1) { - return matchingFunctions.First(); + return value; } else { - IEdmOperation ambiguous = ambiguityCreator(matchingFunctions); - return ambiguous; + return ambiguityCreator(matchingFunctions); } } @@ -577,5 +575,89 @@ private IEnumerable FindParameterizedOperationFromList(IEnumerabl return matchingOperations; } + + private sealed class Wrapper : IEnumerable + { + private readonly IEnumerable source; + + private readonly List enumerated; + + private int atLeast; + + public Wrapper(IEnumerable source) + { + this.source = source; + + this.enumerated = new List(); + } + + public bool AtLeast(int n) + { + if (this.atLeast >= n) + { + return true; + } + + using (var enumerator = this.GetEnumerator()) + { + int i; + for (i = 0; enumerator.MoveNext() && i < n; ++i) + { + } + + if (i == n) + { + return true; + } + } + + return false; + } + + public IReadOnlyList Enumerated + { + get + { + return this.enumerated; + } + } + + public IEnumerator GetEnumerator() + { + foreach (var element in this.source) + { + ++this.atLeast; + this.enumerated.Add(element); + yield return element; + } + } + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return this.GetEnumerator(); + } + } + + private static int TryCount(IEnumerable source, out T value) + { + using (var enumerator = source.GetEnumerator()) + { + int length; + for (length = 0; length < 2 && enumerator.MoveNext(); ++length) + { + } + + if (length == 1) + { + value = enumerator.Current; + } + else + { + value = default; + } + + return length; + } + } } } From bf70b1318e4fba759555789f700a3f0ca0b22a86 Mon Sep 17 00:00:00 2001 From: Garrett DeBruin Date: Wed, 3 Nov 2021 15:28:35 -0700 Subject: [PATCH 2/4] asdf --- .../CsdlSemanticsVocabularyAnnotation.cs | 68 ++----------------- 1 file changed, 5 insertions(+), 63 deletions(-) diff --git a/src/Microsoft.OData.Edm/Csdl/Semantics/CsdlSemanticsVocabularyAnnotation.cs b/src/Microsoft.OData.Edm/Csdl/Semantics/CsdlSemanticsVocabularyAnnotation.cs index ba85ad14c5..39118ec384 100644 --- a/src/Microsoft.OData.Edm/Csdl/Semantics/CsdlSemanticsVocabularyAnnotation.cs +++ b/src/Microsoft.OData.Edm/Csdl/Semantics/CsdlSemanticsVocabularyAnnotation.cs @@ -576,70 +576,12 @@ private IEnumerable FindParameterizedOperationFromList(IEnumerabl return matchingOperations; } - private sealed class Wrapper : IEnumerable - { - private readonly IEnumerable source; - - private readonly List enumerated; - - private int atLeast; - - public Wrapper(IEnumerable source) - { - this.source = source; - - this.enumerated = new List(); - } - - public bool AtLeast(int n) - { - if (this.atLeast >= n) - { - return true; - } - - using (var enumerator = this.GetEnumerator()) - { - int i; - for (i = 0; enumerator.MoveNext() && i < n; ++i) - { - } - - if (i == n) - { - return true; - } - } - - return false; - } - - public IReadOnlyList Enumerated - { - get - { - return this.enumerated; - } - } - - public IEnumerator GetEnumerator() - { - foreach (var element in this.source) - { - ++this.atLeast; - this.enumerated.Add(element); - yield return element; - } - } - - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() - { - return this.GetEnumerator(); - } - } - private static int TryCount(IEnumerable source, out T value) { + //// TODO check build from https://github.com/OData/odata.net/pull/2237 + //// TODO benchmark this + //// TODO ensure that there are existing tests covering this + //// TODO add prepend stuff so that you are sure to enumerate only once? using (var enumerator = source.GetEnumerator()) { int length; @@ -653,7 +595,7 @@ private static int TryCount(IEnumerable source, out T value) } else { - value = default; + value = default(T); } return length; From f8e107f7cb3357a2d516b3cda9b5160c90b4aadf Mon Sep 17 00:00:00 2001 From: Garrett DeBruin Date: Thu, 4 Nov 2021 12:05:26 -0700 Subject: [PATCH 3/4] asdf --- .../CsdlSemanticsVocabularyAnnotation.cs | 161 +----------------- 1 file changed, 1 insertion(+), 160 deletions(-) diff --git a/src/Microsoft.OData.Edm/Csdl/Semantics/CsdlSemanticsVocabularyAnnotation.cs b/src/Microsoft.OData.Edm/Csdl/Semantics/CsdlSemanticsVocabularyAnnotation.cs index 39118ec384..2f3cfe510a 100644 --- a/src/Microsoft.OData.Edm/Csdl/Semantics/CsdlSemanticsVocabularyAnnotation.cs +++ b/src/Microsoft.OData.Edm/Csdl/Semantics/CsdlSemanticsVocabularyAnnotation.cs @@ -290,166 +290,7 @@ private IEdmVocabularyAnnotatable ComputeTarget() } else { - Debug.Assert(this.annotationsContext != null, "Annotation must either have a target context or annotations context"); - string target = this.annotationsContext.Annotations.Target; - string[] targetSegments = target.Split('/'); - int targetSegmentsCount = targetSegments.Length; - IEdmEntityContainer container; - - if (targetSegmentsCount == 1) - { - string elementName = targetSegments[0]; - IEdmSchemaType type = this.model.FindType(elementName); - if (type != null) - { - return type; - } - - IEdmTerm term = this.model.FindTerm(elementName); - if (term != null) - { - return term; - } - - IEdmOperation operation = this.FindParameterizedOperation(elementName, this.model.FindOperations, this.CreateAmbiguousOperation); - if (operation != null) - { - return operation; - } - - container = this.model.FindEntityContainer(elementName); - if (container != null) - { - return container; - } - - return new UnresolvedType(this.model.ReplaceAlias(targetSegments[0]), this.Location); - } - - if (targetSegmentsCount == 2) - { - container = this.model.FindEntityContainer(targetSegments[0]); - if (container != null) - { - // Using the methods here results in a much faster lookup as it uses a dictionary instead of using the list of container elements. - IEdmEntityContainerElement containerElement = container.FindEntitySetExtended(targetSegments[1]) - ?? container.FindSingletonExtended(targetSegments[1]) as IEdmEntityContainerElement; - if (containerElement != null) - { - return containerElement; - } - - IEdmOperationImport operationImport = FindParameterizedOperationImport(targetSegments[1], container.FindOperationImportsExtended, this.CreateAmbiguousOperationImport); - if (operationImport != null) - { - return operationImport; - } - - return new UnresolvedEntitySet(targetSegments[1], container, this.Location); - } - - IEdmSchemaType type = this.model.FindType(targetSegments[0]); - if (type != null) - { - IEdmStructuredType structuredType; - IEdmEnumType enumType; - if ((structuredType = type as IEdmStructuredType) != null) - { - IEdmProperty property = structuredType.FindProperty(targetSegments[1]); - if (property != null) - { - return property; - } - - return new UnresolvedProperty(structuredType, targetSegments[1], this.Location); - } - else if ((enumType = type as IEdmEnumType) != null) - { - foreach (IEdmEnumMember member in enumType.Members) - { - if (String.Equals(member.Name, targetSegments[1], StringComparison.OrdinalIgnoreCase)) - { - return member; - } - } - - return new UnresolvedEnumMember(targetSegments[1], enumType, this.Location); - } - } - - IEdmOperation operation = this.FindParameterizedOperation(targetSegments[0], this.model.FindOperations, this.CreateAmbiguousOperation); - if (operation != null) - { - // $ReturnType - if (targetSegments[1] == CsdlConstants.OperationReturnExternalTarget) - { - if (operation.ReturnType != null) - { - return operation.GetReturn(); - } - - return new UnresolvedReturn(operation, this.Location); - } - - IEdmOperationParameter parameter = operation.FindParameter(targetSegments[1]); - if (parameter != null) - { - return parameter; - } - - return new UnresolvedParameter(operation, targetSegments[1], this.Location); - } - - return new UnresolvedProperty(new UnresolvedEntityType(this.model.ReplaceAlias(targetSegments[0]), this.Location), targetSegments[1], this.Location); - } - - if (targetSegmentsCount == 3) - { - // The only valid target with three segments is a function parameter, or an operation return. - string containerName = targetSegments[0]; - string operationName = targetSegments[1]; - string parameterName = targetSegments[2]; - - container = this.Model.FindEntityContainer(containerName); - if (container != null) - { - IEdmOperationImport operationImport = FindParameterizedOperationImport(operationName, container.FindOperationImportsExtended, this.CreateAmbiguousOperationImport); - if (operationImport != null) - { - // $ReturnType - if (parameterName == CsdlConstants.OperationReturnExternalTarget) - { - if (operationImport.Operation.ReturnType != null) - { - return operationImport.Operation.GetReturn(); - } - - return new UnresolvedReturn(operationImport.Operation, this.Location); - } - - IEdmOperationParameter parameter = operationImport.Operation.FindParameter(parameterName); - if (parameter != null) - { - return parameter; - } - - return new UnresolvedParameter(operationImport.Operation, parameterName, this.Location); - } - } - - string qualifiedOperationName = containerName + "/" + operationName; - UnresolvedOperation unresolvedOperation = new UnresolvedOperation(qualifiedOperationName, Edm.Strings.Bad_UnresolvedOperation(qualifiedOperationName), this.Location); - if (parameterName == CsdlConstants.OperationReturnExternalTarget) - { - return new UnresolvedReturn(unresolvedOperation, this.Location); - } - else - { - return new UnresolvedParameter(unresolvedOperation, parameterName, this.Location); - } - } - - return new BadElement(new EdmError[] { new EdmError(this.Location, EdmErrorCode.ImpossibleAnnotationsTarget, Edm.Strings.CsdlSemantics_ImpossibleAnnotationsTarget(target)) }); + throw new InvalidOperationException(); } } From d1fb2f011b16b53cbd1aee05b1074fc0383390cb Mon Sep 17 00:00:00 2001 From: Garrett DeBruin Date: Thu, 4 Nov 2021 18:40:44 -0700 Subject: [PATCH 4/4] asdf --- .../CsdlSemanticsVocabularyAnnotation.cs | 161 +++++++++++++++++- 1 file changed, 160 insertions(+), 1 deletion(-) diff --git a/src/Microsoft.OData.Edm/Csdl/Semantics/CsdlSemanticsVocabularyAnnotation.cs b/src/Microsoft.OData.Edm/Csdl/Semantics/CsdlSemanticsVocabularyAnnotation.cs index 2f3cfe510a..39118ec384 100644 --- a/src/Microsoft.OData.Edm/Csdl/Semantics/CsdlSemanticsVocabularyAnnotation.cs +++ b/src/Microsoft.OData.Edm/Csdl/Semantics/CsdlSemanticsVocabularyAnnotation.cs @@ -290,7 +290,166 @@ private IEdmVocabularyAnnotatable ComputeTarget() } else { - throw new InvalidOperationException(); + Debug.Assert(this.annotationsContext != null, "Annotation must either have a target context or annotations context"); + string target = this.annotationsContext.Annotations.Target; + string[] targetSegments = target.Split('/'); + int targetSegmentsCount = targetSegments.Length; + IEdmEntityContainer container; + + if (targetSegmentsCount == 1) + { + string elementName = targetSegments[0]; + IEdmSchemaType type = this.model.FindType(elementName); + if (type != null) + { + return type; + } + + IEdmTerm term = this.model.FindTerm(elementName); + if (term != null) + { + return term; + } + + IEdmOperation operation = this.FindParameterizedOperation(elementName, this.model.FindOperations, this.CreateAmbiguousOperation); + if (operation != null) + { + return operation; + } + + container = this.model.FindEntityContainer(elementName); + if (container != null) + { + return container; + } + + return new UnresolvedType(this.model.ReplaceAlias(targetSegments[0]), this.Location); + } + + if (targetSegmentsCount == 2) + { + container = this.model.FindEntityContainer(targetSegments[0]); + if (container != null) + { + // Using the methods here results in a much faster lookup as it uses a dictionary instead of using the list of container elements. + IEdmEntityContainerElement containerElement = container.FindEntitySetExtended(targetSegments[1]) + ?? container.FindSingletonExtended(targetSegments[1]) as IEdmEntityContainerElement; + if (containerElement != null) + { + return containerElement; + } + + IEdmOperationImport operationImport = FindParameterizedOperationImport(targetSegments[1], container.FindOperationImportsExtended, this.CreateAmbiguousOperationImport); + if (operationImport != null) + { + return operationImport; + } + + return new UnresolvedEntitySet(targetSegments[1], container, this.Location); + } + + IEdmSchemaType type = this.model.FindType(targetSegments[0]); + if (type != null) + { + IEdmStructuredType structuredType; + IEdmEnumType enumType; + if ((structuredType = type as IEdmStructuredType) != null) + { + IEdmProperty property = structuredType.FindProperty(targetSegments[1]); + if (property != null) + { + return property; + } + + return new UnresolvedProperty(structuredType, targetSegments[1], this.Location); + } + else if ((enumType = type as IEdmEnumType) != null) + { + foreach (IEdmEnumMember member in enumType.Members) + { + if (String.Equals(member.Name, targetSegments[1], StringComparison.OrdinalIgnoreCase)) + { + return member; + } + } + + return new UnresolvedEnumMember(targetSegments[1], enumType, this.Location); + } + } + + IEdmOperation operation = this.FindParameterizedOperation(targetSegments[0], this.model.FindOperations, this.CreateAmbiguousOperation); + if (operation != null) + { + // $ReturnType + if (targetSegments[1] == CsdlConstants.OperationReturnExternalTarget) + { + if (operation.ReturnType != null) + { + return operation.GetReturn(); + } + + return new UnresolvedReturn(operation, this.Location); + } + + IEdmOperationParameter parameter = operation.FindParameter(targetSegments[1]); + if (parameter != null) + { + return parameter; + } + + return new UnresolvedParameter(operation, targetSegments[1], this.Location); + } + + return new UnresolvedProperty(new UnresolvedEntityType(this.model.ReplaceAlias(targetSegments[0]), this.Location), targetSegments[1], this.Location); + } + + if (targetSegmentsCount == 3) + { + // The only valid target with three segments is a function parameter, or an operation return. + string containerName = targetSegments[0]; + string operationName = targetSegments[1]; + string parameterName = targetSegments[2]; + + container = this.Model.FindEntityContainer(containerName); + if (container != null) + { + IEdmOperationImport operationImport = FindParameterizedOperationImport(operationName, container.FindOperationImportsExtended, this.CreateAmbiguousOperationImport); + if (operationImport != null) + { + // $ReturnType + if (parameterName == CsdlConstants.OperationReturnExternalTarget) + { + if (operationImport.Operation.ReturnType != null) + { + return operationImport.Operation.GetReturn(); + } + + return new UnresolvedReturn(operationImport.Operation, this.Location); + } + + IEdmOperationParameter parameter = operationImport.Operation.FindParameter(parameterName); + if (parameter != null) + { + return parameter; + } + + return new UnresolvedParameter(operationImport.Operation, parameterName, this.Location); + } + } + + string qualifiedOperationName = containerName + "/" + operationName; + UnresolvedOperation unresolvedOperation = new UnresolvedOperation(qualifiedOperationName, Edm.Strings.Bad_UnresolvedOperation(qualifiedOperationName), this.Location); + if (parameterName == CsdlConstants.OperationReturnExternalTarget) + { + return new UnresolvedReturn(unresolvedOperation, this.Location); + } + else + { + return new UnresolvedParameter(unresolvedOperation, parameterName, this.Location); + } + } + + return new BadElement(new EdmError[] { new EdmError(this.Location, EdmErrorCode.ImpossibleAnnotationsTarget, Edm.Strings.CsdlSemantics_ImpossibleAnnotationsTarget(target)) }); } }