diff --git a/src/Microsoft.OData.Edm/Csdl/SchemaWriter.cs b/src/Microsoft.OData.Edm/Csdl/SchemaWriter.cs index d80851a494..e8809803bf 100644 --- a/src/Microsoft.OData.Edm/Csdl/SchemaWriter.cs +++ b/src/Microsoft.OData.Edm/Csdl/SchemaWriter.cs @@ -106,7 +106,7 @@ internal static async Task>> TryWriteSchemaAsy return Tuple.Create(false, errors); } - IEnumerable schemas = new EdmModelSchemaSeparationSerializationVisitor(model).GetSchemas(); + IEnumerable schemas = await (new EdmModelSchemaSeparationSerializationVisitor(model)).GetSchemasAsync().ConfigureAwait(false); if (schemas.Count() > 1 && singleFileExpected) { errors = new EdmError[] { new EdmError(new CsdlLocation(0, 0), EdmErrorCode.SingleFileExpected, Edm.Strings.Serializer_SingleFileExpected) }; diff --git a/src/Microsoft.OData.Edm/Csdl/Serialization/EdmModelCsdlSerializationVisitor.cs b/src/Microsoft.OData.Edm/Csdl/Serialization/EdmModelCsdlSerializationVisitor.cs index 82d133325b..3a32fecd36 100644 --- a/src/Microsoft.OData.Edm/Csdl/Serialization/EdmModelCsdlSerializationVisitor.cs +++ b/src/Microsoft.OData.Edm/Csdl/Serialization/EdmModelCsdlSerializationVisitor.cs @@ -180,18 +180,18 @@ internal async Task VisitEdmSchemaAsync(EdmSchema element, IEnumerable> operation in element.SchemaOperations) { await this.schemaWriter.WriteSchemaOperationsHeaderAsync(operation).ConfigureAwait(false); - VisitSchemaElements(operation.Value.AsEnumerable()); // Call AsEnumerable() to make .net 3.5 happy + await VisitSchemaElementsAsync(operation.Value.AsEnumerable()).ConfigureAwait(false); await this.schemaWriter.WriteSchemaOperationsEndAsync(operation).ConfigureAwait(false); } // EntityContainers are excluded from the EdmSchema.SchemaElements property so they can be forced to the end. - VisitCollection(element.EntityContainers, async (e) => await this.ProcessEntityContainerAsync(e).ConfigureAwait(false)); + await VisitCollectionAsync(element.EntityContainers, this.ProcessEntityContainerAsync).ConfigureAwait(false); if (element.OutOfLineAnnotations.Any()) { @@ -304,8 +304,8 @@ protected override async Task ProcessEntityTypeAsync(IEdmEntityType element) await this.VisitEntityTypeDeclaredKeyAsync(element.DeclaredKey).ConfigureAwait(false); } - this.VisitProperties(element.DeclaredStructuralProperties().Cast()); - this.VisitProperties(element.DeclaredNavigationProperties().Cast()); + await this.VisitPropertiesAsync(element.DeclaredStructuralProperties().Cast()).ConfigureAwait(false); + await this.VisitPropertiesAsync(element.DeclaredNavigationProperties().Cast()).ConfigureAwait(false); await this.EndElementAsync(element).ConfigureAwait(false); } @@ -331,7 +331,7 @@ protected override async Task ProcessStructuralPropertyAsync(IEdmStructuralPrope await this.BeginElementAsync(element, (IEdmStructuralProperty t) => this.schemaWriter.WriteStructuralPropertyElementHeaderAsync(t, inlineType), e => this.ProcessFacetsAsync(e.Type, inlineType)).ConfigureAwait(false); if (!inlineType) { - VisitTypeReference(element.Type); + await VisitTypeReferenceAsync(element.Type).ConfigureAwait(false); } await this.EndElementAsync(element).ConfigureAwait(false); @@ -558,7 +558,7 @@ protected override async Task ProcessTermAsync(IEdmTerm term) { if (term.Type != null) { - VisitTypeReference(term.Type); + await VisitTypeReferenceAsync(term.Type).ConfigureAwait(false); } } @@ -629,7 +629,7 @@ await this.BeginElementAsync( ).ConfigureAwait(false); if (!inlineType) { - VisitTypeReference(element.Type); + await VisitTypeReferenceAsync(element.Type).ConfigureAwait(false); } await this.VisitPrimitiveElementAnnotationsAsync(this.Model.DirectValueAnnotations(element)).ConfigureAwait(false); @@ -691,7 +691,7 @@ await this.BeginElementAsync( } else { - this.VisitTypeReference(type); + await this.VisitTypeReferenceAsync(type).ConfigureAwait(false); } }).ConfigureAwait(false); await this.EndElementAsync(operationReturn).ConfigureAwait(false); @@ -726,7 +726,7 @@ await this.BeginElementAsync( ).ConfigureAwait(false); if (!inlineType) { - VisitTypeReference(element.ElementType); + await VisitTypeReferenceAsync(element.ElementType).ConfigureAwait(false); } await this.EndElementAsync(element).ConfigureAwait(false); @@ -1031,7 +1031,7 @@ protected override async Task ProcessIsTypeExpressionAsync(IEdmIsTypeExpression if (!inlineType) { - VisitTypeReference(expression.Type); + await VisitTypeReferenceAsync(expression.Type).ConfigureAwait(false); } this.VisitExpression(expression.Operand); @@ -1279,7 +1279,7 @@ protected override async Task ProcessCastExpressionAsync(IEdmCastExpression expr if (!inlineType) { - VisitTypeReference(expression.Type); + await VisitTypeReferenceAsync(expression.Type).ConfigureAwait(false); } this.VisitExpression(expression.Operand); @@ -1412,7 +1412,7 @@ private async Task ProcessOperationAsync(TOperation operation, Func< await this.BeginElementAsync(operation, writeElementAction).ConfigureAwait(false); await this.schemaWriter.WriteOperationParametersBeginAsync(operation.Parameters).ConfigureAwait(false); - this.VisitOperationParameters(operation.Parameters); + await this.VisitOperationParametersAsync(operation.Parameters).ConfigureAwait(false); await this.schemaWriter.WriteOperationParametersEndAsync(operation.Parameters).ConfigureAwait(false); IEdmOperationReturn operationReturn = operation.GetReturn(); @@ -1501,12 +1501,12 @@ private async Task ProcessFacetsAsync(IEdmTypeReference element, bool inlineType { IEdmCollectionTypeReference collectionElement = element.AsCollection(); await this.schemaWriter.WriteNullableAttributeAsync(collectionElement.CollectionDefinition().ElementType).ConfigureAwait(false); - VisitTypeReference(collectionElement.CollectionDefinition().ElementType); + await VisitTypeReferenceAsync(collectionElement.CollectionDefinition().ElementType).ConfigureAwait(false); } else { await this.schemaWriter.WriteNullableAttributeAsync(element).ConfigureAwait(false); - VisitTypeReference(element); + await VisitTypeReferenceAsync(element).ConfigureAwait(false); } } } diff --git a/src/Microsoft.OData.Edm/Csdl/Serialization/EdmModelSchemaSeparationSerializationVisitor.cs b/src/Microsoft.OData.Edm/Csdl/Serialization/EdmModelSchemaSeparationSerializationVisitor.cs index 4eb8a323b0..59437dbc64 100644 --- a/src/Microsoft.OData.Edm/Csdl/Serialization/EdmModelSchemaSeparationSerializationVisitor.cs +++ b/src/Microsoft.OData.Edm/Csdl/Serialization/EdmModelSchemaSeparationSerializationVisitor.cs @@ -6,6 +6,8 @@ using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; +using Microsoft.OData.Edm.Helpers; using Microsoft.OData.Edm.Vocabularies; namespace Microsoft.OData.Edm.Csdl.Serialization @@ -31,12 +33,28 @@ public IEnumerable GetSchemas() return this.modelSchemas.Values; } + public async Task> GetSchemasAsync() + { + if (!this.visitCompleted) + { + await this.VisitAsync().ConfigureAwait(false); + } + + return this.modelSchemas.Values; + } + protected void Visit() { this.VisitEdmModel(); this.visitCompleted = true; } + protected async Task VisitAsync() + { + await this.VisitEdmModelAsync().ConfigureAwait(false); + this.visitCompleted = true; + } + protected override void ProcessModel(IEdmModel model) { this.ProcessElement(model); @@ -44,12 +62,25 @@ protected override void ProcessModel(IEdmModel model) this.VisitVocabularyAnnotations(model.VocabularyAnnotations.Where(a => !a.IsInline(this.Model))); } + protected override async Task ProcessModelAsync(IEdmModel model) + { + await this.ProcessElementAsync(model).ConfigureAwait(false); + await this.VisitSchemaElementsAsync(model.SchemaElements).ConfigureAwait(false); + await this.VisitVocabularyAnnotationsAsync(model.VocabularyAnnotations.Where(a => !a.IsInline(this.Model))).ConfigureAwait(false); + } + protected override void ProcessVocabularyAnnotatable(IEdmVocabularyAnnotatable element) { this.VisitAnnotations(this.Model.DirectValueAnnotations(element)); this.VisitVocabularyAnnotations(this.Model.FindDeclaredVocabularyAnnotations(element).Where(a => a.IsInline(this.Model))); } + protected override async Task ProcessVocabularyAnnotatableAsync(IEdmVocabularyAnnotatable element) + { + await this.VisitAnnotationsAsync(this.Model.DirectValueAnnotations(element)).ConfigureAwait(false); + await this.VisitVocabularyAnnotationsAsync(this.Model.FindDeclaredVocabularyAnnotations(element).Where(a => a.IsInline(this.Model))).ConfigureAwait(false); + } + protected override void ProcessSchemaElement(IEdmSchemaElement element) { string namespaceName = element.Namespace; @@ -73,6 +104,29 @@ protected override void ProcessSchemaElement(IEdmSchemaElement element) base.ProcessSchemaElement(element); } + protected override Task ProcessSchemaElementAsync(IEdmSchemaElement element) + { + string namespaceName = element.Namespace; + + // Put all of the namespaceless stuff into one schema. + if (EdmUtil.IsNullOrWhiteSpaceInternal(namespaceName)) + { + namespaceName = string.Empty; + } + + EdmSchema schema; + if (!this.modelSchemas.TryGetValue(namespaceName, out schema)) + { + schema = new EdmSchema(namespaceName); + this.modelSchemas.Add(namespaceName, schema); + } + + schema.AddSchemaElement(element); + this.activeSchema = schema; + + return base.ProcessSchemaElementAsync(element); + } + protected override void ProcessVocabularyAnnotation(IEdmVocabularyAnnotation annotation) { if (!annotation.IsInline(this.Model)) @@ -98,6 +152,31 @@ protected override void ProcessVocabularyAnnotation(IEdmVocabularyAnnotation ann base.ProcessVocabularyAnnotation(annotation); } + protected override async Task ProcessVocabularyAnnotationAsync(IEdmVocabularyAnnotation annotation) + { + if (!annotation.IsInline(this.Model)) + { + var annotationSchemaNamespace = annotation.GetSchemaNamespace(this.Model) ?? this.modelSchemas.Select(s => s.Key).FirstOrDefault() ?? string.Empty; + + EdmSchema annotationSchema; + if (!this.modelSchemas.TryGetValue(annotationSchemaNamespace, out annotationSchema)) + { + annotationSchema = new EdmSchema(annotationSchemaNamespace); + this.modelSchemas.Add(annotationSchema.Namespace, annotationSchema); + } + + annotationSchema.AddVocabularyAnnotation(annotation); + this.activeSchema = annotationSchema; + } + + if (annotation.Term != null) + { + await this.CheckSchemaElementReferenceAsync(annotation.Term).ConfigureAwait(false); + } + + await base.ProcessVocabularyAnnotationAsync(annotation).ConfigureAwait(false); + } + /// /// When we see an entity container, we see if it has . /// If it does, then we attach it to that schema, otherwise we attached to the first existing schema. @@ -121,31 +200,80 @@ protected override void ProcessEntityContainer(IEdmEntityContainer element) base.ProcessEntityContainer(element); } + /// + /// When we see an entity container, we see if it has . + /// If it does, then we attach it to that schema, otherwise we attached to the first existing schema. + /// If there are no schemas, we create the one named "Default" and attach container to it. + /// + /// The entity container being processed. + /// A task that represents the asynchronous operation. + protected override Task ProcessEntityContainerAsync(IEdmEntityContainer element) + { + var containerSchemaNamespace = element.Namespace; + + EdmSchema containerSchema; + if (!this.modelSchemas.TryGetValue(containerSchemaNamespace, out containerSchema)) + { + containerSchema = new EdmSchema(containerSchemaNamespace); + this.modelSchemas.Add(containerSchema.Namespace, containerSchema); + } + + containerSchema.AddEntityContainer(element); + this.activeSchema = containerSchema; + + return base.ProcessEntityContainerAsync(element); + } + protected override void ProcessComplexTypeReference(IEdmComplexTypeReference element) { this.CheckSchemaElementReference(element.ComplexDefinition()); } + protected override Task ProcessComplexTypeReferenceAsync(IEdmComplexTypeReference element) + { + return this.CheckSchemaElementReferenceAsync(element.ComplexDefinition()); + } + protected override void ProcessEntityTypeReference(IEdmEntityTypeReference element) { this.CheckSchemaElementReference(element.EntityDefinition()); } + protected override Task ProcessEntityTypeReferenceAsync(IEdmEntityTypeReference element) + { + return this.CheckSchemaElementReferenceAsync(element.EntityDefinition()); + } + protected override void ProcessEntityReferenceTypeReference(IEdmEntityReferenceTypeReference element) { this.CheckSchemaElementReference(element.EntityType()); } + protected override Task ProcessEntityReferenceTypeReferenceAsync(IEdmEntityReferenceTypeReference element) + { + return this.CheckSchemaElementReferenceAsync(element.EntityType()); + } + protected override void ProcessEnumTypeReference(IEdmEnumTypeReference element) { this.CheckSchemaElementReference(element.EnumDefinition()); } + protected override Task ProcessEnumTypeReferenceAsync(IEdmEnumTypeReference element) + { + return this.CheckSchemaElementReferenceAsync(element.EnumDefinition()); + } + protected override void ProcessTypeDefinitionReference(IEdmTypeDefinitionReference element) { this.CheckSchemaElementReference(element.TypeDefinition()); } + protected override Task ProcessTypeDefinitionReferenceAsync(IEdmTypeDefinitionReference element) + { + return this.CheckSchemaElementReferenceAsync(element.TypeDefinition()); + } + protected override void ProcessEntityType(IEdmEntityType element) { base.ProcessEntityType(element); @@ -155,6 +283,15 @@ protected override void ProcessEntityType(IEdmEntityType element) } } + protected override async Task ProcessEntityTypeAsync(IEdmEntityType element) + { + await base.ProcessEntityTypeAsync(element).ConfigureAwait(false); + if (element.BaseEntityType() != null) + { + await this.CheckSchemaElementReferenceAsync(element.BaseEntityType()).ConfigureAwait(false); + } + } + protected override void ProcessComplexType(IEdmComplexType element) { base.ProcessComplexType(element); @@ -164,23 +301,49 @@ protected override void ProcessComplexType(IEdmComplexType element) } } + protected override async Task ProcessComplexTypeAsync(IEdmComplexType element) + { + await base.ProcessComplexTypeAsync(element).ConfigureAwait(false); + if (element.BaseComplexType() != null) + { + await this.CheckSchemaElementReferenceAsync(element.BaseComplexType()).ConfigureAwait(false); + } + } + protected override void ProcessEnumType(IEdmEnumType element) { base.ProcessEnumType(element); this.CheckSchemaElementReference(element.UnderlyingType); } + protected override async Task ProcessEnumTypeAsync(IEdmEnumType element) + { + await base.ProcessEnumTypeAsync(element).ConfigureAwait(false); + await this.CheckSchemaElementReferenceAsync(element.UnderlyingType).ConfigureAwait(false); + } + protected override void ProcessTypeDefinition(IEdmTypeDefinition element) { base.ProcessTypeDefinition(element); this.CheckSchemaElementReference(element.UnderlyingType); } + protected override async Task ProcessTypeDefinitionAsync(IEdmTypeDefinition element) + { + await base.ProcessTypeDefinitionAsync(element).ConfigureAwait(false); + await this.CheckSchemaElementReferenceAsync(element.UnderlyingType).ConfigureAwait(false); + } + private void CheckSchemaElementReference(IEdmSchemaElement element) { this.CheckSchemaElementReference(element.Namespace); } + private Task CheckSchemaElementReferenceAsync(IEdmSchemaElement element) + { + return this.CheckSchemaElementReferenceAsync(element.Namespace); + } + private void CheckSchemaElementReference(string namespaceName) { if (this.activeSchema != null) @@ -188,5 +351,15 @@ private void CheckSchemaElementReference(string namespaceName) this.activeSchema.AddNamespaceUsing(namespaceName); } } + + private Task CheckSchemaElementReferenceAsync(string namespaceName) + { + if (this.activeSchema != null) + { + this.activeSchema.AddNamespaceUsing(namespaceName); + } + + return TaskUtils.CompletedTask; + } } } diff --git a/src/Microsoft.OData.Edm/EdmModelVisitor.cs b/src/Microsoft.OData.Edm/EdmModelVisitor.cs index 0806adfe8d..0e363d143a 100644 --- a/src/Microsoft.OData.Edm/EdmModelVisitor.cs +++ b/src/Microsoft.OData.Edm/EdmModelVisitor.cs @@ -6,6 +6,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Threading.Tasks; using Microsoft.OData.Edm.Helpers; using Microsoft.OData.Edm.Vocabularies; @@ -26,6 +27,11 @@ public void VisitEdmModel() this.ProcessModel(this.Model); } + public Task VisitEdmModelAsync() + { + return this.ProcessModelAsync(this.Model); + } + #region Visit Methods #region Elements @@ -35,6 +41,11 @@ public void VisitSchemaElements(IEnumerable elements) VisitCollection(elements, this.VisitSchemaElement); } + public Task VisitSchemaElementsAsync(IEnumerable elements) + { + return VisitCollectionAsync(elements, this.VisitSchemaElementAsync); + } + public void VisitSchemaElement(IEdmSchemaElement element) { switch (element.SchemaElementKind) @@ -62,6 +73,28 @@ public void VisitSchemaElement(IEdmSchemaElement element) } } + public Task VisitSchemaElementAsync(IEdmSchemaElement element) + { + switch (element.SchemaElementKind) + { + case EdmSchemaElementKind.Action: + return this.ProcessActionAsync((IEdmAction)element); + case EdmSchemaElementKind.Function: + return this.ProcessFunctionAsync((IEdmFunction)element); + case EdmSchemaElementKind.TypeDefinition: + return this.VisitSchemaTypeAsync((IEdmType)element); + case EdmSchemaElementKind.Term: + return this.ProcessTermAsync((IEdmTerm)element); + case EdmSchemaElementKind.EntityContainer: + return this.ProcessEntityContainerAsync((IEdmEntityContainer)element); + case EdmSchemaElementKind.None: + return this.ProcessSchemaElementAsync(element); + default: + Debug.Assert(false, Edm.Strings.UnknownEnumVal_SchemaElementKind(element.SchemaElementKind)); + return TaskUtils.GetFaultedTask(new InvalidOperationException(Edm.Strings.UnknownEnumVal_SchemaElementKind(element.SchemaElementKind))); + } + } + #endregion #region Annotations @@ -71,16 +104,31 @@ public void VisitAnnotations(IEnumerable annotations) VisitCollection(annotations, this.VisitAnnotation); } + public Task VisitAnnotationsAsync(IEnumerable annotations) + { + return VisitCollectionAsync(annotations, this.VisitAnnotationAsync); + } + public void VisitVocabularyAnnotations(IEnumerable annotations) { VisitCollection(annotations, this.VisitVocabularyAnnotation); } + public Task VisitVocabularyAnnotationsAsync(IEnumerable annotations) + { + return VisitCollectionAsync(annotations, this.VisitVocabularyAnnotationAsync); + } + public void VisitAnnotation(IEdmDirectValueAnnotation annotation) { this.ProcessImmediateValueAnnotation((IEdmDirectValueAnnotation)annotation); } + public Task VisitAnnotationAsync(IEdmDirectValueAnnotation annotation) + { + return this.ProcessImmediateValueAnnotationAsync((IEdmDirectValueAnnotation)annotation); + } + public void VisitVocabularyAnnotation(IEdmVocabularyAnnotation annotation) { if (annotation.Term != null) @@ -93,11 +141,28 @@ public void VisitVocabularyAnnotation(IEdmVocabularyAnnotation annotation) } } + public Task VisitVocabularyAnnotationAsync(IEdmVocabularyAnnotation annotation) + { + if (annotation.Term != null) + { + return this.ProcessAnnotationAsync(annotation); + } + else + { + return this.ProcessVocabularyAnnotationAsync(annotation); + } + } + public void VisitPropertyValueBindings(IEnumerable bindings) { VisitCollection(bindings, this.ProcessPropertyValueBinding); } + public Task VisitPropertyValueBindingsAsync(IEnumerable bindings) + { + return VisitCollectionAsync(bindings, this.ProcessPropertyValueBindingAsync); + } + #endregion #region Expressions @@ -107,6 +172,11 @@ public void VisitExpressions(IEnumerable expressions) VisitCollection(expressions, this.VisitExpression); } + public Task VisitExpressionsAsync(IEnumerable expressions) + { + return VisitCollectionAsync(expressions, this.VisitExpressionAsync); + } + public void VisitExpression(IEdmExpression expression) { switch (expression.ExpressionKind) @@ -194,11 +264,78 @@ public void VisitExpression(IEdmExpression expression) } } + public Task VisitExpressionAsync(IEdmExpression expression) + { + switch (expression.ExpressionKind) + { + case EdmExpressionKind.Cast: + return this.ProcessCastExpressionAsync((IEdmCastExpression)expression); + case EdmExpressionKind.BinaryConstant: + return this.ProcessBinaryConstantExpressionAsync((IEdmBinaryConstantExpression)expression); + case EdmExpressionKind.BooleanConstant: + return this.ProcessBooleanConstantExpressionAsync((IEdmBooleanConstantExpression)expression); + case EdmExpressionKind.Collection: + return this.ProcessCollectionExpressionAsync((IEdmCollectionExpression)expression); + case EdmExpressionKind.DateConstant: + return this.ProcessDateConstantExpressionAsync((IEdmDateConstantExpression)expression); + case EdmExpressionKind.DateTimeOffsetConstant: + return this.ProcessDateTimeOffsetConstantExpressionAsync((IEdmDateTimeOffsetConstantExpression)expression); + case EdmExpressionKind.DecimalConstant: + return this.ProcessDecimalConstantExpressionAsync((IEdmDecimalConstantExpression)expression); + case EdmExpressionKind.EnumMember: + return this.ProcessEnumMemberExpressionAsync((IEdmEnumMemberExpression)expression); + case EdmExpressionKind.FloatingConstant: + return this.ProcessFloatingConstantExpressionAsync((IEdmFloatingConstantExpression)expression); + case EdmExpressionKind.FunctionApplication: + return this.ProcessFunctionApplicationExpressionAsync((IEdmApplyExpression)expression); + case EdmExpressionKind.GuidConstant: + return this.ProcessGuidConstantExpressionAsync((IEdmGuidConstantExpression)expression); + case EdmExpressionKind.If: + return this.ProcessIfExpressionAsync((IEdmIfExpression)expression); + case EdmExpressionKind.IntegerConstant: + return this.ProcessIntegerConstantExpressionAsync((IEdmIntegerConstantExpression)expression); + case EdmExpressionKind.IsType: + return this.ProcessIsTypeExpressionAsync((IEdmIsTypeExpression)expression); + case EdmExpressionKind.LabeledExpressionReference: + return this.ProcessLabeledExpressionReferenceExpressionAsync((IEdmLabeledExpressionReferenceExpression)expression); + case EdmExpressionKind.Labeled: + return this.ProcessLabeledExpressionAsync((IEdmLabeledExpression)expression); + case EdmExpressionKind.Null: + return this.ProcessNullConstantExpressionAsync((IEdmNullExpression)expression); + case EdmExpressionKind.Path: + return this.ProcessPathExpressionAsync((IEdmPathExpression)expression); + case EdmExpressionKind.PropertyPath: + return this.ProcessPropertyPathExpressionAsync((IEdmPathExpression)expression); + case EdmExpressionKind.NavigationPropertyPath: + return this.ProcessNavigationPropertyPathExpressionAsync((IEdmPathExpression)expression); + case EdmExpressionKind.AnnotationPath: + return this.ProcessAnnotationPathExpressionAsync((IEdmPathExpression)expression); + case EdmExpressionKind.Record: + return this.ProcessRecordExpressionAsync((IEdmRecordExpression)expression); + case EdmExpressionKind.StringConstant: + return this.ProcessStringConstantExpressionAsync((IEdmStringConstantExpression)expression); + case EdmExpressionKind.TimeOfDayConstant: + return this.ProcessTimeOfDayConstantExpressionAsync((IEdmTimeOfDayConstantExpression)expression); + case EdmExpressionKind.DurationConstant: + return this.ProcessDurationConstantExpressionAsync((IEdmDurationConstantExpression)expression); + case EdmExpressionKind.None: + return this.ProcessExpressionAsync(expression); + default: + Debug.Assert(false, Edm.Strings.UnknownEnumVal_ExpressionKind(expression.ExpressionKind)); + return TaskUtils.GetFaultedTask(new InvalidOperationException(Edm.Strings.UnknownEnumVal_ExpressionKind(expression.ExpressionKind))); + } + } + public void VisitPropertyConstructors(IEnumerable constructor) { VisitCollection(constructor, this.ProcessPropertyConstructor); } + public Task VisitPropertyConstructorsAsync(IEnumerable constructor) + { + return VisitCollectionAsync(constructor, this.ProcessPropertyConstructorAsync); + } + #endregion #region Data Model @@ -230,31 +367,29 @@ public virtual void VisitEntityContainerElements(IEnumerable elements) + public virtual Task VisitEntityContainerElementsAsync(IEnumerable elements) { foreach (IEdmEntityContainerElement element in elements) { switch (element.ContainerElementKind) { case EdmContainerElementKind.EntitySet: - await this.ProcessEntitySetAsync((IEdmEntitySet)element).ConfigureAwait(false); - break; + return this.ProcessEntitySetAsync((IEdmEntitySet)element); case EdmContainerElementKind.Singleton: - await this.ProcessSingletonAsync((IEdmSingleton)element).ConfigureAwait(false); - break; + return this.ProcessSingletonAsync((IEdmSingleton)element); case EdmContainerElementKind.ActionImport: - await this.ProcessActionImportAsync((IEdmActionImport)element).ConfigureAwait(false); - break; + return this.ProcessActionImportAsync((IEdmActionImport)element); case EdmContainerElementKind.FunctionImport: - await this.ProcessFunctionImportAsync((IEdmFunctionImport)element).ConfigureAwait(false); - break; + return this.ProcessFunctionImportAsync((IEdmFunctionImport)element); case EdmContainerElementKind.None: - await this.ProcessEntityContainerElementAsync(element).ConfigureAwait(false); - break; + return this.ProcessEntityContainerElementAsync(element); default: - throw new InvalidOperationException(Edm.Strings.UnknownEnumVal_ContainerElementKind(element.ContainerElementKind.ToString())); + Debug.Assert(false, Edm.Strings.UnknownEnumVal_ContainerElementKind(element.ContainerElementKind.ToString())); + return TaskUtils.GetFaultedTask(new InvalidOperationException(Edm.Strings.UnknownEnumVal_ContainerElementKind(element.ContainerElementKind.ToString()))); } } + + return TaskUtils.CompletedTask; } #endregion @@ -300,6 +435,36 @@ public void VisitTypeReference(IEdmTypeReference reference) } } + public Task VisitTypeReferenceAsync(IEdmTypeReference reference) + { + switch (reference.TypeKind()) + { + case EdmTypeKind.Collection: + return this.ProcessCollectionTypeReferenceAsync(reference.AsCollection()); + case EdmTypeKind.Complex: + return this.ProcessComplexTypeReferenceAsync(reference.AsComplex()); + case EdmTypeKind.Entity: + return this.ProcessEntityTypeReferenceAsync(reference.AsEntity()); + case EdmTypeKind.EntityReference: + return this.ProcessEntityReferenceTypeReferenceAsync(reference.AsEntityReference()); + case EdmTypeKind.Enum: + return this.ProcessEnumTypeReferenceAsync(reference.AsEnum()); + case EdmTypeKind.Primitive: + return this.VisitPrimitiveTypeReferenceAsync(reference.AsPrimitive()); + case EdmTypeKind.TypeDefinition: + return this.ProcessTypeDefinitionReferenceAsync(reference.AsTypeDefinition()); + case EdmTypeKind.None: + return this.ProcessTypeReferenceAsync(reference); + case EdmTypeKind.Path: + return this.ProcessPathTypeReferenceAsync(reference.AsPath()); + case EdmTypeKind.Untyped: + return this.ProcessUntypedTypeReferenceAsync(reference as IEdmUntypedTypeReference); + default: + Debug.Assert(false, Edm.Strings.UnknownEnumVal_TypeKind(reference.TypeKind().ToString())); + return TaskUtils.GetFaultedTask(new InvalidOperationException(Edm.Strings.UnknownEnumVal_TypeKind(reference.TypeKind().ToString()))); + } + } + public void VisitPrimitiveTypeReference(IEdmPrimitiveTypeReference reference) { switch (reference.PrimitiveKind()) @@ -356,6 +521,57 @@ public void VisitPrimitiveTypeReference(IEdmPrimitiveTypeReference reference) } } + public Task VisitPrimitiveTypeReferenceAsync(IEdmPrimitiveTypeReference reference) + { + switch (reference.PrimitiveKind()) + { + case EdmPrimitiveTypeKind.Binary: + return this.ProcessBinaryTypeReferenceAsync(reference.AsBinary()); + case EdmPrimitiveTypeKind.Decimal: + return this.ProcessDecimalTypeReferenceAsync(reference.AsDecimal()); + case EdmPrimitiveTypeKind.String: + return this.ProcessStringTypeReferenceAsync(reference.AsString()); + case EdmPrimitiveTypeKind.DateTimeOffset: + case EdmPrimitiveTypeKind.Duration: + case EdmPrimitiveTypeKind.TimeOfDay: + return this.ProcessTemporalTypeReferenceAsync(reference.AsTemporal()); + case EdmPrimitiveTypeKind.Geography: + case EdmPrimitiveTypeKind.GeographyPoint: + case EdmPrimitiveTypeKind.GeographyLineString: + case EdmPrimitiveTypeKind.GeographyPolygon: + case EdmPrimitiveTypeKind.GeographyCollection: + case EdmPrimitiveTypeKind.GeographyMultiPolygon: + case EdmPrimitiveTypeKind.GeographyMultiLineString: + case EdmPrimitiveTypeKind.GeographyMultiPoint: + case EdmPrimitiveTypeKind.Geometry: + case EdmPrimitiveTypeKind.GeometryPoint: + case EdmPrimitiveTypeKind.GeometryLineString: + case EdmPrimitiveTypeKind.GeometryPolygon: + case EdmPrimitiveTypeKind.GeometryCollection: + case EdmPrimitiveTypeKind.GeometryMultiPolygon: + case EdmPrimitiveTypeKind.GeometryMultiLineString: + case EdmPrimitiveTypeKind.GeometryMultiPoint: + return this.ProcessSpatialTypeReferenceAsync(reference.AsSpatial()); + case EdmPrimitiveTypeKind.Boolean: + case EdmPrimitiveTypeKind.Byte: + case EdmPrimitiveTypeKind.Double: + case EdmPrimitiveTypeKind.Guid: + case EdmPrimitiveTypeKind.Int16: + case EdmPrimitiveTypeKind.Int32: + case EdmPrimitiveTypeKind.Int64: + case EdmPrimitiveTypeKind.SByte: + case EdmPrimitiveTypeKind.Single: + case EdmPrimitiveTypeKind.Stream: + case EdmPrimitiveTypeKind.Date: + case EdmPrimitiveTypeKind.PrimitiveType: + case EdmPrimitiveTypeKind.None: + return this.ProcessPrimitiveTypeReferenceAsync(reference); + default: + Debug.Assert(false, Edm.Strings.UnknownEnumVal_PrimitiveKind(reference.PrimitiveKind().ToString())); + return TaskUtils.GetFaultedTask(new InvalidOperationException(Edm.Strings.UnknownEnumVal_PrimitiveKind(reference.PrimitiveKind().ToString()))); + } + } + #endregion #region Type Definitions @@ -384,11 +600,36 @@ public void VisitSchemaType(IEdmType definition) } } + public Task VisitSchemaTypeAsync(IEdmType definition) + { + switch (definition.TypeKind) + { + case EdmTypeKind.Complex: + return this.ProcessComplexTypeAsync((IEdmComplexType)definition); + case EdmTypeKind.Entity: + return this.ProcessEntityTypeAsync((IEdmEntityType)definition); + case EdmTypeKind.Enum: + return this.ProcessEnumTypeAsync((IEdmEnumType)definition); + case EdmTypeKind.TypeDefinition: + return this.ProcessTypeDefinitionAsync((IEdmTypeDefinition)definition); + case EdmTypeKind.None: + return this.VisitSchemaTypeAsync(definition); + default: + Debug.Assert(false, Edm.Strings.UnknownEnumVal_TypeKind(definition.TypeKind)); + return TaskUtils.GetFaultedTask(new InvalidOperationException(Edm.Strings.UnknownEnumVal_TypeKind(definition.TypeKind))); + } + } + public void VisitProperties(IEnumerable properties) { VisitCollection(properties, this.VisitProperty); } + public Task VisitPropertiesAsync(IEnumerable properties) + { + return VisitCollectionAsync(properties, this.VisitPropertyAsync); + } + public void VisitProperty(IEdmProperty property) { switch (property.PropertyKind) @@ -407,16 +648,42 @@ public void VisitProperty(IEdmProperty property) } } + public Task VisitPropertyAsync(IEdmProperty property) + { + switch (property.PropertyKind) + { + case EdmPropertyKind.Navigation: + return this.ProcessNavigationPropertyAsync((IEdmNavigationProperty)property); + case EdmPropertyKind.Structural: + return this.ProcessStructuralPropertyAsync((IEdmStructuralProperty)property); + case EdmPropertyKind.None: + return this.ProcessPropertyAsync(property); + default: + Debug.Assert(false, Edm.Strings.UnknownEnumVal_PropertyKind(property.PropertyKind.ToString())); + return TaskUtils.GetFaultedTask(new InvalidOperationException(Edm.Strings.UnknownEnumVal_PropertyKind(property.PropertyKind.ToString()))); + } + } + public void VisitEnumMembers(IEnumerable enumMembers) { VisitCollection(enumMembers, this.VisitEnumMember); } + public Task VisitEnumMembersAsync(IEnumerable enumMembers) + { + return VisitCollectionAsync(enumMembers, this.VisitEnumMemberAsync); + } + public void VisitEnumMember(IEdmEnumMember enumMember) { this.ProcessEnumMember(enumMember); } + public Task VisitEnumMemberAsync(IEdmEnumMember enumMember) + { + return this.ProcessEnumMemberAsync(enumMember); + } + #endregion #region Operation Related @@ -426,6 +693,11 @@ public void VisitOperationParameters(IEnumerable paramet VisitCollection(parameters, this.ProcessOperationParameter); } + public Task VisitOperationParametersAsync(IEnumerable parameters) + { + return VisitCollectionAsync(parameters, this.ProcessOperationParameterAsync); + } + #endregion protected static void VisitCollection(IEnumerable collection, Action visitMethod) @@ -435,6 +707,14 @@ protected static void VisitCollection(IEnumerable collection, Action vi visitMethod(element); } } + + protected static async Task VisitCollectionAsync(IEnumerable collection, Func visitMethod) + { + foreach (T element in collection) + { + await visitMethod(element).ConfigureAwait(false); + } + } #endregion #region Process Methods @@ -447,6 +727,14 @@ protected virtual void ProcessModel(IEdmModel model) this.VisitVocabularyAnnotations(model.VocabularyAnnotations); } + protected virtual async Task ProcessModelAsync(IEdmModel model) + { + await this.ProcessElementAsync(model).ConfigureAwait(false); + + await this.VisitSchemaElementsAsync(model.SchemaElements).ConfigureAwait(false); + await this.VisitVocabularyAnnotationsAsync(model.VocabularyAnnotations).ConfigureAwait(false); + } + #region Base Element Types protected virtual void ProcessElement(IEdmElement element) @@ -457,10 +745,7 @@ protected virtual void ProcessElement(IEdmElement element) protected virtual Task ProcessElementAsync(IEdmElement element) { - // TODO: DirectValueAnnotationsInMainSchema (not including those in referenced schemas) - this.VisitAnnotations(this.Model.DirectValueAnnotations(element)); - - return TaskUtils.CompletedTask; + return this.VisitAnnotationsAsync(this.Model.DirectValueAnnotations(element)); } protected virtual void ProcessNamedElement(IEdmNamedElement element) @@ -468,9 +753,9 @@ protected virtual void ProcessNamedElement(IEdmNamedElement element) this.ProcessElement(element); } - protected virtual async Task ProcessNamedElementAsync(IEdmNamedElement element) + protected virtual Task ProcessNamedElementAsync(IEdmNamedElement element) { - await this.ProcessElementAsync(element).ConfigureAwait(false); + return this.ProcessElementAsync(element); } protected virtual void ProcessSchemaElement(IEdmSchemaElement element) @@ -503,9 +788,9 @@ protected virtual void ProcessComplexTypeReference(IEdmComplexTypeReference refe this.ProcessStructuredTypeReference(reference); } - protected virtual async Task ProcessComplexTypeReferenceAsync(IEdmComplexTypeReference reference) + protected virtual Task ProcessComplexTypeReferenceAsync(IEdmComplexTypeReference reference) { - await this.ProcessStructuredTypeReferenceAsync(reference).ConfigureAwait(false); + return this.ProcessStructuredTypeReferenceAsync(reference); } protected virtual void ProcessEntityTypeReference(IEdmEntityTypeReference reference) @@ -513,9 +798,9 @@ protected virtual void ProcessEntityTypeReference(IEdmEntityTypeReference refere this.ProcessStructuredTypeReference(reference); } - protected virtual async Task ProcessEntityTypeReferenceAsync(IEdmEntityTypeReference reference) + protected virtual Task ProcessEntityTypeReferenceAsync(IEdmEntityTypeReference reference) { - await this.ProcessStructuredTypeReferenceAsync(reference).ConfigureAwait(false); + return this.ProcessStructuredTypeReferenceAsync(reference); } protected virtual void ProcessEntityReferenceTypeReference(IEdmEntityReferenceTypeReference reference) @@ -547,9 +832,9 @@ protected virtual void ProcessEnumTypeReference(IEdmEnumTypeReference reference) this.ProcessTypeReference(reference); } - protected virtual async Task ProcessEnumTypeReferenceAsync(IEdmEnumTypeReference reference) + protected virtual Task ProcessEnumTypeReferenceAsync(IEdmEnumTypeReference reference) { - await this.ProcessTypeReferenceAsync(reference).ConfigureAwait(false); + return this.ProcessTypeReferenceAsync(reference); } protected virtual void ProcessTypeDefinitionReference(IEdmTypeDefinitionReference reference) @@ -557,9 +842,9 @@ protected virtual void ProcessTypeDefinitionReference(IEdmTypeDefinitionReferenc this.ProcessTypeReference(reference); } - protected virtual async Task ProcessTypeDefinitionReferenceAsync(IEdmTypeDefinitionReference reference) + protected virtual Task ProcessTypeDefinitionReferenceAsync(IEdmTypeDefinitionReference reference) { - await this.ProcessTypeReferenceAsync(reference).ConfigureAwait(false); + return this.ProcessTypeReferenceAsync(reference); } protected virtual void ProcessBinaryTypeReference(IEdmBinaryTypeReference reference) @@ -567,9 +852,9 @@ protected virtual void ProcessBinaryTypeReference(IEdmBinaryTypeReference refere this.ProcessPrimitiveTypeReference(reference); } - protected virtual async Task ProcessBinaryTypeReferenceAsync(IEdmBinaryTypeReference reference) + protected virtual Task ProcessBinaryTypeReferenceAsync(IEdmBinaryTypeReference reference) { - await this.ProcessPrimitiveTypeReferenceAsync(reference).ConfigureAwait(false); + return this.ProcessPrimitiveTypeReferenceAsync(reference); } protected virtual void ProcessDecimalTypeReference(IEdmDecimalTypeReference reference) @@ -577,9 +862,9 @@ protected virtual void ProcessDecimalTypeReference(IEdmDecimalTypeReference refe this.ProcessPrimitiveTypeReference(reference); } - protected virtual async Task ProcessDecimalTypeReferenceAsync(IEdmDecimalTypeReference reference) + protected virtual Task ProcessDecimalTypeReferenceAsync(IEdmDecimalTypeReference reference) { - await this.ProcessPrimitiveTypeReferenceAsync(reference).ConfigureAwait(false); + return this.ProcessPrimitiveTypeReferenceAsync(reference); } protected virtual void ProcessSpatialTypeReference(IEdmSpatialTypeReference reference) @@ -587,9 +872,9 @@ protected virtual void ProcessSpatialTypeReference(IEdmSpatialTypeReference refe this.ProcessPrimitiveTypeReference(reference); } - protected virtual async Task ProcessSpatialTypeReferenceAsync(IEdmSpatialTypeReference reference) + protected virtual Task ProcessSpatialTypeReferenceAsync(IEdmSpatialTypeReference reference) { - await this.ProcessPrimitiveTypeReferenceAsync(reference).ConfigureAwait(false); + return this.ProcessPrimitiveTypeReferenceAsync(reference); } protected virtual void ProcessStringTypeReference(IEdmStringTypeReference reference) @@ -597,9 +882,9 @@ protected virtual void ProcessStringTypeReference(IEdmStringTypeReference refere this.ProcessPrimitiveTypeReference(reference); } - protected virtual async Task ProcessStringTypeReferenceAsync(IEdmStringTypeReference reference) + protected virtual Task ProcessStringTypeReferenceAsync(IEdmStringTypeReference reference) { - await this.ProcessPrimitiveTypeReferenceAsync(reference).ConfigureAwait(false); + return this.ProcessPrimitiveTypeReferenceAsync(reference); } protected virtual void ProcessTemporalTypeReference(IEdmTemporalTypeReference reference) @@ -607,9 +892,9 @@ protected virtual void ProcessTemporalTypeReference(IEdmTemporalTypeReference re this.ProcessPrimitiveTypeReference(reference); } - protected virtual async Task ProcessTemporalTypeReferenceAsync(IEdmTemporalTypeReference reference) + protected virtual Task ProcessTemporalTypeReferenceAsync(IEdmTemporalTypeReference reference) { - await this.ProcessPrimitiveTypeReferenceAsync(reference).ConfigureAwait(false); + return this.ProcessPrimitiveTypeReferenceAsync(reference); } protected virtual void ProcessPrimitiveTypeReference(IEdmPrimitiveTypeReference reference) @@ -617,9 +902,9 @@ protected virtual void ProcessPrimitiveTypeReference(IEdmPrimitiveTypeReference this.ProcessTypeReference(reference); } - protected virtual async Task ProcessPrimitiveTypeReferenceAsync(IEdmPrimitiveTypeReference reference) + protected virtual Task ProcessPrimitiveTypeReferenceAsync(IEdmPrimitiveTypeReference reference) { - await this.ProcessTypeReferenceAsync(reference).ConfigureAwait(false); + return this.ProcessTypeReferenceAsync(reference); } protected virtual void ProcessStructuredTypeReference(IEdmStructuredTypeReference reference) @@ -627,9 +912,9 @@ protected virtual void ProcessStructuredTypeReference(IEdmStructuredTypeReferenc this.ProcessTypeReference(reference); } - protected virtual async Task ProcessStructuredTypeReferenceAsync(IEdmStructuredTypeReference reference) + protected virtual Task ProcessStructuredTypeReferenceAsync(IEdmStructuredTypeReference reference) { - await this.ProcessTypeReferenceAsync(reference).ConfigureAwait(false); + return this.ProcessTypeReferenceAsync(reference); } protected virtual void ProcessTypeReference(IEdmTypeReference element) @@ -637,9 +922,9 @@ protected virtual void ProcessTypeReference(IEdmTypeReference element) this.ProcessElement(element); } - protected virtual async Task ProcessTypeReferenceAsync(IEdmTypeReference element) + protected virtual Task ProcessTypeReferenceAsync(IEdmTypeReference element) { - await this.ProcessElementAsync(element).ConfigureAwait(false); + return this.ProcessElementAsync(element); } protected virtual void ProcessPathTypeReference(IEdmPathTypeReference reference) @@ -647,9 +932,9 @@ protected virtual void ProcessPathTypeReference(IEdmPathTypeReference reference) this.ProcessTypeReference(reference); } - protected virtual async Task ProcessPathTypeReferenceAsync(IEdmPathTypeReference reference) + protected virtual Task ProcessPathTypeReferenceAsync(IEdmPathTypeReference reference) { - await this.ProcessTypeReferenceAsync(reference).ConfigureAwait(false); + return this.ProcessTypeReferenceAsync(reference); } protected virtual void ProcessUntypedTypeReference(IEdmUntypedTypeReference reference) @@ -657,9 +942,9 @@ protected virtual void ProcessUntypedTypeReference(IEdmUntypedTypeReference refe this.ProcessTypeReference(reference); } - protected virtual async Task ProcessUntypedTypeReferenceAsync(IEdmUntypedTypeReference reference) + protected virtual Task ProcessUntypedTypeReferenceAsync(IEdmUntypedTypeReference reference) { - await this.ProcessTypeReferenceAsync(reference).ConfigureAwait(false); + return this.ProcessTypeReferenceAsync(reference); } #endregion @@ -675,7 +960,7 @@ protected virtual void ProcessTerm(IEdmTerm term) protected virtual async Task ProcessTermAsync(IEdmTerm term) { await this.ProcessSchemaElementAsync(term).ConfigureAwait(false); - this.VisitTypeReference(term.Type); + await this.VisitTypeReferenceAsync(term.Type).ConfigureAwait(false); } #endregion @@ -721,7 +1006,7 @@ protected virtual async Task ProcessCollectionTypeAsync(IEdmCollectionType defin { await this.ProcessElementAsync(definition).ConfigureAwait(false); await this.ProcessTypeAsync(definition).ConfigureAwait(false); - this.VisitTypeReference(definition.ElementType); + await this.VisitTypeReferenceAsync(definition.ElementType).ConfigureAwait(false); } protected virtual void ProcessEnumType(IEdmEnumType definition) @@ -737,7 +1022,7 @@ protected virtual async Task ProcessEnumTypeAsync(IEdmEnumType definition) await this.ProcessSchemaElementAsync(definition).ConfigureAwait(false); await this.ProcessTypeAsync(definition).ConfigureAwait(false); await this.ProcessSchemaTypeAsync(definition).ConfigureAwait(false); - this.VisitEnumMembers(definition.Members); + await this.VisitEnumMembersAsync(definition.Members).ConfigureAwait(false); } protected virtual void ProcessTypeDefinition(IEdmTypeDefinition definition) @@ -775,7 +1060,7 @@ protected virtual void ProcessStructuredType(IEdmStructuredType definition) protected virtual async Task ProcessStructuredTypeAsync(IEdmStructuredType definition) { await this.ProcessTypeAsync(definition).ConfigureAwait(false); - this.VisitProperties(definition.DeclaredProperties); + await this.VisitPropertiesAsync(definition.DeclaredProperties).ConfigureAwait(false); } protected virtual void ProcessSchemaType(IEdmSchemaType type) @@ -807,9 +1092,9 @@ protected virtual void ProcessNavigationProperty(IEdmNavigationProperty property this.ProcessProperty(property); } - protected virtual async Task ProcessNavigationPropertyAsync(IEdmNavigationProperty property) + protected virtual Task ProcessNavigationPropertyAsync(IEdmNavigationProperty property) { - await this.ProcessPropertyAsync(property).ConfigureAwait(false); + return this.ProcessPropertyAsync(property); } protected virtual void ProcessStructuralProperty(IEdmStructuralProperty property) @@ -817,9 +1102,9 @@ protected virtual void ProcessStructuralProperty(IEdmStructuralProperty property this.ProcessProperty(property); } - protected virtual async Task ProcessStructuralPropertyAsync(IEdmStructuralProperty property) + protected virtual Task ProcessStructuralPropertyAsync(IEdmStructuralProperty property) { - await this.ProcessPropertyAsync(property).ConfigureAwait(false); + return this.ProcessPropertyAsync(property); } protected virtual void ProcessProperty(IEdmProperty property) @@ -833,7 +1118,7 @@ protected virtual async Task ProcessPropertyAsync(IEdmProperty property) { await this.ProcessVocabularyAnnotatableAsync(property).ConfigureAwait(false); await this.ProcessNamedElementAsync(property).ConfigureAwait(false); - this.VisitTypeReference(property.Type); + await this.VisitTypeReferenceAsync(property.Type).ConfigureAwait(false); } protected virtual void ProcessEnumMember(IEdmEnumMember enumMember) @@ -841,9 +1126,9 @@ protected virtual void ProcessEnumMember(IEdmEnumMember enumMember) this.ProcessNamedElement(enumMember); } - protected virtual async Task ProcessEnumMemberAsync(IEdmEnumMember enumMember) + protected virtual Task ProcessEnumMemberAsync(IEdmEnumMember enumMember) { - await this.ProcessNamedElementAsync(enumMember).ConfigureAwait(false); + return this.ProcessNamedElementAsync(enumMember); } #endregion @@ -855,9 +1140,9 @@ protected virtual void ProcessVocabularyAnnotation(IEdmVocabularyAnnotation anno this.ProcessElement(annotation); } - protected virtual async Task ProcessVocabularyAnnotationAsync(IEdmVocabularyAnnotation annotation) + protected virtual Task ProcessVocabularyAnnotationAsync(IEdmVocabularyAnnotation annotation) { - await this.ProcessElementAsync(annotation).ConfigureAwait(false); + return this.ProcessElementAsync(annotation); } protected virtual void ProcessImmediateValueAnnotation(IEdmDirectValueAnnotation annotation) @@ -865,9 +1150,9 @@ protected virtual void ProcessImmediateValueAnnotation(IEdmDirectValueAnnotation this.ProcessNamedElement(annotation); } - protected virtual async Task ProcessImmediateValueAnnotationAsync(IEdmDirectValueAnnotation annotation) + protected virtual Task ProcessImmediateValueAnnotationAsync(IEdmDirectValueAnnotation annotation) { - await this.ProcessNamedElementAsync(annotation).ConfigureAwait(false); + return this.ProcessNamedElementAsync(annotation); } protected virtual void ProcessAnnotation(IEdmVocabularyAnnotation annotation) @@ -879,7 +1164,7 @@ protected virtual void ProcessAnnotation(IEdmVocabularyAnnotation annotation) protected virtual async Task ProcessAnnotationAsync(IEdmVocabularyAnnotation annotation) { await this.ProcessVocabularyAnnotationAsync(annotation).ConfigureAwait(false); - this.VisitExpression(annotation.Value); + await this.VisitExpressionAsync(annotation.Value).ConfigureAwait(false); } protected virtual void ProcessPropertyValueBinding(IEdmPropertyValueBinding binding) @@ -887,6 +1172,11 @@ protected virtual void ProcessPropertyValueBinding(IEdmPropertyValueBinding bind this.VisitExpression(binding.Value); } + protected virtual Task ProcessPropertyValueBindingAsync(IEdmPropertyValueBinding binding) + { + return this.VisitExpressionAsync(binding.Value); + } + #endregion #region Expressions @@ -905,9 +1195,9 @@ protected virtual void ProcessStringConstantExpression(IEdmStringConstantExpress this.ProcessExpression(expression); } - protected virtual async Task ProcessStringConstantExpressionAsync(IEdmStringConstantExpression expression) + protected virtual Task ProcessStringConstantExpressionAsync(IEdmStringConstantExpression expression) { - await this.ProcessExpressionAsync(expression).ConfigureAwait(false); + return this.ProcessExpressionAsync(expression); } protected virtual void ProcessBinaryConstantExpression(IEdmBinaryConstantExpression expression) @@ -915,9 +1205,9 @@ protected virtual void ProcessBinaryConstantExpression(IEdmBinaryConstantExpress this.ProcessExpression(expression); } - protected virtual async Task ProcessBinaryConstantExpressionAsync(IEdmBinaryConstantExpression expression) + protected virtual Task ProcessBinaryConstantExpressionAsync(IEdmBinaryConstantExpression expression) { - await this.ProcessExpressionAsync(expression).ConfigureAwait(false); + return this.ProcessExpressionAsync(expression); } protected virtual void ProcessRecordExpression(IEdmRecordExpression expression) @@ -936,10 +1226,10 @@ protected virtual async Task ProcessRecordExpressionAsync(IEdmRecordExpression e await this.ProcessExpressionAsync(expression).ConfigureAwait(false); if (expression.DeclaredType != null) { - this.VisitTypeReference(expression.DeclaredType); + await this.VisitTypeReferenceAsync(expression.DeclaredType).ConfigureAwait(false); } - this.VisitPropertyConstructors(expression.Properties); + await this.VisitPropertyConstructorsAsync(expression.Properties).ConfigureAwait(false); } protected virtual void ProcessPathExpression(IEdmPathExpression expression) @@ -947,9 +1237,9 @@ protected virtual void ProcessPathExpression(IEdmPathExpression expression) this.ProcessExpression(expression); } - protected virtual async Task ProcessPathExpressionAsync(IEdmPathExpression expression) + protected virtual Task ProcessPathExpressionAsync(IEdmPathExpression expression) { - await this.ProcessExpressionAsync(expression).ConfigureAwait(false); + return this.ProcessExpressionAsync(expression); } protected virtual void ProcessPropertyPathExpression(IEdmPathExpression expression) @@ -957,9 +1247,9 @@ protected virtual void ProcessPropertyPathExpression(IEdmPathExpression expressi this.ProcessExpression(expression); } - protected virtual async Task ProcessPropertyPathExpressionAsync(IEdmPathExpression expression) + protected virtual Task ProcessPropertyPathExpressionAsync(IEdmPathExpression expression) { - await this.ProcessExpressionAsync(expression).ConfigureAwait(false); + return this.ProcessExpressionAsync(expression); } protected virtual void ProcessNavigationPropertyPathExpression(IEdmPathExpression expression) @@ -967,9 +1257,9 @@ protected virtual void ProcessNavigationPropertyPathExpression(IEdmPathExpressio this.ProcessExpression(expression); } - protected virtual async Task ProcessNavigationPropertyPathExpressionAsync(IEdmPathExpression expression) + protected virtual Task ProcessNavigationPropertyPathExpressionAsync(IEdmPathExpression expression) { - await this.ProcessExpressionAsync(expression).ConfigureAwait(false); + return this.ProcessExpressionAsync(expression); } protected virtual void ProcessAnnotationPathExpression(IEdmPathExpression expression) @@ -977,9 +1267,9 @@ protected virtual void ProcessAnnotationPathExpression(IEdmPathExpression expres this.ProcessExpression(expression); } - protected virtual async Task ProcessAnnotationPathExpressionAsync(IEdmPathExpression expression) + protected virtual Task ProcessAnnotationPathExpressionAsync(IEdmPathExpression expression) { - await this.ProcessExpressionAsync(expression).ConfigureAwait(false); + return this.ProcessExpressionAsync(expression); } protected virtual void ProcessCollectionExpression(IEdmCollectionExpression expression) @@ -991,7 +1281,7 @@ protected virtual void ProcessCollectionExpression(IEdmCollectionExpression expr protected virtual async Task ProcessCollectionExpressionAsync(IEdmCollectionExpression expression) { await this.ProcessExpressionAsync(expression).ConfigureAwait(false); - this.VisitExpressions(expression.Elements); + await this.VisitExpressionsAsync(expression.Elements).ConfigureAwait(false); } protected virtual void ProcessLabeledExpressionReferenceExpression(IEdmLabeledExpressionReferenceExpression expression) @@ -999,9 +1289,9 @@ protected virtual void ProcessLabeledExpressionReferenceExpression(IEdmLabeledEx this.ProcessExpression(expression); } - protected virtual async Task ProcessLabeledExpressionReferenceExpressionAsync(IEdmLabeledExpressionReferenceExpression expression) + protected virtual Task ProcessLabeledExpressionReferenceExpressionAsync(IEdmLabeledExpressionReferenceExpression expression) { - await this.ProcessExpressionAsync(expression).ConfigureAwait(false); + return this.ProcessExpressionAsync(expression); } protected virtual void ProcessIsTypeExpression(IEdmIsTypeExpression expression) @@ -1014,8 +1304,8 @@ protected virtual void ProcessIsTypeExpression(IEdmIsTypeExpression expression) protected virtual async Task ProcessIsTypeExpressionAsync(IEdmIsTypeExpression expression) { await this.ProcessExpressionAsync(expression).ConfigureAwait(false); - this.VisitTypeReference(expression.Type); - this.VisitExpression(expression.Operand); + await this.VisitTypeReferenceAsync(expression.Type).ConfigureAwait(false); + await this.VisitExpressionAsync(expression.Operand).ConfigureAwait(false); } protected virtual void ProcessIntegerConstantExpression(IEdmIntegerConstantExpression expression) @@ -1023,9 +1313,9 @@ protected virtual void ProcessIntegerConstantExpression(IEdmIntegerConstantExpre this.ProcessExpression(expression); } - protected virtual async Task ProcessIntegerConstantExpressionAsync(IEdmIntegerConstantExpression expression) + protected virtual Task ProcessIntegerConstantExpressionAsync(IEdmIntegerConstantExpression expression) { - await this.ProcessExpressionAsync(expression).ConfigureAwait(false); + return this.ProcessExpressionAsync(expression); } protected virtual void ProcessIfExpression(IEdmIfExpression expression) @@ -1039,9 +1329,9 @@ protected virtual void ProcessIfExpression(IEdmIfExpression expression) protected virtual async Task ProcessIfExpressionAsync(IEdmIfExpression expression) { await this.ProcessExpressionAsync(expression).ConfigureAwait(false); - this.VisitExpression(expression.TestExpression); - this.VisitExpression(expression.TrueExpression); - this.VisitExpression(expression.FalseExpression); + await this.VisitExpressionAsync(expression.TestExpression).ConfigureAwait(false); + await this.VisitExpressionAsync(expression.TrueExpression).ConfigureAwait(false); + await this.VisitExpressionAsync(expression.FalseExpression).ConfigureAwait(false); } protected virtual void ProcessFunctionApplicationExpression(IEdmApplyExpression expression) @@ -1053,7 +1343,7 @@ protected virtual void ProcessFunctionApplicationExpression(IEdmApplyExpression protected virtual async Task ProcessFunctionApplicationExpressionAsync(IEdmApplyExpression expression) { await this.ProcessExpressionAsync(expression).ConfigureAwait(false); - this.VisitExpressions(expression.Arguments); + await this.VisitExpressionsAsync(expression.Arguments).ConfigureAwait(false); } protected virtual void ProcessFloatingConstantExpression(IEdmFloatingConstantExpression expression) @@ -1061,9 +1351,9 @@ protected virtual void ProcessFloatingConstantExpression(IEdmFloatingConstantExp this.ProcessExpression(expression); } - protected virtual async Task ProcessFloatingConstantExpressionAsync(IEdmFloatingConstantExpression expression) + protected virtual Task ProcessFloatingConstantExpressionAsync(IEdmFloatingConstantExpression expression) { - await this.ProcessExpressionAsync(expression).ConfigureAwait(false); + return this.ProcessExpressionAsync(expression); } protected virtual void ProcessGuidConstantExpression(IEdmGuidConstantExpression expression) @@ -1071,9 +1361,9 @@ protected virtual void ProcessGuidConstantExpression(IEdmGuidConstantExpression this.ProcessExpression(expression); } - protected virtual async Task ProcessGuidConstantExpressionAsync(IEdmGuidConstantExpression expression) + protected virtual Task ProcessGuidConstantExpressionAsync(IEdmGuidConstantExpression expression) { - await this.ProcessExpressionAsync(expression).ConfigureAwait(false); + return this.ProcessExpressionAsync(expression); } protected virtual void ProcessEnumMemberExpression(IEdmEnumMemberExpression expression) @@ -1081,9 +1371,9 @@ protected virtual void ProcessEnumMemberExpression(IEdmEnumMemberExpression expr this.ProcessExpression(expression); } - protected virtual async Task ProcessEnumMemberExpressionAsync(IEdmEnumMemberExpression expression) + protected virtual Task ProcessEnumMemberExpressionAsync(IEdmEnumMemberExpression expression) { - await this.ProcessExpressionAsync(expression).ConfigureAwait(false); + return this.ProcessExpressionAsync(expression); } protected virtual void ProcessDecimalConstantExpression(IEdmDecimalConstantExpression expression) @@ -1091,9 +1381,9 @@ protected virtual void ProcessDecimalConstantExpression(IEdmDecimalConstantExpre this.ProcessExpression(expression); } - protected virtual async Task ProcessDecimalConstantExpressionAsync(IEdmDecimalConstantExpression expression) + protected virtual Task ProcessDecimalConstantExpressionAsync(IEdmDecimalConstantExpression expression) { - await this.ProcessExpressionAsync(expression).ConfigureAwait(false); + return this.ProcessExpressionAsync(expression); } protected virtual void ProcessDateConstantExpression(IEdmDateConstantExpression expression) @@ -1101,9 +1391,9 @@ protected virtual void ProcessDateConstantExpression(IEdmDateConstantExpression this.ProcessExpression(expression); } - protected virtual async Task ProcessDateConstantExpressionAsync(IEdmDateConstantExpression expression) + protected virtual Task ProcessDateConstantExpressionAsync(IEdmDateConstantExpression expression) { - await this.ProcessExpressionAsync(expression).ConfigureAwait(false); + return this.ProcessExpressionAsync(expression); } protected virtual void ProcessTimeOfDayConstantExpression(IEdmTimeOfDayConstantExpression expression) @@ -1111,9 +1401,9 @@ protected virtual void ProcessTimeOfDayConstantExpression(IEdmTimeOfDayConstantE this.ProcessExpression(expression); } - protected virtual async Task ProcessTimeOfDayConstantExpressionAsync(IEdmTimeOfDayConstantExpression expression) + protected virtual Task ProcessTimeOfDayConstantExpressionAsync(IEdmTimeOfDayConstantExpression expression) { - await this.ProcessExpressionAsync(expression).ConfigureAwait(false); + return this.ProcessExpressionAsync(expression); } protected virtual void ProcessDateTimeOffsetConstantExpression(IEdmDateTimeOffsetConstantExpression expression) @@ -1121,9 +1411,9 @@ protected virtual void ProcessDateTimeOffsetConstantExpression(IEdmDateTimeOffse this.ProcessExpression(expression); } - protected virtual async Task ProcessDateTimeOffsetConstantExpressionAsync(IEdmDateTimeOffsetConstantExpression expression) + protected virtual Task ProcessDateTimeOffsetConstantExpressionAsync(IEdmDateTimeOffsetConstantExpression expression) { - await this.ProcessExpressionAsync(expression).ConfigureAwait(false); + return this.ProcessExpressionAsync(expression); } protected virtual void ProcessDurationConstantExpression(IEdmDurationConstantExpression expression) @@ -1131,9 +1421,9 @@ protected virtual void ProcessDurationConstantExpression(IEdmDurationConstantExp this.ProcessExpression(expression); } - protected virtual async Task ProcessDurationConstantExpressionAsync(IEdmDurationConstantExpression expression) + protected virtual Task ProcessDurationConstantExpressionAsync(IEdmDurationConstantExpression expression) { - await this.ProcessExpressionAsync(expression).ConfigureAwait(false); + return this.ProcessExpressionAsync(expression); } protected virtual void ProcessBooleanConstantExpression(IEdmBooleanConstantExpression expression) @@ -1141,9 +1431,9 @@ protected virtual void ProcessBooleanConstantExpression(IEdmBooleanConstantExpre this.ProcessExpression(expression); } - protected virtual async Task ProcessBooleanConstantExpressionAsync(IEdmBooleanConstantExpression expression) + protected virtual Task ProcessBooleanConstantExpressionAsync(IEdmBooleanConstantExpression expression) { - await this.ProcessExpressionAsync(expression).ConfigureAwait(false); + return this.ProcessExpressionAsync(expression); } protected virtual void ProcessCastExpression(IEdmCastExpression expression) @@ -1156,8 +1446,8 @@ protected virtual void ProcessCastExpression(IEdmCastExpression expression) protected virtual async Task ProcessCastExpressionAsync(IEdmCastExpression expression) { await this.ProcessExpressionAsync(expression).ConfigureAwait(false); - this.VisitTypeReference(expression.Type); - this.VisitExpression(expression.Operand); + await this.VisitTypeReferenceAsync(expression.Type).ConfigureAwait(false); + await this.VisitExpressionAsync(expression.Operand).ConfigureAwait(false); } @@ -1166,11 +1456,9 @@ protected virtual void ProcessLabeledExpression(IEdmLabeledExpression element) this.VisitExpression(element.Expression); } - protected virtual Task ProcessLabeledExpressionAsync(IEdmLabeledExpression element) + protected virtual async Task ProcessLabeledExpressionAsync(IEdmLabeledExpression element) { - this.VisitExpression(element.Expression); - - return TaskUtils.CompletedTask; + await this.VisitExpressionAsync(element.Expression).ConfigureAwait(false); } protected virtual void ProcessPropertyConstructor(IEdmPropertyConstructor constructor) @@ -1178,11 +1466,9 @@ protected virtual void ProcessPropertyConstructor(IEdmPropertyConstructor constr this.VisitExpression(constructor.Value); } - protected virtual Task ProcessPropertyConstructorAsync(IEdmPropertyConstructor constructor) + protected virtual async Task ProcessPropertyConstructorAsync(IEdmPropertyConstructor constructor) { - this.VisitExpression(constructor.Value); - - return TaskUtils.CompletedTask; + await this.VisitExpressionAsync(constructor.Value).ConfigureAwait(false); } protected virtual void ProcessNullConstantExpression(IEdmNullExpression expression) @@ -1190,9 +1476,9 @@ protected virtual void ProcessNullConstantExpression(IEdmNullExpression expressi this.ProcessExpression(expression); } - protected virtual async Task ProcessNullConstantExpressionAsync(IEdmNullExpression expression) + protected virtual Task ProcessNullConstantExpressionAsync(IEdmNullExpression expression) { - await this.ProcessExpressionAsync(expression).ConfigureAwait(false); + return this.ProcessExpressionAsync(expression); } #endregion @@ -1218,9 +1504,9 @@ protected virtual void ProcessEntityContainerElement(IEdmEntityContainerElement this.ProcessNamedElement(element); } - protected virtual async Task ProcessEntityContainerElementAsync(IEdmEntityContainerElement element) + protected virtual Task ProcessEntityContainerElementAsync(IEdmEntityContainerElement element) { - await this.ProcessNamedElementAsync(element).ConfigureAwait(false); + return this.ProcessNamedElementAsync(element); } protected virtual void ProcessEntitySet(IEdmEntitySet set) @@ -1228,9 +1514,9 @@ protected virtual void ProcessEntitySet(IEdmEntitySet set) this.ProcessEntityContainerElement(set); } - protected virtual async Task ProcessEntitySetAsync(IEdmEntitySet set) + protected virtual Task ProcessEntitySetAsync(IEdmEntitySet set) { - await this.ProcessEntityContainerElementAsync(set).ConfigureAwait(false); + return this.ProcessEntityContainerElementAsync(set); } protected virtual void ProcessSingleton(IEdmSingleton singleton) @@ -1238,9 +1524,9 @@ protected virtual void ProcessSingleton(IEdmSingleton singleton) this.ProcessEntityContainerElement(singleton); } - protected virtual async Task ProcessSingletonAsync(IEdmSingleton singleton) + protected virtual Task ProcessSingletonAsync(IEdmSingleton singleton) { - await this.ProcessEntityContainerElementAsync(singleton).ConfigureAwait(false); + return this.ProcessEntityContainerElementAsync(singleton); } #endregion @@ -1276,9 +1562,9 @@ protected virtual void ProcessActionImport(IEdmActionImport actionImport) this.ProcessEntityContainerElement(actionImport); } - protected virtual async Task ProcessActionImportAsync(IEdmActionImport actionImport) + protected virtual Task ProcessActionImportAsync(IEdmActionImport actionImport) { - await this.ProcessEntityContainerElementAsync(actionImport).ConfigureAwait(false); + return this.ProcessEntityContainerElementAsync(actionImport); } protected virtual void ProcessFunctionImport(IEdmFunctionImport functionImport) @@ -1286,9 +1572,9 @@ protected virtual void ProcessFunctionImport(IEdmFunctionImport functionImport) this.ProcessEntityContainerElement(functionImport); } - protected virtual async Task ProcessFunctionImportAsync(IEdmFunctionImport functionImport) + protected virtual Task ProcessFunctionImportAsync(IEdmFunctionImport functionImport) { - await this.ProcessEntityContainerElementAsync(functionImport).ConfigureAwait(false); + return this.ProcessEntityContainerElementAsync(functionImport); } protected virtual void ProcessOperation(IEdmOperation operation) @@ -1303,7 +1589,7 @@ protected virtual void ProcessOperation(IEdmOperation operation) protected virtual async Task ProcessOperationAsync(IEdmOperation operation) { // Do not visit vocabularyAnnotatable because functions and operation imports are always going to be either a schema element or a container element and will be visited through those paths. - this.VisitOperationParameters(operation.Parameters); + await this.VisitOperationParametersAsync(operation.Parameters).ConfigureAwait(false); IEdmOperationReturn operationReturn = operation.GetReturn(); await this.ProcessOperationReturnAsync(operationReturn).ConfigureAwait(false); @@ -1320,7 +1606,7 @@ protected virtual async Task ProcessOperationParameterAsync(IEdmOperationParamet { await this.ProcessVocabularyAnnotatableAsync(parameter).ConfigureAwait(false); await this.ProcessNamedElementAsync(parameter).ConfigureAwait(false); - this.VisitTypeReference(parameter.Type); + await this.VisitTypeReferenceAsync(parameter.Type).ConfigureAwait(false); } protected virtual void ProcessOperationReturn(IEdmOperationReturn operationReturn) @@ -1342,7 +1628,7 @@ protected virtual async Task ProcessOperationReturnAsync(IEdmOperationReturn ope } await this.ProcessVocabularyAnnotatableAsync(operationReturn).ConfigureAwait(false); - this.VisitTypeReference(operationReturn.Type); + await this.VisitTypeReferenceAsync(operationReturn.Type).ConfigureAwait(false); } #endregion diff --git a/src/Microsoft.OData.Edm/Helpers/TaskUtils.cs b/src/Microsoft.OData.Edm/Helpers/TaskUtils.cs index d6038a71c5..0e40728583 100644 --- a/src/Microsoft.OData.Edm/Helpers/TaskUtils.cs +++ b/src/Microsoft.OData.Edm/Helpers/TaskUtils.cs @@ -4,12 +4,14 @@ // //--------------------------------------------------------------------- +using System; using System.Threading.Tasks; namespace Microsoft.OData.Edm.Helpers { internal class TaskUtils { + #region Completed task /// /// Already completed task. /// @@ -40,5 +42,36 @@ public static Task CompletedTask return _completedTask; } } + #endregion + + #region Faulted task + /// + /// Returns an already completed task instance with the specified error. + /// + /// The exception of the faulted result. + /// An already completed task with the specified exception. + internal static Task GetFaultedTask(Exception exception) + { + // Since there's no non-generic version use a dummy object return value and cast to non-generic version. + return GetFaultedTask(exception); + } + + /// + /// Returns an already completed task instance with the specified error. + /// + /// Type of the result. + /// The exception of the faulted result. + /// An already completed task with the specified exception. + internal static Task GetFaultedTask(Exception exception) + { +#if NETSTANDARD2_0 || NETCOREAPP3_1_OR_GREATER + return Task.FromException(exception); +#else + TaskCompletionSource taskCompletionSource = new TaskCompletionSource(); + taskCompletionSource.SetException(exception); + return taskCompletionSource.Task; +#endif + } + #endregion } } diff --git a/test/FunctionalTests/Microsoft.OData.Core.Tests/AsyncYieldStream.cs b/test/FunctionalTests/Microsoft.OData.Core.Tests/AsyncYieldStream.cs new file mode 100644 index 0000000000..c271bc8a43 --- /dev/null +++ b/test/FunctionalTests/Microsoft.OData.Core.Tests/AsyncYieldStream.cs @@ -0,0 +1,138 @@ +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace Microsoft.OData.Tests +{ + /// + /// Stream wrapper that yields in every async method + /// to ensure the read/write operations complete asynchronously + /// even when the operate completely in-memory. This helps simulate + /// async I/O in unit tests without making requests out of the process. + /// + internal class AsyncYieldStream : Stream + { + private Stream stream; + public AsyncYieldStream(Stream stream) + { + this.stream = stream; + } + + public override bool CanRead => this.stream.CanRead; + + public override bool CanSeek => this.stream.CanSeek; + + public override bool CanWrite => this.stream.CanWrite; + + public override long Length => this.stream.Length; + + public override int ReadTimeout { get => this.stream.ReadTimeout; set => this.stream.ReadTimeout = value; } + + public override int WriteTimeout { get => this.stream.WriteTimeout; set => this.stream.WriteTimeout = value; } + + public override bool CanTimeout => this.stream.CanTimeout; + +#if NETCOREAPP3_1_OR_GREATER + public override void Close() + { + this.stream.Close(); + } +#endif + + public override long Position { get => this.stream.Position; set => this.stream.Position = value; } + + public override void Flush() + { + this.stream.Flush(); + } + + public override async Task FlushAsync(CancellationToken cancellationToken) + { + await Task.Yield(); + await base.FlushAsync(cancellationToken); + } + + public override int Read(byte[] buffer, int offset, int count) + { + return this.stream.Read(buffer, offset, count); + } + + public override async Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + await Task.Yield(); + int result = await this.stream.ReadAsync(buffer, offset, count, cancellationToken); + return result; + } + + public override int ReadByte() + { + return this.stream.ReadByte(); + } + + public override void WriteByte(byte value) + { + this.stream.WriteByte(value); + } + + public override async Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken) + { + await Task.Yield(); + await this.stream.CopyToAsync(destination, bufferSize, cancellationToken); + } + + public override long Seek(long offset, SeekOrigin origin) + { + return this.stream.Seek(offset, origin); + } + + public override void SetLength(long value) + { + this.stream.SetLength(value); + } + + public override void Write(byte[] buffer, int offset, int count) + { + this.stream.Write(buffer, offset, count); + } + + public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + await Task.Yield(); + await this.stream.WriteAsync(buffer, offset, count, cancellationToken); + } + +#if NETCOREAPP3_1_OR_GREATER + public override async ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default) + { + await Task.Yield(); + await base.WriteAsync(buffer, cancellationToken); + } + + public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state) + { + return this.stream.BeginRead(buffer, offset, count, callback, state); + } + + public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state) + { + return this.stream.BeginWrite(buffer, offset, count, callback, state); + } + + public override int EndRead(IAsyncResult asyncResult) + { + return this.stream.EndRead(asyncResult); + } + + public override void EndWrite(IAsyncResult asyncResult) + { + this.stream.EndWrite(asyncResult); + } +#endif + + public override string ToString() + { + return this.stream.ToString(); + } + } +} \ No newline at end of file diff --git a/test/FunctionalTests/Microsoft.OData.Core.Tests/ODataMessageWriterTests.cs b/test/FunctionalTests/Microsoft.OData.Core.Tests/ODataMessageWriterTests.cs index 643a2f9edf..b564a554a7 100644 --- a/test/FunctionalTests/Microsoft.OData.Core.Tests/ODataMessageWriterTests.cs +++ b/test/FunctionalTests/Microsoft.OData.Core.Tests/ODataMessageWriterTests.cs @@ -1010,6 +1010,66 @@ public async Task WriteMetadataDocumentPayload_MustEqual_WriteMetadataDocumentAs // Assert Assert.Equal(syncPayload, asyncPayload); } + + [Fact] + public async Task WriteLargeMetadataDocumentPayload_MustEqual_WriteLargeMetadataDocumentAsyncPayload_ForJsonCsdl() + { + // Arrange + var contentType = "application/json"; + + // Act + for (int i = 0; i < 10; i++) + { + // Json CSDL generated synchronously + string syncPayload = this.WriteAndGetPayload(_largeEdmModel, contentType, omWriter => + { + omWriter.WriteMetadataDocument(); + }); + + // Json CSDL generated asynchronously + string asyncPayload = await this.WriteAndGetPayloadWithAsyncYieldStreamAsync(_largeEdmModel, contentType, async omWriter => + { + await omWriter.WriteMetadataDocumentAsync(); + }); + + // Assert + Assert.Equal(syncPayload, asyncPayload); + } + } + + [Fact] + public async Task WriteLargeMetadataDocumentAsync_CalledMultipleTimes_WorksForJsonCsdl_NoExceptionThrown() + { + // Arrange + string contentType = "application/json"; + + var message = new InMemoryMessage() { Stream = new AsyncYieldStream(new MemoryStream()) }; + + message.SetHeader("Content-Type", contentType); + + var writerSettings = new ODataMessageWriterSettings(); + writerSettings.EnableMessageStreamDisposal = false; + writerSettings.BaseUri = new Uri("http://www.example.com/"); + writerSettings.SetServiceDocumentUri(new Uri("http://www.example.com/")); + + // Act + for (int i = 0; i < 5; i++) + { + var exception = await Record.ExceptionAsync(async () => + { +#if NETCOREAPP3_1_OR_GREATER + await using (var msgWriter = new ODataMessageWriter((IODataResponseMessageAsync)message, writerSettings, _largeEdmModel)) +#else + using (var msgWriter = new ODataMessageWriter((IODataResponseMessageAsync)message, writerSettings, _largeEdmModel)) +#endif + { + await msgWriter.WriteMetadataDocumentAsync(); + } + }); + + Assert.Null(exception); + } + } #endif [Fact] @@ -1055,6 +1115,40 @@ public async Task WriteMetadataDocumentAsync_WorksForXmlCsdl() "", payload); } + [Fact] + public async Task WriteLargeMetadataDocumentAsync_CalledMultipleTimes_WorksForXmlCsdl_NoExceptionThrown() + { + // Arrange + string contentType = "application/xml"; + + var message = new InMemoryMessage() { Stream = new AsyncYieldStream(new MemoryStream()) }; + + message.SetHeader("Content-Type", contentType); + + var writerSettings = new ODataMessageWriterSettings(); + writerSettings.EnableMessageStreamDisposal = false; + writerSettings.BaseUri = new Uri("http://www.example.com/"); + writerSettings.SetServiceDocumentUri(new Uri("http://www.example.com/")); + + // Act + for (int i = 0; i < 5; i++) + { + var exception = await Record.ExceptionAsync(async () => + { +#if NETCOREAPP3_1_OR_GREATER + await using (var msgWriter = new ODataMessageWriter((IODataResponseMessageAsync)message, writerSettings, _largeEdmModel)) +#else + using (var msgWriter = new ODataMessageWriter((IODataResponseMessageAsync)message, writerSettings, _largeEdmModel)) +#endif + { + await msgWriter.WriteMetadataDocumentAsync(); + } + }); + + Assert.Null(exception); + } + } + [Fact] public async Task WriteMetadataDocumentAsync_WorksForXmlCsdl_WithNoSynchronousIOSupport() { @@ -1112,6 +1206,33 @@ public async Task WriteMetadataDocumentPayload_MustEqual_WriteMetadataDocumentAs Assert.Equal(asyncPayload, syncPayload); } + [Fact] + public async Task WriteLargeMetadataDocumentPayload_MustEqual_WriteLargeMetadataDocumentAsyncPayload_ForXmlCsdl() + { + // Arrange + var contentType = "application/xml"; + + // Act + string syncPayload = this.WriteAndGetPayload(_largeEdmModel, contentType, omWriter => + { + omWriter.WriteMetadataDocument(); + }); + + string asyncPayload = await this.WriteAndGetPayloadAsync(_largeEdmModel, contentType, async omWriter => + { + await omWriter.WriteMetadataDocumentAsync(); + }); + + string asyncPayloadWithAsyncYield = await this.WriteAndGetPayloadWithAsyncYieldStreamAsync(_largeEdmModel, contentType, async omWriter => + { + await omWriter.WriteMetadataDocumentAsync(); + }); + + // Assert + Assert.Equal(syncPayload, asyncPayload); + Assert.Equal(syncPayload, asyncPayloadWithAsyncYield); + } + #region "DisposeAsync" #if NETCOREAPP3_1_OR_GREATER @@ -1317,6 +1438,9 @@ public async void DisposeAsync_Should_Dispose_Stream_Asynchronously_AfterWriting private static IEdmModel _edmModel; + // Large model with 1000 entity types + private static IEdmModel _largeEdmModel = GetLargeEdmModel(); + private static IEdmModel GetEdmModel() { if (_edmModel != null) @@ -1342,6 +1466,53 @@ private static IEdmModel GetEdmModel() return edmModel; } + /// + /// This large EdmModel is used to test issues related to writing large metadata documents. + /// For example, async writing of large metadata documents, writing large metadata documents multiple times, etc. + /// + /// Large EdmModel + private static IEdmModel GetLargeEdmModel() + { + EdmModel edmModel = new EdmModel(); + + EdmEntityContainer container = new EdmEntityContainer("NS", "Container"); + edmModel.AddElement(container); + + string longString = GenerateLongString(); + + // Add 500 entity types + for (int i = 0; i < 500; i++) + { + EdmEntityType entityType = new EdmEntityType("NS", $"Entity{i}"); + var idProperty = new EdmStructuralProperty(entityType, $"Entity{i}Id", EdmCoreModel.Instance.GetInt32(false)); + entityType.AddProperty(idProperty); + entityType.AddKeys(new IEdmStructuralProperty[] { idProperty }); + + // Add 60 properties to each entity type + for (int j = 0; j < 20; j++) + { + entityType.AddProperty(new EdmStructuralProperty(entityType, $"PropertyString{longString}{j}", EdmCoreModel.Instance.GetString(false))); + entityType.AddProperty(new EdmStructuralProperty(entityType, $"PropertyInt{longString}{j}", EdmCoreModel.Instance.GetInt32(false))); + entityType.AddProperty(new EdmStructuralProperty(entityType, $"PropertyBool{j}{longString}{j}", EdmCoreModel.Instance.GetBoolean(false))); + } + + edmModel.AddElement(entityType); + container.AddEntitySet($"Entities{i}{longString}", entityType); + } + + return edmModel; + } + + private static string GenerateLongString() + { + var sb = new StringBuilder(); + for (int i = 0; i < 20; i++) + { + sb.Append("abcxyz"); + } + return sb.ToString(); + } + /// /// Write a message using an instance /// and returns the written payload as a string. @@ -1517,6 +1688,43 @@ private async Task WriteAndGetPayloadAsync( } } + /// + /// Asynchronously writes a message using an instance, AsyncYieldStream + /// and returns the written payload as a string. + /// + /// The used to initialize the writer. + /// The value of the message's Content-Type header. + /// The action that writes the payload. + /// A task representing the asynchrnous operation. The result of the task will be the written output. + private async Task WriteAndGetPayloadWithAsyncYieldStreamAsync(IEdmModel edmModel, string contentType, Func test) + { + var message = new InMemoryMessage() { Stream = new AsyncYieldStream(new MemoryStream()) }; + + message.SetHeader("Content-Type", contentType); + + var writerSettings = new ODataMessageWriterSettings(); + writerSettings.EnableMessageStreamDisposal = false; + writerSettings.BaseUri = new Uri("http://www.example.com/"); + writerSettings.SetServiceDocumentUri(new Uri("http://www.example.com/")); + +#if NETCOREAPP3_1_OR_GREATER + await using (var msgWriter = new ODataMessageWriter((IODataResponseMessageAsync)message, writerSettings, edmModel)) +#else + using (var msgWriter = new ODataMessageWriter((IODataResponseMessageAsync)message, writerSettings, edmModel)) +#endif + { + await test(msgWriter); + } + + message.Stream.Seek(0, SeekOrigin.Begin); + + using (var reader = new StreamReader(message.Stream, encoding: Encoding.UTF8, detectEncodingFromByteOrderMarks: true, bufferSize: 1024, leaveOpen: true)) + { + string contents = await reader.ReadToEndAsync(); + return contents; + } + } + private static IServiceProvider CreateTestServiceContainer(Action configureServices) { IContainerBuilder builder = new TestContainerBuilder(); diff --git a/test/FunctionalTests/Microsoft.OData.Edm.Tests/Csdl/Serialization/EdmModelCsdlSerializationVisitorTests.Async.cs b/test/FunctionalTests/Microsoft.OData.Edm.Tests/Csdl/Serialization/EdmModelCsdlSerializationVisitorTests.Async.cs index ccd4894c48..55d1d8d2fd 100644 --- a/test/FunctionalTests/Microsoft.OData.Edm.Tests/Csdl/Serialization/EdmModelCsdlSerializationVisitorTests.Async.cs +++ b/test/FunctionalTests/Microsoft.OData.Edm.Tests/Csdl/Serialization/EdmModelCsdlSerializationVisitorTests.Async.cs @@ -35,7 +35,7 @@ public async Task VerifyComplexTypeWrittenCorrectly_Async() complexType.AddStructuralProperty("Breadth", EdmCoreModel.Instance.GetDecimal(6, 0, true)); // Act & Assert for XML - await VisitAndVerifyXmlAsync(v => v.VisitSchemaType(complexType), + await VisitAndVerifyXmlAsync(v => v.VisitSchemaTypeAsync(complexType), @" @@ -44,7 +44,7 @@ await VisitAndVerifyXmlAsync(v => v.VisitSchemaType(complexType), ").ConfigureAwait(false); // Act & Assert for JSON - await VisitAndVerifyJsonAsync(v => v.VisitSchemaType(complexType), @"{ + await VisitAndVerifyJsonAsync(v => v.VisitSchemaTypeAsync(complexType), @"{ ""Dimensions"": { ""$Kind"": ""ComplexType"", ""Height"": { @@ -87,7 +87,7 @@ public async Task VerifySchemaWithActionsWrittenCorrectly_Async() schema.AddSchemaElement(action); // Act & Assert for XML - await VisitAndVerifyXmlAsync(async (v) => await v.VisitEdmSchemaAsync(schema, null).ConfigureAwait(false), + await VisitAndVerifyXmlAsync((v) => v.VisitEdmSchemaAsync(schema, null), @" @@ -100,7 +100,7 @@ await VisitAndVerifyXmlAsync(async (v) => await v.VisitEdmSchemaAsync(schema, nu ").ConfigureAwait(false); // Act & Assert for JSON - await VisitAndVerifyJsonAsync(async (v) => await v.VisitEdmSchemaAsync(schema, null).ConfigureAwait(false), @"{ + await VisitAndVerifyJsonAsync((v) => v.VisitEdmSchemaAsync(schema, null), @"{ ""NS"": { ""DoStuff"": [ { @@ -146,7 +146,7 @@ public async Task VerifyEntitySetWrittenCorrectly_Async() EdmEntitySet entitySet = new EdmEntitySet(container, "Set", entityType); // Act & Assert for XML - await VisitAndVerifyXmlAsync(async (v) => await v.VisitEntityContainerElementsAsync(new[] { entitySet }).ConfigureAwait(false), + await VisitAndVerifyXmlAsync((v) => v.VisitEntityContainerElementsAsync(new[] { entitySet }), @"").ConfigureAwait(false); // Act & Assert for JSON @@ -159,7 +159,7 @@ await VisitAndVerifyJsonAsync(async (v) => await v.VisitEntityContainerElementsA }").ConfigureAwait(false); // Act & Assert for non-indent JSON - await VisitAndVerifyJsonAsync(async (v) => await v.VisitEntityContainerElementsAsync(new[] { entitySet }).ConfigureAwait(false), + await VisitAndVerifyJsonAsync((v) => v.VisitEntityContainerElementsAsync(new[] { entitySet }), @"{""Set"":{""$Collection"":true,""$Type"":""NS.EntityType""}}", false).ConfigureAwait(false); } @@ -226,7 +226,7 @@ public async Task VerifyEntitySetWithAllInformationsWrittenCorrectly_Async() model.SetVocabularyAnnotation(annotation); // Act & Assert for XML - await VisitAndVerifyXmlAsync(async (v) => await v.VisitEntityContainerElementsAsync(new[] { people }).ConfigureAwait(false), + await VisitAndVerifyXmlAsync((v) => v.VisitEntityContainerElementsAsync(new[] { people }), @" @@ -235,7 +235,7 @@ await VisitAndVerifyXmlAsync(async (v) => await v.VisitEntityContainerElementsAs ").ConfigureAwait(false); // Act & Assert for JSON - await VisitAndVerifyJsonAsync(async (v) => await v.VisitEntityContainerElementsAsync(new[] { people }).ConfigureAwait(false), + await VisitAndVerifyJsonAsync((v) => v.VisitEntityContainerElementsAsync(new[] { people }), @"{ ""People"": { ""$Collection"": true, @@ -259,16 +259,16 @@ public async Task VerifySingletonWrittenCorrectly_Async() IEdmEntityContainer container = new EdmEntityContainer("NS", "Container"); EdmSingleton singleton = new EdmSingleton(container, "Me", entityType); - await VisitAndVerifyXmlAsync(async (v) => await v.VisitEntityContainerElementsAsync(new[] { singleton }).ConfigureAwait(false), + await VisitAndVerifyXmlAsync((v) => v.VisitEntityContainerElementsAsync(new[] { singleton }), @"").ConfigureAwait(false); - await VisitAndVerifyJsonAsync(async (v) => await v.VisitEntityContainerElementsAsync(new[] { singleton }).ConfigureAwait(false), @"{ + await VisitAndVerifyJsonAsync((v) => v.VisitEntityContainerElementsAsync(new[] { singleton }), @"{ ""Me"": { ""$Type"": ""NS.EntityType"" } }").ConfigureAwait(false); - await VisitAndVerifyJsonAsync(async (v) => await v.VisitEntityContainerElementsAsync(new[] { singleton }).ConfigureAwait(false), + await VisitAndVerifyJsonAsync((v) => v.VisitEntityContainerElementsAsync(new[] { singleton }), @"{""Me"":{""$Type"":""NS.EntityType""}}", false).ConfigureAwait(false); } @@ -293,14 +293,14 @@ public async Task VerifySingletonWithAnnotationWrittenCorrectly_Async() annotation.SetSerializationLocation(model, EdmVocabularyAnnotationSerializationLocation.Inline); this.model.SetVocabularyAnnotation(annotation); - await VisitAndVerifyXmlAsync(async (v) => await v.VisitEntityContainerElementsAsync(new[] { singleton }).ConfigureAwait(false), + await VisitAndVerifyXmlAsync((v) => v.VisitEntityContainerElementsAsync(new[] { singleton }), @" NS.Permission/Read NS.Permission/Write ").ConfigureAwait(false); - await VisitAndVerifyJsonAsync(async (v) => await v.VisitEntityContainerElementsAsync(new[] { singleton }).ConfigureAwait(false), @"{ + await VisitAndVerifyJsonAsync((v) => v.VisitEntityContainerElementsAsync(new[] { singleton }), @"{ ""Me"": { ""$Type"": ""NS.EntityType"", ""@NS.MyTerm"": ""Read,Write"" @@ -318,11 +318,11 @@ public async Task VerifyActionImportWrittenCorrectly_Async() var actionImport = new EdmActionImport(defaultContainer, "Checkout", defaultCheckoutAction, null); // Act & Assert for XML - await VisitAndVerifyXmlAsync(async (v) => await v.VisitEntityContainerElementsAsync(new IEdmEntityContainerElement[] { actionImport }).ConfigureAwait(false), + await VisitAndVerifyXmlAsync((v) => v.VisitEntityContainerElementsAsync(new IEdmEntityContainerElement[] { actionImport }), @"").ConfigureAwait(false); // Act & Assert for JSON - await VisitAndVerifyJsonAsync(async (v) => await v.VisitEntityContainerElementsAsync(new IEdmEntityContainerElement[] { actionImport }).ConfigureAwait(false), + await VisitAndVerifyJsonAsync((v) => v.VisitEntityContainerElementsAsync(new IEdmEntityContainerElement[] { actionImport }), @"{ ""Checkout"": { ""$Kind"": ""ActionImport"", @@ -331,7 +331,7 @@ await VisitAndVerifyJsonAsync(async (v) => await v.VisitEntityContainerElementsA }").ConfigureAwait(false); // Act & Assert for non-indent JSON - await VisitAndVerifyJsonAsync(async (v) => await v.VisitEntityContainerElementsAsync(new IEdmEntityContainerElement[] { actionImport }).ConfigureAwait(false), + await VisitAndVerifyJsonAsync((v) => v.VisitEntityContainerElementsAsync(new IEdmEntityContainerElement[] { actionImport }), @"{""Checkout"":{""$Kind"":""ActionImport"",""$Action"":""Default.NameSpace2.CheckOut""}}", false).ConfigureAwait(false); } @@ -343,11 +343,11 @@ public async Task VerifyTwoIdenticalNamedActionImportsWithNoEntitySetPropertyOnl var actionImport2 = new EdmActionImport(defaultContainer, "Checkout", defaultCheckoutAction, null); // Act & Assert for XML - await VisitAndVerifyXmlAsync(async (v) => await v.VisitEntityContainerElementsAsync(new IEdmEntityContainerElement[] { actionImport, actionImport2 }).ConfigureAwait(false), + await VisitAndVerifyXmlAsync((v) => v.VisitEntityContainerElementsAsync(new IEdmEntityContainerElement[] { actionImport, actionImport2 }), @"").ConfigureAwait(false); // Act & Assert for JSON - await VisitAndVerifyJsonAsync(async (v) => await v.VisitEntityContainerElementsAsync(new IEdmEntityContainerElement[] { actionImport, actionImport2 }).ConfigureAwait(false), + await VisitAndVerifyJsonAsync((v) => v.VisitEntityContainerElementsAsync(new IEdmEntityContainerElement[] { actionImport, actionImport2 }), @"{ ""Checkout"": { ""$Kind"": ""ActionImport"", @@ -364,11 +364,11 @@ public async Task VerifyTwoIdenticalNamedActionImportsWithSameEntitySetOnlyWritt var actionImport2 = new EdmActionImport(defaultContainer, "Checkout", defaultCheckoutAction, new EdmPathExpression("Set")); // Act & Assert for XML - await VisitAndVerifyXmlAsync(async (v) => await v.VisitEntityContainerElementsAsync(new IEdmEntityContainerElement[] { actionImport, actionImport2 }).ConfigureAwait(false), + await VisitAndVerifyXmlAsync((v) => v.VisitEntityContainerElementsAsync(new IEdmEntityContainerElement[] { actionImport, actionImport2 }), @"").ConfigureAwait(false); // Act & Assert for JSON - await VisitAndVerifyJsonAsync(async (v) => await v.VisitEntityContainerElementsAsync(new IEdmEntityContainerElement[] { actionImport, actionImport2 }).ConfigureAwait(false), + await VisitAndVerifyJsonAsync((v) => v.VisitEntityContainerElementsAsync(new IEdmEntityContainerElement[] { actionImport, actionImport2 }), @"{ ""Checkout"": { ""$Kind"": ""ActionImport"", @@ -386,11 +386,11 @@ public async Task VerifyTwoIdenticalNamedActionImportsWithSameEdmPathOnlyWritten var actionImport2 = new EdmActionImport(defaultContainer, "Checkout", defaultCheckoutAction, new EdmPathExpression("path1", "path2")); // Act & Assert for XML - await VisitAndVerifyXmlAsync(async (v) => await v.VisitEntityContainerElementsAsync(new IEdmEntityContainerElement[] { actionImport, actionImport2 }).ConfigureAwait(false), + await VisitAndVerifyXmlAsync((v) => v.VisitEntityContainerElementsAsync(new IEdmEntityContainerElement[] { actionImport, actionImport2 }), @"").ConfigureAwait(false); // Act & Assert for JSON - await VisitAndVerifyJsonAsync(async (v) => await v.VisitEntityContainerElementsAsync(new IEdmEntityContainerElement[] { actionImport, actionImport2 }).ConfigureAwait(false), + await VisitAndVerifyJsonAsync((v) => v.VisitEntityContainerElementsAsync(new IEdmEntityContainerElement[] { actionImport, actionImport2 }), @"{ ""Checkout"": { ""$Kind"": ""ActionImport"", @@ -414,14 +414,14 @@ public async Task VerifyIdenticalNamedActionImportsWithDifferentEntitySetPropert var actionImportWithUniqueEdmPath = new EdmActionImport(defaultContainer, "Checkout", defaultCheckoutAction, new EdmPathExpression("path1", "path2")); // Act & Assert for XML - await VisitAndVerifyXmlAsync(async (v) => await v.VisitEntityContainerElementsAsync(new IEdmEntityContainerElement[] { actionImportOnSet, actionImportOnSet2, actionImportWithNoEntitySet, actionImportWithUniqueEdmPath }).ConfigureAwait(false), + await VisitAndVerifyXmlAsync((v) => v.VisitEntityContainerElementsAsync(new IEdmEntityContainerElement[] { actionImportOnSet, actionImportOnSet2, actionImportWithNoEntitySet, actionImportWithUniqueEdmPath }), @" ").ConfigureAwait(false); // Act & Assert for JSON - await VisitAndVerifyJsonAsync(async (v) => await v.VisitEntityContainerElementsAsync(new IEdmEntityContainerElement[] { actionImportOnSet, actionImportOnSet2, actionImportWithNoEntitySet, actionImportWithUniqueEdmPath }).ConfigureAwait(false), + await VisitAndVerifyJsonAsync((v) => v.VisitEntityContainerElementsAsync(new IEdmEntityContainerElement[] { actionImportOnSet, actionImportOnSet2, actionImportWithNoEntitySet, actionImportWithUniqueEdmPath }), @"{ ""Checkout"": { ""$Kind"": ""ActionImport"", @@ -753,7 +753,7 @@ await VisitAndVerifyJsonAsync(async (v) => await v.VisitEdmSchemaAsync(schema, n } #endregion - internal async Task VisitAndVerifyXmlAsync(Action testAction, string expected, bool indent = true) + internal async Task VisitAndVerifyXmlAsync(Func testAction, string expected, bool indent = true) { XmlWriter xmlWriter; MemoryStream memStream; @@ -770,7 +770,7 @@ internal async Task VisitAndVerifyXmlAsync(Action testAction, string expected, bool indent = true, bool wrapper = true) + internal async Task VisitAndVerifyJsonAsync(Func testAction, string expected, bool indent = true, bool wrapper = true) { #if NETCOREAPP3_1 Version edmxVersion = this.model.GetEdmxVersion(); @@ -806,7 +806,7 @@ internal async Task VisitAndVerifyJsonAsync(Action