From d5f88cf7741264fef1641b06da4bac64dcb15f9f Mon Sep 17 00:00:00 2001 From: Samuel Wanjohi Date: Tue, 23 Jul 2024 12:36:31 +0300 Subject: [PATCH 1/2] Provide async APIs for CsdlWriter and SchemaWriter (#3006) * Add corresponding async methods for syncronous methods of EdmModelCsdlSchemaJsonWriter * Implemented asynchronous counterparts for synchronous virtual and abstract methods for EdmModelCsdlSchemaWriter class * Added asynchronous implementations for the synchronous methods in EdmModelCsdlSchemaXmlWriter * Added corresponding asynchronous methods for EdmModelReferenceElementsJsonVisitor, EdmModelCsdlSerializationVisitor and EdmModelReferenceElementsXmlVisitor * Added asynchronous counterparts for csdl writer methods * Added async corresponding sync methods for EdmModelVisitor * Fix WriteStartElementAsync prefix and namespace missing * Added csdl async/await and functional tests for csdl writer async methods * Added async APIs to net48 public API * Fixed comments to add ConfigureAwait(false) * Added configureAwait(false) to await tests * Elude await/async in BeginElementAsync method Func params * Rewrite the doc string of the async methods to add and provide correct summary * Rewrite WriteSchemata to WriteSchema * Move the asynhronous tests to .Async partial classes * Added ContextUrlWriterReaderTests.Async partial class with async tests and fix asynhronous equal true when writing xml * Added doc string for * Resolve missing spaces * Added Returns Documentation string for WriteCsdlAsync * Remove unnecessary 'else' * Added WriteMetadataDocumentAsync methods and tests * Rename test functions for WriteMetadataDocumentAsync * Renaming WriteSchema to WriteSchemas and WriteSchemaAsync to WriteSchemasAsync * Provide the correct file in copyright for the added files * Rename partial class OasisActionsFunctionsRelationshipChangesAcceptanceTests to OasisActionsFunctionsRelationshipChangesAcceptanceTest to reuse the methods used in main class OasisActionsFunctionsRelationshipChangesAcceptanceTest --- src/Microsoft.OData.Core/ErrorUtils.cs | 109 +- .../Metadata/ODataMetadataWriterUtils.cs | 13 + .../ODataMetadataJsonOutputContext.cs | 32 +- .../ODataMetadataOutputContext.cs | 42 +- .../Csdl/CsdlJsonWriter.cs | 92 +- src/Microsoft.OData.Edm/Csdl/CsdlWriter.cs | 70 + src/Microsoft.OData.Edm/Csdl/CsdlXmlWriter.cs | 115 + src/Microsoft.OData.Edm/Csdl/SchemaWriter.cs | 69 + .../EdmModelCsdlSchemaJsonWriter.cs | 2030 ++++++++++- .../Serialization/EdmModelCsdlSchemaWriter.cs | 146 + .../EdmModelCsdlSchemaXmlWriter.cs | 1591 +++++++++ .../EdmModelCsdlSerializationVisitor.cs | 976 ++++++ .../EdmModelReferenceElementsJsonVisitor.cs | 137 +- .../EdmModelReferenceElementsXmlVisitor.cs | 52 + src/Microsoft.OData.Edm/EdmModelVisitor.cs | 463 +++ .../PublicAPI/net45/PublicAPI.Unshipped.txt | 4 + .../netstandard1.1/PublicAPI.Unshipped.txt | 4 + .../netstandard2.0/PublicAPI.Unshipped.txt | 6 + .../ODataMessageWriterTests.cs | 172 + .../ContextUrlWriterReaderTests.Async.cs | 335 ++ .../Roundtrip/ContextUrlWriterReaderTests.cs | 2 +- .../Csdl/CsdlReaderTests.Async.cs | 325 ++ .../Csdl/CsdlWriterTests.Async.cs | 3014 +++++++++++++++++ .../Csdl/CsdlWriterTests.TargetPath.Async.cs | 417 +++ .../EdmModelCsdlSchemaWriterTests.Async.cs | 187 + .../EdmModelCsdlSchemaWriterTests.cs | 2 +- ...odelCsdlSerializationVisitorTests.Async.cs | 828 +++++ .../EdmModelCsdlSerializationVisitorTests.cs | 2 +- ...elationshipChangesAcceptanceTests.Async.cs | 42 + ...tionsRelationshipChangesAcceptanceTests.cs | 2 +- ...elationshipChangesAcceptanceTests.Async.cs | 41 + ...OasisRelationshipChangesAcceptanceTests.cs | 2 +- .../AlternateKeysVocabularyTests.Async.cs | 68 + .../AlternateKeysVocabularyTests.cs | 2 +- .../AuthorizationVocabularyTests.Async.cs | 156 + .../AuthorizationVocabularyTests.cs | 2 +- .../CapabilitiesVocabularyTests.Async.cs | 863 +++++ .../CapabilitiesVocabularyTests.cs | 2 +- .../CommunityVocabularyTests.Async.cs | 49 + .../Vocabularies/CommunityVocabularyTests.cs | 2 +- .../Vocabularies/CoreVocabularyTests.Async.cs | 473 +++ .../Vocabularies/CoreVocabularyTests.cs | 2 +- .../ValidationVocabularyTests.Async.cs | 140 + .../Vocabularies/ValidationVocabularyTests.cs | 4 +- .../Microsoft.OData.PublicApi.net45.bsl | 16 + ...crosoft.OData.PublicApi.netstandard1.1.bsl | 16 + ...crosoft.OData.PublicApi.netstandard2.0.bsl | 16 + 47 files changed, 13062 insertions(+), 71 deletions(-) create mode 100644 test/FunctionalTests/Microsoft.OData.Core.Tests/ScenarioTests/Roundtrip/ContextUrlWriterReaderTests.Async.cs create mode 100644 test/FunctionalTests/Microsoft.OData.Edm.Tests/Csdl/CsdlReaderTests.Async.cs create mode 100644 test/FunctionalTests/Microsoft.OData.Edm.Tests/Csdl/CsdlWriterTests.Async.cs create mode 100644 test/FunctionalTests/Microsoft.OData.Edm.Tests/Csdl/CsdlWriterTests.TargetPath.Async.cs create mode 100644 test/FunctionalTests/Microsoft.OData.Edm.Tests/Csdl/Serialization/EdmModelCsdlSchemaWriterTests.Async.cs create mode 100644 test/FunctionalTests/Microsoft.OData.Edm.Tests/Csdl/Serialization/EdmModelCsdlSerializationVisitorTests.Async.cs create mode 100644 test/FunctionalTests/Microsoft.OData.Edm.Tests/ScenarioTests/OasisActionsFunctionsRelationshipChangesAcceptanceTests.Async.cs create mode 100644 test/FunctionalTests/Microsoft.OData.Edm.Tests/ScenarioTests/OasisRelationshipChangesAcceptanceTests.Async.cs create mode 100644 test/FunctionalTests/Microsoft.OData.Edm.Tests/Vocabularies/AlternateKeysVocabularyTests.Async.cs create mode 100644 test/FunctionalTests/Microsoft.OData.Edm.Tests/Vocabularies/AuthorizationVocabularyTests.Async.cs create mode 100644 test/FunctionalTests/Microsoft.OData.Edm.Tests/Vocabularies/CapabilitiesVocabularyTests.Async.cs create mode 100644 test/FunctionalTests/Microsoft.OData.Edm.Tests/Vocabularies/CommunityVocabularyTests.Async.cs create mode 100644 test/FunctionalTests/Microsoft.OData.Edm.Tests/Vocabularies/CoreVocabularyTests.Async.cs create mode 100644 test/FunctionalTests/Microsoft.OData.Edm.Tests/Vocabularies/ValidationVocabularyTests.Async.cs diff --git a/src/Microsoft.OData.Core/ErrorUtils.cs b/src/Microsoft.OData.Core/ErrorUtils.cs index 30995dc4e0..3da7da282c 100644 --- a/src/Microsoft.OData.Core/ErrorUtils.cs +++ b/src/Microsoft.OData.Core/ErrorUtils.cs @@ -6,6 +6,7 @@ using System; using System.Diagnostics; +using System.Threading.Tasks; using System.Xml; using Microsoft.OData.Metadata; @@ -56,6 +57,25 @@ internal static void WriteXmlError(XmlWriter writer, ODataError error, bool incl WriteXmlError(writer, code, message, innerError, maxInnerErrorDepth); } + /// + /// Asynchronously Write an error message. + /// + /// The Xml writer to write to. + /// The error instance to write. + /// A flag indicating whether error details should be written (in debug mode only) or not. + /// The maximum number of nested inner errors to allow. + internal static async Task WriteXmlErrorAsync(XmlWriter writer, ODataError error, bool includeDebugInformation, int maxInnerErrorDepth) + { + Debug.Assert(writer != null, "writer != null"); + Debug.Assert(error != null, "error != null"); + + string code, message; + ErrorUtils.GetErrorDetails(error, out code, out message); + + ODataInnerError innerError = includeDebugInformation ? error.InnerError : null; + await WriteXmlErrorAsync(writer, code, message, innerError, maxInnerErrorDepth).ConfigureAwait(false); + } + /// /// Write an error message. /// @@ -88,6 +108,38 @@ private static void WriteXmlError(XmlWriter writer, string code, string message, writer.WriteEndElement(); } + /// + /// Asynchronously Write an error message. + /// + /// The Xml writer to write to. + /// The code of the error. + /// The message of the error. + /// Inner error details that will be included in debug mode (if present). + /// The maximum number of nested inner errors to allow. + private static async Task WriteXmlErrorAsync(XmlWriter writer, string code, string message, ODataInnerError innerError, int maxInnerErrorDepth) + { + Debug.Assert(writer != null, "writer != null"); + Debug.Assert(code != null, "code != null"); + Debug.Assert(message != null, "message != null"); + + // + await writer.WriteStartElementAsync(ODataMetadataConstants.ODataMetadataNamespacePrefix, ODataMetadataConstants.ODataErrorElementName, ODataMetadataConstants.ODataMetadataNamespace).ConfigureAwait(false); + + // code + await writer.WriteElementStringAsync(ODataMetadataConstants.ODataMetadataNamespacePrefix, ODataMetadataConstants.ODataErrorCodeElementName, ODataMetadataConstants.ODataMetadataNamespace, code).ConfigureAwait(false); + + // error message + await writer.WriteElementStringAsync(ODataMetadataConstants.ODataMetadataNamespacePrefix, ODataMetadataConstants.ODataErrorMessageElementName, ODataMetadataConstants.ODataMetadataNamespace, message).ConfigureAwait(false); + + if (innerError != null) + { + await WriteXmlInnerErrorAsync(writer, innerError, ODataMetadataConstants.ODataInnerErrorElementName, /* recursionDepth */ 0, maxInnerErrorDepth).ConfigureAwait(false); + } + + // + await writer.WriteEndElementAsync().ConfigureAwait(false); + } + /// /// Writes the inner exception information in debug mode. /// @@ -142,5 +194,60 @@ private static void WriteXmlInnerError(XmlWriter writer, ODataInnerError innerEr // or writer.WriteEndElement(); } + + /// + /// Asynchronously Writes the inner exception information in debug mode. + /// + /// The Xml writer to write to. + /// The inner error to write. + /// The local name of the element representing the inner error. + /// The number of times this method has been called recursively. + /// The maximum number of nested inner errors to allow. + private static async Task WriteXmlInnerErrorAsync(XmlWriter writer, ODataInnerError innerError, string innerErrorElementName, int recursionDepth, int maxInnerErrorDepth) + { + Debug.Assert(writer != null, "writer != null"); + + recursionDepth++; + if (recursionDepth > maxInnerErrorDepth) + { +#if ODATA_CORE + throw new ODataException(Strings.ValidationUtils_RecursionDepthLimitReached(maxInnerErrorDepth)); +#else + throw new ODataException(Microsoft.OData.Service.Strings.BadRequest_DeepRecursion(maxInnerErrorDepth)); +#endif + } + + // or + await writer.WriteStartElementAsync(ODataMetadataConstants.ODataMetadataNamespacePrefix, innerErrorElementName, ODataMetadataConstants.ODataMetadataNamespace).ConfigureAwait(false); + + //// NOTE: we add empty elements if no information is provided for the message, error type and stack trace + //// to stay compatible with Astoria. + + // ... + string errorMessage = innerError.Message ?? String.Empty; + await writer.WriteStartElementAsync(null, ODataMetadataConstants.ODataInnerErrorMessageElementName, ODataMetadataConstants.ODataMetadataNamespace).ConfigureAwait(false); + await writer.WriteStringAsync(errorMessage).ConfigureAwait(false); + await writer.WriteEndElementAsync(); + + // ... + string errorType = innerError.TypeName ?? string.Empty; + await writer.WriteStartElementAsync(null, ODataMetadataConstants.ODataInnerErrorTypeElementName, ODataMetadataConstants.ODataMetadataNamespace).ConfigureAwait(false); + await writer.WriteStringAsync(errorType).ConfigureAwait(false); + await writer.WriteEndElementAsync().ConfigureAwait(false); + + // ... + string errorStackTrace = innerError.StackTrace ?? String.Empty; + await writer.WriteStartElementAsync(null, ODataMetadataConstants.ODataInnerErrorStackTraceElementName, ODataMetadataConstants.ODataMetadataNamespace).ConfigureAwait(false); + await writer.WriteStringAsync(errorStackTrace).ConfigureAwait(false); + await writer.WriteEndElementAsync().ConfigureAwait(false); + + if (innerError.InnerError != null) + { + await WriteXmlInnerErrorAsync(writer, innerError.InnerError, ODataMetadataConstants.ODataInnerErrorInnerErrorElementName, recursionDepth, maxInnerErrorDepth).ConfigureAwait(false); + } + + // or + await writer.WriteEndElementAsync().ConfigureAwait(false); + } } -} \ No newline at end of file +} diff --git a/src/Microsoft.OData.Core/Metadata/ODataMetadataWriterUtils.cs b/src/Microsoft.OData.Core/Metadata/ODataMetadataWriterUtils.cs index 068991f9c1..75041084ee 100644 --- a/src/Microsoft.OData.Core/Metadata/ODataMetadataWriterUtils.cs +++ b/src/Microsoft.OData.Core/Metadata/ODataMetadataWriterUtils.cs @@ -10,6 +10,7 @@ namespace Microsoft.OData.Metadata using System.Diagnostics; using System.IO; using System.Text; + using System.Threading.Tasks; using System.Xml; #endregion Namespaces @@ -58,6 +59,18 @@ internal static void WriteError(XmlWriter writer, ODataError error, bool include ErrorUtils.WriteXmlError(writer, error, includeDebugInformation, maxInnerErrorDepth); } + /// + /// Asynchronously Write an error message. + /// + /// The Xml writer to write to. + /// The error instance to write. + /// A flag indicating whether error details should be written (in debug mode only) or not. + /// The maximum number of nested inner errors to allow. + internal static Task WriteErrorAsync(XmlWriter writer, ODataError error, bool includeDebugInformation, int maxInnerErrorDepth) + { + return ErrorUtils.WriteXmlErrorAsync(writer, error, includeDebugInformation, maxInnerErrorDepth); + } + /// /// Creates a new XmlWriterSettings instance using the encoding. /// diff --git a/src/Microsoft.OData.Core/ODataMetadataJsonOutputContext.cs b/src/Microsoft.OData.Core/ODataMetadataJsonOutputContext.cs index 39cb190fd3..9e3322d8fd 100644 --- a/src/Microsoft.OData.Core/ODataMetadataJsonOutputContext.cs +++ b/src/Microsoft.OData.Core/ODataMetadataJsonOutputContext.cs @@ -89,16 +89,12 @@ internal Task FlushAsync() /// /// A task representing the asynchronous operation of writing the metadata document. /// It is the responsibility of this method to flush the output before the task finishes. - internal override Task WriteMetadataDocumentAsync() + internal override async Task WriteMetadataDocumentAsync() { this.AssertAsynchronous(); - return TaskUtils.GetTaskForSynchronousOperationReturningTask( - () => - { - this.WriteMetadataDocumentImplementation(); - return this.FlushAsync(); - }); + await this.WriteMetadataDocumentImplementationAsync().ConfigureAwait(false); + await this.FlushAsync().ConfigureAwait(false); } /// @@ -259,6 +255,28 @@ private void WriteMetadataDocumentImplementation() } } + private async Task WriteMetadataDocumentImplementationAsync() + { + var writerSettings = new CsdlJsonWriterSettings + { + IsIeee754Compatible = MessageWriterSettings.IsIeee754Compatible, + }; + + var (success, errors) = await CsdlWriter.TryWriteCsdlAsync(this.Model, this.jsonWriter, writerSettings).ConfigureAwait(false); + if (!success) + { + Debug.Assert(errors != null, "errors != null"); + + StringBuilder builder = new StringBuilder(); + foreach (EdmError error in errors) + { + builder.AppendLine(error.ToString()); + } + + throw new ODataException(Strings.ODataMetadataOutputContext_ErrorWritingMetadata(builder.ToString())); + } + } + private async Task DisposeOutputStreamAsync() { await this.asynchronousOutputStream.FlushAsync().ConfigureAwait(false); diff --git a/src/Microsoft.OData.Core/ODataMetadataOutputContext.cs b/src/Microsoft.OData.Core/ODataMetadataOutputContext.cs index ad554f6cb9..99b3b7d4f2 100644 --- a/src/Microsoft.OData.Core/ODataMetadataOutputContext.cs +++ b/src/Microsoft.OData.Core/ODataMetadataOutputContext.cs @@ -100,16 +100,13 @@ internal Task FlushAsync() /// be included in the payload. This should only be used in debug scenarios. /// /// Task which represents the pending write operation. - internal override Task WriteInStreamErrorAsync(ODataError error, bool includeDebugInformation) + internal override async Task WriteInStreamErrorAsync(ODataError error, bool includeDebugInformation) { this.AssertAsynchronous(); - return TaskUtils.GetTaskForSynchronousOperationReturningTask( - () => - { - ODataMetadataWriterUtils.WriteError(this.xmlWriter, error, includeDebugInformation, this.MessageWriterSettings.MessageQuotas.MaxNestingDepth); - return this.FlushAsync(); - }); + await ODataMetadataWriterUtils.WriteErrorAsync(this.xmlWriter, error, includeDebugInformation, this.MessageWriterSettings.MessageQuotas.MaxNestingDepth).ConfigureAwait(false); + + await this.FlushAsync().ConfigureAwait(false); } /// @@ -117,16 +114,12 @@ internal override Task WriteInStreamErrorAsync(ODataError error, bool includeDeb /// /// A task representing the asynchronous operation of writing the metadata document. /// It is the responsibility of this method to flush the output before the task finishes. - internal override Task WriteMetadataDocumentAsync() + internal override async Task WriteMetadataDocumentAsync() { this.AssertAsynchronous(); - return TaskUtils.GetTaskForSynchronousOperationReturningTask( - () => - { - this.WriteMetadataDocumentImplementation(); - return this.FlushAsync(); - }); + await this.WriteMetadataDocumentImplementationAsync().ConfigureAwait(false); + await this.FlushAsync().ConfigureAwait(false); } /// @@ -280,5 +273,26 @@ private void WriteMetadataDocumentImplementation() throw new ODataException(Strings.ODataMetadataOutputContext_ErrorWritingMetadata(builder.ToString())); } } + + private async Task WriteMetadataDocumentImplementationAsync() + { + Tuple> result = await CsdlWriter.TryWriteCsdlAsync(this.Model, this.xmlWriter, CsdlTarget.OData).ConfigureAwait(false); + bool success = result.Item1; + + if (!success) + { + IEnumerable errors = result.Item2; + + Debug.Assert(errors != null, "errors != null"); + + StringBuilder builder = new StringBuilder(); + foreach (EdmError error in errors) + { + builder.AppendLine(error.ToString()); + } + + throw new ODataException(Strings.ODataMetadataOutputContext_ErrorWritingMetadata(builder.ToString())); + } + } } } diff --git a/src/Microsoft.OData.Edm/Csdl/CsdlJsonWriter.cs b/src/Microsoft.OData.Edm/Csdl/CsdlJsonWriter.cs index 99ceec418c..096bc08063 100644 --- a/src/Microsoft.OData.Edm/Csdl/CsdlJsonWriter.cs +++ b/src/Microsoft.OData.Edm/Csdl/CsdlJsonWriter.cs @@ -9,6 +9,7 @@ using System.Collections.Generic; using System.Linq; using System.Text.Json; +using System.Threading.Tasks; using Microsoft.OData.Edm.Csdl.Serialization; namespace Microsoft.OData.Edm.Csdl @@ -39,7 +40,7 @@ public CsdlJsonWriter(IEdmModel model, Utf8JsonWriter jsonWriter, CsdlJsonWriter } /// - /// Write the JSON CSDL. + /// Writes the JSON CSDL. /// protected override void WriteCsdl() { @@ -49,13 +50,30 @@ protected override void WriteCsdl() WriteReferenceElements(); // It also MAY contain members for schemas. - WriteSchemata(); + WriteSchemas(); WriteCsdlEnd(); } /// - /// CSDL JSON Document Object + /// Asynchronously Writes the JSON CSDL. + /// + /// Task that represents the asynchronous operation. + protected override async Task WriteCsdlAsync() + { + await WriteCsdlStartAsync().ConfigureAwait(false); + + // The CSDL JSON Document object MAY contain the member $Reference to reference other CSDL documents. + await WriteReferenceElementsAsync().ConfigureAwait(false); + + // It also MAY contain members for schemas. + await WriteSchemasAsync().ConfigureAwait(false); + + await WriteCsdlEndAsync().ConfigureAwait(false); + } + + /// + /// Writes CSDL JSON Document Object /// private void WriteCsdlStart() { @@ -73,7 +91,28 @@ private void WriteCsdlStart() } /// - /// $Reference Object + /// Asynchronously Writes CSDL JSON Document Object + /// + /// A task that represents the asynchronous operation + private Task WriteCsdlStartAsync() + { + // A CSDL JSON document consists of a single JSON object. + this.jsonWriter.WriteStartObject(); + + // This document object MUST contain the member $Version. + this.jsonWriter.WriteRequiredProperty("$Version", GetVersionString(edmxVersion)); + + // If the CSDL JSON document is the metadata document of an OData service, the document object MUST contain the member $EntityContainer. + if (model.EntityContainer != null) + { + this.jsonWriter.WriteRequiredProperty("$EntityContainer", model.EntityContainer.FullName()); + } + + return Task.CompletedTask; + } + + /// + /// Writes $Reference Object /// private void WriteReferenceElements() { @@ -84,9 +123,20 @@ EdmModelReferenceElementsJsonVisitor visitor } /// - /// Schema Object. + /// Asynchronously writes $Reference Object. /// - private void WriteSchemata() + /// A task that represents the asynchronous operation + private Task WriteReferenceElementsAsync() + { + var visitor = new EdmModelReferenceElementsJsonVisitor(this.model, this.jsonWriter, this.edmxVersion); + + return visitor.VisitEdmReferencesAsync(this.model); + } + + /// + /// Writes Schema Object. + /// + private void WriteSchemas() { // A schema is represented as a member of the document object whose name is the schema namespace. // Its value is an object that MAY contain the members $Alias and $Annotations. @@ -100,6 +150,24 @@ private void WriteSchemata() } } + /// + /// Asynchronously Writes Schema Object + /// + /// A task that represents the asynchronous operation + private async Task WriteSchemasAsync() + { + // A schema is represented as a member of the document object whose name is the schema namespace. + // Its value is an object that MAY contain the members $Alias and $Annotations. + EdmModelCsdlSerializationVisitor visitor; + Version edmVersion = this.model.GetEdmVersion() ?? EdmConstants.EdmVersionLatest; + foreach (EdmSchema schema in this.schemas) + { + EdmModelCsdlSchemaWriter writer = new EdmModelCsdlSchemaJsonWriter(model, jsonWriter, edmVersion, settings); + visitor = new EdmModelCsdlSerializationVisitor(this.model, writer); + await visitor.VisitEdmSchemaAsync(schema, this.model.GetNamespacePrefixMappings()).ConfigureAwait(false); + } + } + private void WriteCsdlEnd() { // End of the CSDL JSON document. @@ -107,6 +175,18 @@ private void WriteCsdlEnd() this.jsonWriter.Flush(); } + + /// + /// Write the end of the CSDL JSON document asynchronously. + /// + /// A task that flushing the JSON writer (jsonWriter) asynchronously. + private Task WriteCsdlEndAsync() + { + // End of the CSDL JSON document. + this.jsonWriter.WriteEndObject(); + + return this.jsonWriter.FlushAsync(); + } } } #endif \ No newline at end of file diff --git a/src/Microsoft.OData.Edm/Csdl/CsdlWriter.cs b/src/Microsoft.OData.Edm/Csdl/CsdlWriter.cs index 466bdcf8fd..08f6716dcb 100644 --- a/src/Microsoft.OData.Edm/Csdl/CsdlWriter.cs +++ b/src/Microsoft.OData.Edm/Csdl/CsdlWriter.cs @@ -11,6 +11,7 @@ #if NETSTANDARD2_0 using System.Text.Json; #endif +using System.Threading.Tasks; using System.Xml; using Microsoft.OData.Edm.Csdl.Serialization; @@ -55,6 +56,17 @@ public static bool TryWriteCsdl(IEdmModel model, Utf8JsonWriter writer, out IEnu return TryWriteCsdl(model, writer, CsdlJsonWriterSettings.Default, out errors); } + /// + /// Asynchronously Outputs a CSDL JSON artifact to the provided . + /// + /// The Edm model to be written. + /// JSON writer the generated CSDL will be written to. + /// A Task with a tuple with a value indicating whether serialization was successful and EdmError if any + public static Task<(bool, IEnumerable)> TryWriteCsdlAsync(IEdmModel model, Utf8JsonWriter writer) + { + return TryWriteCsdlAsync(model, writer, CsdlJsonWriterSettings.Default); + } + /// /// Outputs a CSDL JSON artifact to the provided using the settings. /// @@ -81,6 +93,31 @@ public static bool TryWriteCsdl(IEdmModel model, Utf8JsonWriter writer, CsdlJson errors = Enumerable.Empty(); return true; } + + /// + /// Asynchronously Outputs a CSDL JSON artifact to the provided using the settings. + /// + /// The Edm model to be written. + /// JSON writer the generated CSDL will be written to. + /// The CSDL writer settings. + /// A Task with tuple with a value indicating whether serialization was successful and EdmError if any + public static async Task<(bool, IEnumerable)> TryWriteCsdlAsync(IEdmModel model, Utf8JsonWriter writer, CsdlJsonWriterSettings settings) + { + EdmUtil.CheckArgumentNull(model, nameof(model)); + EdmUtil.CheckArgumentNull(writer, nameof(writer)); + EdmUtil.CheckArgumentNull(settings, nameof(settings)); + + Version edmxVersion; + if (!VerifyAndGetVersion(model, out edmxVersion, out IEnumerable errors)) + { + return (false, errors); + } + + CsdlWriter csdlWriter = new CsdlJsonWriter(model, writer, settings, edmxVersion); + await csdlWriter.WriteCsdlAsync().ConfigureAwait(false); + + return (true, Enumerable.Empty()); + } #endif /// @@ -109,6 +146,30 @@ public static bool TryWriteCsdl(IEdmModel model, XmlWriter writer, CsdlTarget ta return true; } + /// + /// Asynchronously Outputs a CSDL XML artifact to the provided . + /// + /// Model to be written. + /// XmlWriter the generated CSDL will be written to. + /// Target implementation of the CSDL being generated. + /// A task with a value indicating whether serialization was successful. + public static async Task>> TryWriteCsdlAsync(IEdmModel model, XmlWriter writer, CsdlTarget target) + { + EdmUtil.CheckArgumentNull(model, "model"); + EdmUtil.CheckArgumentNull(writer, "writer"); + + Version edmxVersion; + if (!VerifyAndGetVersion(model, out edmxVersion, out IEnumerable errors)) + { + return Tuple.Create(false, errors); + } + + CsdlWriter csdlWriter = new CsdlXmlWriter(model, writer, edmxVersion, target); + await csdlWriter.WriteCsdlAsync().ConfigureAwait(false); + + return Tuple.Create(true, Enumerable.Empty()); + } + /// /// Write CSDL output. /// @@ -117,6 +178,15 @@ protected virtual void WriteCsdl() // nothing here } + /// + /// Asynchronously Writes CSDL output. + /// + /// Task represents an asynchronous operation that may or may not return a result. + protected virtual Task WriteCsdlAsync() + { + return Task.FromResult(0); + } + /// /// Gets the string form of the EdmVersion. /// Note that Version 4.01 needs two digits of minor version precision. diff --git a/src/Microsoft.OData.Edm/Csdl/CsdlXmlWriter.cs b/src/Microsoft.OData.Edm/Csdl/CsdlXmlWriter.cs index abf5026c2a..c09a6180a2 100644 --- a/src/Microsoft.OData.Edm/Csdl/CsdlXmlWriter.cs +++ b/src/Microsoft.OData.Edm/Csdl/CsdlXmlWriter.cs @@ -7,6 +7,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Threading.Tasks; using System.Xml; using Microsoft.OData.Edm.Csdl.Serialization; @@ -58,6 +59,25 @@ protected override void WriteCsdl() } } + /// + /// Asynchronously write the CSDL XML. + /// + /// + protected override async Task WriteCsdlAsync() + { + switch (this.target) + { + case CsdlTarget.EntityFramework: + await this.WriteEFCsdlAsync().ConfigureAwait(false); + break; + case CsdlTarget.OData: + await this.WriteODataCsdlAsync().ConfigureAwait(false); + break; + default: + throw new InvalidOperationException(Strings.UnknownEnumVal_CsdlTarget(this.target.ToString())); + } + } + private void WriteODataCsdl() { this.WriteEdmxElement(); @@ -69,6 +89,17 @@ private void WriteODataCsdl() this.writer.Flush(); } + private async Task WriteODataCsdlAsync() + { + await this.WriteEdmxElementAsync().ConfigureAwait(false); + await this.WriteReferenceElementsAsync().ConfigureAwait(false); + await this.WriteDataServicesElementAsync().ConfigureAwait(false); + await this.WriteSchemasAsync().ConfigureAwait(false); + await this.EndElementAsync().ConfigureAwait(false); // + await this.EndElementAsync().ConfigureAwait(false); // + await this.writer.FlushAsync().ConfigureAwait(false); + } + private void WriteEFCsdl() { this.WriteEdmxElement(); @@ -81,22 +112,54 @@ private void WriteEFCsdl() this.writer.Flush(); } + private async Task WriteEFCsdlAsync() + { + await this.WriteEdmxElementAsync().ConfigureAwait(false); + await this.WriteRuntimeElementAsync().ConfigureAwait(false); + await this.WriteConceptualModelsElementAsync().ConfigureAwait(false); + await this.WriteSchemasAsync().ConfigureAwait(false); + await this.EndElementAsync().ConfigureAwait(false); // + await this.EndElementAsync().ConfigureAwait(false); // + await this.EndElementAsync().ConfigureAwait(false); // + await this.writer.FlushAsync().ConfigureAwait(false); + } + private void WriteEdmxElement() { this.writer.WriteStartElement(CsdlConstants.Prefix_Edmx, CsdlConstants.Element_Edmx, this.edmxNamespace); this.writer.WriteAttributeString(CsdlConstants.Attribute_Version, GetVersionString(this.edmxVersion)); } + private async Task WriteEdmxElementAsync() + { + await this.writer.WriteStartElementAsync(CsdlConstants.Prefix_Edmx, CsdlConstants.Element_Edmx, this.edmxNamespace).ConfigureAwait(false); + await this.writer.WriteAttributeStringAsync(null, CsdlConstants.Attribute_Version, null, GetVersionString(this.edmxVersion)).ConfigureAwait(false); + } + private void WriteRuntimeElement() { this.writer.WriteStartElement(CsdlConstants.Prefix_Edmx, CsdlConstants.Element_Runtime, this.edmxNamespace); } + private Task WriteRuntimeElementAsync() + { + return this.writer.WriteStartElementAsync(CsdlConstants.Prefix_Edmx, CsdlConstants.Element_Runtime, this.edmxNamespace); + } + private void WriteConceptualModelsElement() { this.writer.WriteStartElement(CsdlConstants.Prefix_Edmx, CsdlConstants.Element_ConceptualModels, this.edmxNamespace); } + /// + /// Asynchronously write the ConceptualModels element. + /// + /// The task represents the asynchronous operation. + private Task WriteConceptualModelsElementAsync() + { + return this.writer.WriteStartElementAsync(CsdlConstants.Prefix_Edmx, CsdlConstants.Element_ConceptualModels, this.edmxNamespace); + } + private void WriteReferenceElements() { EdmModelReferenceElementsXmlVisitor visitor; @@ -126,11 +189,45 @@ private void WriteReferenceElements() } } + private async Task WriteReferenceElementsAsync() + { + EdmModelReferenceElementsXmlVisitor visitor; + IEnumerable references = model.GetEdmReferences(); + if (references != null) + { + foreach (IEdmReference reference in references) + { + //loop through the includes and set the namespace alias + if (reference.Includes != null) + { + foreach (IEdmInclude include in reference.Includes) + { + if (include.Alias != null) + { + model.SetNamespaceAlias(include.Namespace, include.Alias); + } + } + } + } + + foreach (IEdmReference edmReference in references) + { + visitor = new EdmModelReferenceElementsXmlVisitor(this.model, this.writer, this.edmxVersion); + await visitor.VisitEdmReferencesAsync(this.model, edmReference).ConfigureAwait(false); + } + } + } + private void WriteDataServicesElement() { this.writer.WriteStartElement(CsdlConstants.Prefix_Edmx, CsdlConstants.Element_DataServices, this.edmxNamespace); } + private Task WriteDataServicesElementAsync() + { + return this.writer.WriteStartElementAsync(CsdlConstants.Prefix_Edmx, CsdlConstants.Element_DataServices, this.edmxNamespace); + } + private void WriteSchemas() { // TODO: for referenced model - write alias as is, instead of writing its namespace. @@ -144,9 +241,27 @@ private void WriteSchemas() } } + private async Task WriteSchemasAsync() + { + // TODO: for referenced model - write alias as is, instead of writing its namespace. + EdmModelCsdlSerializationVisitor visitor; + Version edmVersion = this.model.GetEdmVersion() ?? EdmConstants.EdmVersionLatest; + foreach (EdmSchema schema in this.schemas) + { + EdmModelCsdlSchemaXmlWriter schemaWriter = new EdmModelCsdlSchemaXmlWriter(model, this.writer, edmVersion); + visitor = new EdmModelCsdlSerializationVisitor(this.model, schemaWriter); + await visitor.VisitEdmSchemaAsync(schema, this.model.GetNamespacePrefixMappings()).ConfigureAwait(false); + } + } + private void EndElement() { this.writer.WriteEndElement(); } + + private Task EndElementAsync() + { + return this.writer.WriteEndElementAsync(); + } } } diff --git a/src/Microsoft.OData.Edm/Csdl/SchemaWriter.cs b/src/Microsoft.OData.Edm/Csdl/SchemaWriter.cs index 64421d47ad..31f2f8af86 100644 --- a/src/Microsoft.OData.Edm/Csdl/SchemaWriter.cs +++ b/src/Microsoft.OData.Edm/Csdl/SchemaWriter.cs @@ -7,6 +7,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; using System.Xml; using Microsoft.OData.Edm.Csdl.Serialization; using Microsoft.OData.Edm.Validation; @@ -30,6 +31,17 @@ public static bool TryWriteSchema(this IEdmModel model, XmlWriter writer, out IE return TryWriteSchema(model, x => writer, true, out errors); } + /// + /// Asynchronously Outputs a Schema artifact to the provided writer. + /// + /// Model to be written. + /// XmlWriter the generated Schema will be written to. + /// A task represents a Tuple with value indicating whether serialization was successful and Errors that prevented successful serialization, or no errors if serialization was successful. + public static Task>> TryWriteSchemaAsync(this IEdmModel model, XmlWriter writer) + { + return TryWriteSchemaAsync(model, x => writer, true); + } + /// /// Outputs Schema artifacts to the provided writers. /// @@ -42,6 +54,17 @@ public static bool TryWriteSchema(this IEdmModel model, Func return TryWriteSchema(model, writerProvider, false, out errors); } + /// + /// Asynchronously Outputs Schema artifacts to the provided writers. + /// + /// Model to be written. + /// A delegate that takes in a Schema namespace name and returns an XmlWriter to write the Schema to. + /// A task represents a Tuple with value indicating whether serialization was successful and Errors that prevented successful serialization, or no errors if serialization was successful. + public static Task>> TryWriteSchemaAsync(this IEdmModel model, Func writerProvider) + { + return TryWriteSchemaAsync(model, writerProvider, false); + } + internal static bool TryWriteSchema(IEdmModel model, Func writerProvider, bool singleFileExpected, out IEnumerable errors) { EdmUtil.CheckArgumentNull(model, "model"); @@ -72,6 +95,36 @@ internal static bool TryWriteSchema(IEdmModel model, Func wri return true; } + internal static async Task>> TryWriteSchemaAsync(IEdmModel model, Func writerProvider, bool singleFileExpected) + { + EdmUtil.CheckArgumentNull(model, "model"); + EdmUtil.CheckArgumentNull(writerProvider, "writerProvider"); + + IEnumerable errors = model.GetSerializationErrors(); + if (errors.FirstOrDefault() != null) + { + return Tuple.Create(false, errors); + } + + IEnumerable schemas = new EdmModelSchemaSeparationSerializationVisitor(model).GetSchemas(); + if (schemas.Count() > 1 && singleFileExpected) + { + errors = new EdmError[] { new EdmError(new CsdlLocation(0, 0), EdmErrorCode.SingleFileExpected, Edm.Strings.Serializer_SingleFileExpected) }; + return Tuple.Create(false, errors); + } + + if (!schemas.Any()) + { + errors = new EdmError[] { new EdmError(new CsdlLocation(0, 0), EdmErrorCode.NoSchemasProduced, Edm.Strings.Serializer_NoSchemasProduced) }; + return Tuple.Create(false, errors); + } + + await WriteSchemasAsync(model, schemas, writerProvider).ConfigureAwait(false); + + errors = Enumerable.Empty(); + return Tuple.Create(true, errors); + } + internal static void WriteSchemas(IEdmModel model, IEnumerable schemas, Func writerProvider) { EdmModelCsdlSerializationVisitor visitor; @@ -87,5 +140,21 @@ internal static void WriteSchemas(IEdmModel model, IEnumerable schema } } } + + internal static async Task WriteSchemasAsync(IEdmModel model, IEnumerable schemas, Func writerProvider) + { + EdmModelCsdlSerializationVisitor visitor; + Version edmVersion = model.GetEdmVersion() ?? EdmConstants.EdmVersionDefault; + foreach (EdmSchema schema in schemas) + { + XmlWriter writer = writerProvider(schema.Namespace); + if (writer != null) + { + EdmModelCsdlSchemaXmlWriter schemaWriter = new EdmModelCsdlSchemaXmlWriter(model, writer, edmVersion); + visitor = new EdmModelCsdlSerializationVisitor(model, schemaWriter); + await visitor.VisitEdmSchemaAsync(schema, model.GetNamespacePrefixMappings()).ConfigureAwait(false); + } + } + } } } diff --git a/src/Microsoft.OData.Edm/Csdl/Serialization/EdmModelCsdlSchemaJsonWriter.cs b/src/Microsoft.OData.Edm/Csdl/Serialization/EdmModelCsdlSchemaJsonWriter.cs index 5944ae5749..6b72c7fa8b 100644 --- a/src/Microsoft.OData.Edm/Csdl/Serialization/EdmModelCsdlSchemaJsonWriter.cs +++ b/src/Microsoft.OData.Edm/Csdl/Serialization/EdmModelCsdlSchemaJsonWriter.cs @@ -12,6 +12,7 @@ using System.Linq; using System.Text; using System.Text.Json; +using System.Threading.Tasks; using Microsoft.OData.Edm.Csdl.CsdlSemantics; using Microsoft.OData.Edm.Csdl.Parsing.Ast; using Microsoft.OData.Edm.Vocabularies; @@ -56,6 +57,10 @@ internal EdmModelCsdlSchemaJsonWriter(IEdmModel model, Utf8JsonWriter writer, Ve this.settings = settings; } + /// + /// Writes Reference Element Header. + /// + /// The Edm Reference. internal override void WriteReferenceElementHeader(IEdmReference reference) { // The name of the pair is a URI for the referenced document. @@ -65,11 +70,47 @@ internal override void WriteReferenceElementHeader(IEdmReference reference) this.jsonWriter.WriteStartObject(); } + /// + /// Asynchronously Writes Reference Element Header. + /// + /// The Edm Reference. + /// Task represents an asynchronous operation that may or may not return a result. + internal override Task WriteReferenceElementHeaderAsync(IEdmReference reference) + { + // The name of the pair is a URI for the referenced document. + this.jsonWriter.WritePropertyName(reference.Uri.OriginalString); + + // The value of each member is a reference object. + this.jsonWriter.WriteStartObject(); + + return Task.CompletedTask; + } + + /// + /// Writes Reference Element End. + /// + /// The Edm Reference element. internal override void WriteReferenceElementEnd(IEdmReference reference) { this.jsonWriter.WriteEndObject(); } + /// + /// Asynchronously Writes Reference Element End. + /// + /// The Edm Reference element. + /// Task represents an asynchronous operation that may or may not return a result. + internal override Task WriteReferenceElementEndAsync(IEdmReference reference) + { + this.jsonWriter.WriteEndObject(); + + return Task.CompletedTask; + } + + /// + /// Writes Include Element Header. + /// + /// The Edm Include information. internal override void WritIncludeElementHeader(IEdmInclude include) { // Array items are objects. @@ -82,11 +123,46 @@ internal override void WritIncludeElementHeader(IEdmInclude include) this.jsonWriter.WriteOptionalProperty("$Alias", include.Alias); } + /// + /// Asynchronously Writes Include Element Header. + /// + /// The Edm Include information. + /// Task represents an asynchronous operation that may or may not return a result. + internal override Task WritIncludeElementHeaderAsync(IEdmInclude include) + { + // Array items are objects. + this.jsonWriter.WriteStartObject(); + + // MUST contain the member $Namespace + this.jsonWriter.WriteRequiredProperty("$Namespace", include.Namespace); + + // MAY contain the member $Alias + this.jsonWriter.WriteOptionalProperty("$Alias", include.Alias); + + return Task.CompletedTask; + } + + /// + /// Writes Include Element End. + /// + /// The Edm Include information. internal override void WriteIncludeElementEnd(IEdmInclude include) { this.jsonWriter.WriteEndObject(); } + /// + /// Asynchronously Writes Include Element End. + /// + /// The Edm Include information. + /// Task representing asynchronous + internal override Task WriteIncludeElementEndAsync(IEdmInclude include) + { + this.jsonWriter.WriteEndObject(); + + return Task.CompletedTask; + } + /// /// Write Term Object header /// @@ -135,6 +211,51 @@ internal override void WriteTermElementHeader(IEdmTerm term, bool inlineType) // These members are processed in ProcessFacets(). } + /// + /// Asynchronously Writes Term Object header. + /// + /// The Edm Term + /// Is inline type or not. + internal override async Task WriteTermElementHeaderAsync(IEdmTerm term, bool inlineType) + { + // A term is represented as a member of the schema object whose name is the unqualified name of the term. + this.jsonWriter.WritePropertyName(term.Name); + + // whose value is an object. + this.jsonWriter.WriteStartObject(); + + // The term object MUST contain the member $Kind with a string value of Term. + this.jsonWriter.WriteRequiredProperty("$Kind", CsdlConstants.Element_Term); + + // It MAY contain the members $Type, $Collection. + if (inlineType && term.Type != null) + { + await WriteTypeReferenceAsync(term.Type).ConfigureAwait(false); + } + + // A term MAY specialize another term in scope by specifying it as its base term. + // The value of $BaseTerm is the qualified name of the base term. So far, it's not supported. + + // It MAY contain the members $AppliesTo. + // The value of $AppliesTo is an array whose items are strings containing symbolic values from a table + // that identify model elements the term is intended to be applied to. + if (term.AppliesTo != null) + { + string[] appliesTo = term.AppliesTo.Split(','); + this.jsonWriter.WritePropertyName("$AppliesTo"); + this.jsonWriter.WriteStartArray(); + foreach (var applyTo in appliesTo) + { + this.jsonWriter.WriteStringValue(applyTo); + } + this.jsonWriter.WriteEndArray(); + } + + // It MAY contain the members $DefaultValue. + // The value of $DefaultValue is the type-specific JSON representation of the default value of the term + this.jsonWriter.WriteOptionalProperty("$DefaultValue", term.DefaultValue); + } + /// /// Write Entity Type object /// @@ -163,6 +284,37 @@ internal override void WriteEntityTypeElementHeader(IEdmEntityType entityType) this.jsonWriter.WriteOptionalProperty("$HasStream", entityType.HasStream, CsdlConstants.Default_HasStream); } + /// + /// Asynchronously Writes Entity Type object. + /// + /// The Edm entity type. + /// Task represents an asynchronous operation that may or may not return a result. + internal override Task WriteEntityTypeElementHeaderAsync(IEdmEntityType entityType) + { + // An entity type is represented as a member of the schema object whose name is the unqualified name of the entity type + this.jsonWriter.WritePropertyName(entityType.Name); + + // whose value is an object + this.jsonWriter.WriteStartObject(); + + // The entity type object MUST contain the member $Kind with a string value of EntityType. + this.jsonWriter.WriteRequiredProperty("$Kind", CsdlConstants.Element_EntityType); + + // It MAY contain the members $BaseType + this.jsonWriter.WriteOptionalProperty("$BaseType", entityType.BaseEntityType(), this.TypeDefinitionAsJson); + + // It MAY contain the members $Abstract, The value of $Abstract is one of the Boolean literals true or false. Absence of the member means false. + this.jsonWriter.WriteOptionalProperty("$Abstract", entityType.IsAbstract, CsdlConstants.Default_Abstract); + + // It MAY contain the members $OpenType, The value of $OpenType is one of the Boolean literals true or false. Absence of the member means false. + this.jsonWriter.WriteOptionalProperty("$OpenType", entityType.IsOpen, CsdlConstants.Default_OpenType); + + // It MAY contain the members $HasStream, The value of $HasStream is one of the Boolean literals true or false. Absence of the member means false + this.jsonWriter.WriteOptionalProperty("$HasStream", entityType.HasStream, CsdlConstants.Default_HasStream); + + return Task.CompletedTask; + } + /// /// Write Complex Type Object /// @@ -188,6 +340,34 @@ internal override void WriteComplexTypeElementHeader(IEdmComplexType complexType this.jsonWriter.WriteOptionalProperty("$OpenType", complexType.IsOpen, CsdlConstants.Default_OpenType); } + /// + /// Asynchronously Writes Complex Type Object. + /// + /// The Edm complex type. + /// Task represents an asynchronous operation that may or may not return a result. + internal override Task WriteComplexTypeElementHeaderAsync(IEdmComplexType complexType) + { + // A complex type is represented as a member of the schema object whose name is the unqualified name of the complex type + this.jsonWriter.WritePropertyName(complexType.Name); + + // whose value is an object + this.jsonWriter.WriteStartObject(); + + // The complex type object MUST contain the member $Kind with a string value of ComplexType. + this.jsonWriter.WriteRequiredProperty("$Kind", CsdlConstants.Element_ComplexType); + + // It MAY contain the members $BaseType + this.jsonWriter.WriteOptionalProperty("$BaseType", complexType.BaseComplexType(), this.TypeDefinitionAsJson); + + // It MAY contain the members $Abstract, The value of $Abstract is one of the Boolean literals true or false. Absence of the member means false. + this.jsonWriter.WriteOptionalProperty("$Abstract", complexType.IsAbstract, CsdlConstants.Default_Abstract); + + // It MAY contain the members $OpenType, The value of $OpenType is one of the Boolean literals true or false. Absence of the member means false. + this.jsonWriter.WriteOptionalProperty("$OpenType", complexType.IsOpen, CsdlConstants.Default_OpenType); + + return Task.CompletedTask; + } + /// /// Write $Key /// @@ -200,10 +380,25 @@ internal override void WriteDeclaredKeyPropertiesElementHeader() this.jsonWriter.WriteStartArray(); } + /// + /// Asynchronously Writes $Key. + /// + /// Task represents an asynchronous operation that may or may not return a result. + internal override Task WriteDeclaredKeyPropertiesElementHeaderAsync() + { + // The value of $Key is an array with one item per key property. + this.jsonWriter.WritePropertyName("$Key"); + + // Its value is an array. + this.jsonWriter.WriteStartArray(); + + return Task.CompletedTask; + } + /// /// Write the Key property /// - /// + /// The Edm Structural Property. internal override void WritePropertyRefElement(IEdmStructuralProperty property) { // Key properties without a key alias are represented as strings containing the property name. @@ -212,6 +407,20 @@ internal override void WritePropertyRefElement(IEdmStructuralProperty property) this.jsonWriter.WriteStringValue(property.Name); } + /// + /// Asynchronously Writes the Key property. + /// + /// The Edm Structural Property. + /// Task represents an asynchronous operation that may or may not return a result. + internal override Task WritePropertyRefElementAsync(IEdmStructuralProperty property) + { + // Key properties without a key alias are represented as strings containing the property name. + // Key properties with a key alias are represented as objects with one member whose name is the key alias and whose value is a string containing the path to the property. + this.jsonWriter.WriteStringValue(property.Name); + + return Task.CompletedTask; + } + /// /// Write Navigation Property Object /// @@ -248,6 +457,47 @@ internal override void WriteNavigationPropertyElementHeader(IEdmNavigationProper this.jsonWriter.WriteOptionalProperty("$ContainsTarget", property.ContainsTarget, CsdlConstants.Default_ContainsTarget); } + /// + /// Asynchronously Writes Navigation Property Object. + /// + /// The Edm navigation property. + /// Task represents an asynchronous operation. + internal override async Task WriteNavigationPropertyElementHeaderAsync(IEdmNavigationProperty property) + { + // Navigation properties are represented as members of the object representing a structured type. The member name is the property name. + this.jsonWriter.WritePropertyName(property.Name); + + // the member value is an object + this.jsonWriter.WriteStartObject(); + + // The navigation property object MUST contain the member $Kind with a string value of NavigationProperty. + this.jsonWriter.WriteRequiredProperty("$Kind", CsdlConstants.Element_NavigationProperty); + + // It MUST contain the member $Type (because the navigation property type never be Edm.String) + // It MAY contain the members $Collection. + await WriteTypeReferenceAsync(property.Type).ConfigureAwait(false); + + // It MAY contain the members $Partner. + // A navigation property of an entity type MAY specify a partner navigation property. Navigation properties of complex types MUST NOT specify a partner. + // So far, it doesn't support to set the path. + if (property.Partner != null) + { + IEdmPathExpression pathExpression = property.GetPartnerPath(); + if (pathExpression != null) + { + this.jsonWriter.WriteRequiredProperty("$Partner", property.GetPartnerPath().Path); + } + } + + // It MAY contain the members $Collection, $Nullable, $Partner, $ContainsTarget + // The value of $ContainsTarget is one of the Boolean literals true or false. Absence of the member means false. + this.jsonWriter.WriteOptionalProperty("$ContainsTarget", property.ContainsTarget, CsdlConstants.Default_ContainsTarget); + } + + /// + /// Writes Referential Constraint Begin. + /// + /// The Edm Referential Constraint. internal override void WriteReferentialConstraintBegin(IEdmReferentialConstraint referentialConstraint) { // The value of $ReferentialConstraint is an object with one member per referential constraint. @@ -256,6 +506,25 @@ internal override void WriteReferentialConstraintBegin(IEdmReferentialConstraint this.jsonWriter.WriteStartObject(); } + /// + /// Asynchronously Writes Referential Constraint Begin. + /// + /// The Edm Referential Constraint. + /// Task represents an asynchronous operation that may or may not return a result. + internal override Task WriteReferentialConstraintBeginAsync(IEdmReferentialConstraint referentialConstraint) + { + // The value of $ReferentialConstraint is an object with one member per referential constraint. + this.jsonWriter.WritePropertyName("$ReferentialConstraint"); + + this.jsonWriter.WriteStartObject(); + + return Task.CompletedTask; + } + + /// + /// Writes Referential Constraint End. + /// + /// The Edm Referential Constraint. internal override void WriteReferentialConstraintEnd(IEdmReferentialConstraint referentialConstraint) { // It also MAY contain annotations. These are prefixed with the path of the dependent property of the annotated referential constraint. @@ -264,6 +533,25 @@ internal override void WriteReferentialConstraintEnd(IEdmReferentialConstraint r this.jsonWriter.WriteEndObject(); } + /// + /// Asynchronously Writes Referential Constraint End. + /// + /// The Edm Referential Constraint. + /// Task represents an asynchronous operation that may or may not return a result. + internal override Task WriteReferentialConstraintEndAsync(IEdmReferentialConstraint referentialConstraint) + { + // It also MAY contain annotations. These are prefixed with the path of the dependent property of the annotated referential constraint. + // So far, it's not supported. + + this.jsonWriter.WriteEndObject(); + + return Task.CompletedTask; + } + + /// + /// Writes Referential Constraint Pair. + /// + /// The Edm Referential Constraint Property Pair. internal override void WriteReferentialConstraintPair(EdmReferentialConstraintPropertyPair pair) { // One member per referential constraint @@ -274,6 +562,27 @@ internal override void WriteReferentialConstraintPair(EdmReferentialConstraintPr this.jsonWriter.WriteStringValue(pair.PrincipalProperty.Name); // It should be the path, so far it's not supported. } + /// + /// Asynchronously Writes Referential Constraint Pair. + /// + /// The Edm Referential Constraint Property Pair. + /// Task represents an asynchronous operation that may or may not return a result. + internal override Task WriteReferentialConstraintPairAsync(EdmReferentialConstraintPropertyPair pair) + { + // One member per referential constraint + // The member name is the path to the dependent property, this path is relative to the structured type declaring the navigation property. + this.jsonWriter.WritePropertyName(pair.DependentProperty.Name); // It should be the path, so far, it's not supported. + + // The member value is a string containing the path to the principal property, + this.jsonWriter.WriteStringValue(pair.PrincipalProperty.Name); // It should be the path, so far it's not supported. + + return Task.CompletedTask; + } + + /// + /// Writes Navigation OnDelete Action Element. + /// + /// The Edm OnDelete Action. internal override void WriteNavigationOnDeleteActionElement(EdmOnDeleteAction operationAction) { // $OnDelete @@ -285,6 +594,24 @@ internal override void WriteNavigationOnDeleteActionElement(EdmOnDeleteAction op // Annotations for $OnDelete are prefixed with $OnDelete. So far, it's not supported now. } + /// + /// Asynchronously Writes Navigation OnDelete Action Element. + /// + /// The Edm OnDelete Action. + /// Task represents an asynchronous operation that may or may not return a result. + internal override Task WriteNavigationOnDeleteActionElementAsync(EdmOnDeleteAction operationAction) + { + // $OnDelete + this.jsonWriter.WritePropertyName("$OnDelete"); + + // The value of $OnDelete is a string with one of the values Cascade, None, SetNull, or SetDefault. + this.jsonWriter.WriteStringValue(operationAction.ToString()); + + // Annotations for $OnDelete are prefixed with $OnDelete. So far, it's not supported now. + + return Task.CompletedTask; + } + /// /// Write Schema Object /// @@ -304,15 +631,50 @@ internal override void WriteSchemaElementHeader(EdmSchema schema, string alias, } /// - /// Write $Annotations : { + /// Asynchronously Writes Schema Object. /// - /// The total out of line annotations. - internal override void WriteOutOfLineAnnotationsBegin(IEnumerable>> outOfLineAnnotations) + /// The Schema + /// The alias + /// The namespace prefix mapping. It's used in XML, Not apply for JSON. + /// Task represents an asynchronous operation that may or may not return a result. + internal override Task WriteSchemaElementHeaderAsync(EdmSchema schema, string alias, IEnumerable> mappings) { - // The value of $Annotations is an object with one member per annotation target. - this.jsonWriter.WritePropertyName("$Annotations"); - this.jsonWriter.WriteStartObject(); - } + // A schema is represented as a member of the document object whose name is the schema namespace. + this.jsonWriter.WritePropertyName(schema.Namespace); + + // Its value is an object + this.jsonWriter.WriteStartObject(); + + // It MAY contain the members $Alias + this.jsonWriter.WriteOptionalProperty("$Alias", alias); + + return Task.CompletedTask; + } + + /// + /// Write $Annotations : { + /// + /// The total out of line annotations. + internal override void WriteOutOfLineAnnotationsBegin(IEnumerable>> outOfLineAnnotations) + { + // The value of $Annotations is an object with one member per annotation target. + this.jsonWriter.WritePropertyName("$Annotations"); + this.jsonWriter.WriteStartObject(); + } + + /// + /// Asynchronously Writes $Annotations Begin : { + /// + /// The total out of line annotations. + /// Task represents an asynchronous operation that may or may not return a result. + internal override Task WriteOutOfLineAnnotationsBeginAsync(IEnumerable>> outOfLineAnnotations) + { + // The value of $Annotations is an object with one member per annotation target. + this.jsonWriter.WritePropertyName("$Annotations"); + this.jsonWriter.WriteStartObject(); + + return Task.CompletedTask; + } /// /// Write Annotations with External Targeting. @@ -325,6 +687,20 @@ internal override void WriteAnnotationsElementHeader(KeyValuePair + /// Asynchronously Writes Annotations with External Targeting. + /// + /// The annotation. + /// Task represents an asynchronous operation that may or may not return a result. + internal override Task WriteAnnotationsElementHeaderAsync(KeyValuePair> annotationsForTarget) + { + // The member name is a path identifying the annotation target, the member value is an object containing annotations for that target. + this.jsonWriter.WritePropertyName(annotationsForTarget.Key); + this.jsonWriter.WriteStartObject(); + + return Task.CompletedTask; + } + /// /// Write $Annotations End: } /// @@ -334,6 +710,18 @@ internal override void WriteOutOfLineAnnotationsEnd(IEnumerable + /// Asynchronously Writes $Annotations End: } + /// + /// The total out of line annotations. + /// Task represents an asynchronous operation that may or may not return a result. + internal override Task WriteOutOfLineAnnotationsEndAsync(IEnumerable>> outOfLineAnnotations) + { + this.jsonWriter.WriteEndObject(); + + return Task.CompletedTask; + } + /// /// Write structural property object. /// @@ -361,6 +749,34 @@ internal override void WriteStructuralPropertyElementHeader(IEdmStructuralProper this.jsonWriter.WriteOptionalProperty("$DefaultValue", property.DefaultValueString); } + /// + /// Asynchronously Writes structural property object. + /// + /// The Edm structural property. + /// Is line type or not. + /// Task represents an asynchronous operation. + internal override async Task WriteStructuralPropertyElementHeaderAsync(IEdmStructuralProperty property, bool inlineType) + { + // Structural properties are represented as members of the object representing a structured type. The member name is the property name. + this.jsonWriter.WritePropertyName(property.Name); + + // The member value is an object. + this.jsonWriter.WriteStartObject(); + + // The property object MAY contain the member $Kind with a string value of Property. This member SHOULD be omitted to reduce document size. + // So, we omit the $Kind for property. + + // It MAY contain the member $Type & $Collection + if (inlineType) + { + await WriteTypeReferenceAsync(property.Type).ConfigureAwait(false); + } + + // The value of $DefaultValue is the type-specific JSON representation of the default value of the property. + // So far, it only includes the string format. + this.jsonWriter.WriteOptionalProperty("$DefaultValue", property.DefaultValueString); + } + /// /// Write enumeration type object header /// @@ -389,6 +805,37 @@ internal override void WriteEnumTypeElementHeader(IEdmEnumType enumType) this.isInEnumTypeWriting = true; } + /// + /// Asynchronously Writes enumeration type object header + /// + /// The given enumeration type. + /// Task represents an asynchronous operation that may or may not return a result. + internal override Task WriteEnumTypeElementHeaderAsync(IEdmEnumType enumType) + { + // An enumeration type is represented as a member of the schema object whose name is the unqualified name of the enumeration type + this.jsonWriter.WritePropertyName(enumType.Name); + + // whose value is an object + this.jsonWriter.WriteStartObject(); + + // The enumeration type object MUST contain the member $Kind with a string value of EnumType. + this.jsonWriter.WriteRequiredProperty("$Kind", CsdlConstants.Element_EnumType); + + // An enumeration type MAY specify one of Edm.Byte, Edm.SByte, Edm.Int16, Edm.Int32, or Edm.Int64 as its underlying type. + // If not explicitly specified, Edm.Int32 is used as the underlying type. + if (enumType.UnderlyingType.PrimitiveKind != EdmPrimitiveTypeKind.Int32) + { + this.jsonWriter.WriteRequiredProperty("$UnderlyingType", enumType.UnderlyingType, this.TypeDefinitionAsJson); + } + + // The value of $IsFlags is one of the Boolean literals true or false. Absence of the member means false. + this.jsonWriter.WriteOptionalProperty("$IsFlags", enumType.IsFlags, CsdlConstants.Default_IsFlags); + + this.isInEnumTypeWriting = true; + + return Task.CompletedTask; + } + /// /// Write enumeration type object end /// @@ -399,6 +846,17 @@ internal override void WriteEnumTypeElementEnd(IEdmEnumType enumType) this.isInEnumTypeWriting = false; } + /// + /// Asynchronously Writes enumeration type object end + /// + /// The given enumeration type. + /// Task represents an asynchronous operation. + internal override async Task WriteEnumTypeElementEndAsync(IEdmEnumType enumType) + { + await WriteEndElementAsync().ConfigureAwait(false); + this.isInEnumTypeWriting = false; + } + /// /// Write Enumeration Member Object start /// @@ -411,6 +869,21 @@ internal override void WriteEnumMemberElementHeader(IEdmEnumMember member) this.jsonWriter.WriteRequiredProperty(member.Name, member.Value.Value); } + /// + /// Asynchronously Writes Enumeration Member Object start + /// + /// The Edm Enum member. + /// Task represents an asynchronous operation that may or may not return a result. + internal override Task WriteEnumMemberElementHeaderAsync(IEdmEnumMember member) + { + // Enumeration type members are represented as JSON object members. + // Member name is the enumeration member name. + // member value is the enumeration member value + this.jsonWriter.WriteRequiredProperty(member.Name, member.Value.Value); + + return Task.CompletedTask; + } + /// /// 7.2.1 Nullable, A Boolean value specifying whether a value is required for the property. /// @@ -421,6 +894,23 @@ internal override void WriteNullableAttribute(IEdmTypeReference reference) this.jsonWriter.WriteOptionalProperty("$Nullable", reference.IsNullable, defaultValue: false); } + /// + /// Asynchronously Writes Nullable, A Boolean value specifying whether a value is required for the property. + /// + /// The Edm type reference. + /// Task represents an asynchronous operation that may or may not return a result. + internal override Task WriteNullableAttributeAsync(IEdmTypeReference reference) + { + // The value of $Nullable is one of the Boolean literals true or false. Absence of the member means false. + this.jsonWriter.WriteOptionalProperty("$Nullable", reference.IsNullable, defaultValue: false); + + return Task.CompletedTask; + } + + /// + /// Writes Type Definition Attributes. + /// + /// The Edm Type Definition Reference. internal override void WriteTypeDefinitionAttributes(IEdmTypeDefinitionReference reference) { IEdmTypeReference actualTypeReference = reference.AsActualTypeReference(); @@ -447,6 +937,41 @@ internal override void WriteTypeDefinitionAttributes(IEdmTypeDefinitionReference } } + /// + /// Asynchronously Writes Type Definition Attributes. + /// + /// The Edm Type Definition Reference. + /// Task represents an asynchronous operation. + internal override async Task WriteTypeDefinitionAttributesAsync(IEdmTypeDefinitionReference reference) + { + IEdmTypeReference actualTypeReference = reference.AsActualTypeReference(); + + if (actualTypeReference.IsBinary()) + { + await this.WriteBinaryTypeAttributesAsync(actualTypeReference.AsBinary()).ConfigureAwait(false); + } + else if (actualTypeReference.IsString()) + { + await this.WriteStringTypeAttributesAsync(actualTypeReference.AsString()).ConfigureAwait(false); + } + else if (actualTypeReference.IsTemporal()) + { + await this.WriteTemporalTypeAttributesAsync(actualTypeReference.AsTemporal()).ConfigureAwait(false); + } + else if (actualTypeReference.IsDecimal()) + { + await this.WriteDecimalTypeAttributesAsync(actualTypeReference.AsDecimal()).ConfigureAwait(false); + } + else if (actualTypeReference.IsSpatial()) + { + await this.WriteSpatialTypeAttributesAsync(actualTypeReference.AsSpatial()).ConfigureAwait(false); + } + } + + /// + /// Writes Binary Type Attributes. + /// + /// The Edm Binary Type Reference internal override void WriteBinaryTypeAttributes(IEdmBinaryTypeReference reference) { // CSDL XML defines a symbolic value max that is only allowed in OData 4.0 responses. @@ -455,6 +980,25 @@ internal override void WriteBinaryTypeAttributes(IEdmBinaryTypeReference referen this.jsonWriter.WriteOptionalProperty("$MaxLength", reference.MaxLength); } + /// + /// Asynchronously Writes Binary Type Attributes. + /// + /// The Edm Binary Type Reference + /// Task represents an asynchronous operation that may or may not return a result. + internal override Task WriteBinaryTypeAttributesAsync(IEdmBinaryTypeReference reference) + { + // CSDL XML defines a symbolic value max that is only allowed in OData 4.0 responses. + // This symbolic value is not allowed in CDSL JSON documents at all. + // So, 'IsUnbounded' is skipped in CSDL JSON. + this.jsonWriter.WriteOptionalProperty("$MaxLength", reference.MaxLength); + + return Task.CompletedTask; + } + + /// + /// Writes DecimalType Attributes. + /// + /// The Edm Decimal Type Reference. internal override void WriteDecimalTypeAttributes(IEdmDecimalTypeReference reference) { // The value of $Precision is a number. Absence of $Precision means arbitrary precision. @@ -466,6 +1010,28 @@ internal override void WriteDecimalTypeAttributes(IEdmDecimalTypeReference refer this.jsonWriter.WriteOptionalProperty("$Scale", reference.Scale); } + /// + /// Asynchronously Writes Decimal Type Attributes. + /// + /// The Edm Decimal Type Reference. + /// Task represents an asynchronous operation that may or may not return a result. + internal override Task WriteDecimalTypeAttributesAsync(IEdmDecimalTypeReference reference) + { + // The value of $Precision is a number. Absence of $Precision means arbitrary precision. + this.jsonWriter.WriteOptionalProperty("$Precision", reference.Precision); + + // The value of $Scale is a number or a string with one of the symbolic values floating or variable. + // TODO: the symbolic values floating or variable is not supported now. + // Absence of $Scale means variable. + this.jsonWriter.WriteOptionalProperty("$Scale", reference.Scale); + + return Task.CompletedTask; + } + + /// + /// Writes Spatial Type Attributes. $SRID + /// + /// The Edm Spatial Type Reference. internal override void WriteSpatialTypeAttributes(IEdmSpatialTypeReference reference) { // The value of $SRID is a string containing a number or the symbolic value variable @@ -482,6 +1048,33 @@ internal override void WriteSpatialTypeAttributes(IEdmSpatialTypeReference refer } } + /// + /// Asynchronously Writes Spatial Type Attributes. $SRID + /// + /// The Edm Spatial Type Reference. + /// Task represents an asynchronous operation that may or may not return a result. + internal override Task WriteSpatialTypeAttributesAsync(IEdmSpatialTypeReference reference) + { + // The value of $SRID is a string containing a number or the symbolic value variable + // The value of the SRID facet MUST be a non-negative integer or the special value variable. + // TODO: the special value variable is not supported now. + // If no value is specified, the facet defaults to 0 for Geometry types or 4326 for Geography types. + if (reference.IsGeography()) + { + this.jsonWriter.WriteOptionalProperty("$SRID", reference.SpatialReferenceIdentifier, CsdlConstants.Default_SpatialGeographySrid); + } + else if (reference.IsGeometry()) + { + this.jsonWriter.WriteOptionalProperty("$SRID", reference.SpatialReferenceIdentifier, CsdlConstants.Default_SpatialGeometrySrid); + } + + return Task.CompletedTask; + } + + /// + /// Writes String Type Attributes. $MaxLength, $Unicode + /// + /// The Edm String Type Reference. internal override void WriteStringTypeAttributes(IEdmStringTypeReference reference) { // CSDL XML defines a symbolic value max that is only allowed in OData 4.0 responses. @@ -492,12 +1085,50 @@ internal override void WriteStringTypeAttributes(IEdmStringTypeReference referen this.jsonWriter.WriteOptionalProperty("$Unicode", reference.IsUnicode, CsdlConstants.Default_IsUnicode); } + /// + /// Asynchronously Writes String Type Attributes. $MaxLength, $Unicode + /// + /// The Edm String Type Reference. + /// Task represents an asynchronous operation that may or may not return a result. + internal override Task WriteStringTypeAttributesAsync(IEdmStringTypeReference reference) + { + // CSDL XML defines a symbolic value max that is only allowed in OData 4.0 responses. + // This symbolic value is not allowed in CDSL JSON documents at all. + this.jsonWriter.WriteOptionalProperty("$MaxLength", reference.MaxLength); + + // The value of $Unicode is one of the Boolean literals true or false.Absence of the member means true. + this.jsonWriter.WriteOptionalProperty("$Unicode", reference.IsUnicode, CsdlConstants.Default_IsUnicode); + + return Task.CompletedTask; + } + + /// + /// Writes Temporal Type Attributes. $Precision + /// + /// The Edm Temporal Type Reference. internal override void WriteTemporalTypeAttributes(IEdmTemporalTypeReference reference) { // The value of $Precision is a number. Absence of $Precision means arbitrary precision. this.jsonWriter.WriteOptionalProperty("$Precision", reference.Precision); } + /// + /// Asynchronously Writes Temporal Type Attributes. $Precision + /// + /// The Edm Temporal Type Reference. + /// Task represents an asynchronous operation that may or may not return a result. + internal override Task WriteTemporalTypeAttributesAsync(IEdmTemporalTypeReference reference) + { + // The value of $Precision is a number. Absence of $Precision means arbitrary precision. + this.jsonWriter.WriteOptionalProperty("$Precision", reference.Precision); + + return Task.CompletedTask; + } + + /// + /// Writes Annotation String Attribute. + /// + /// The Edm Direct Value Annotation. internal override void WriteAnnotationStringAttribute(IEdmDirectValueAnnotation annotation) { var edmValue = (IEdmPrimitiveValue)annotation.Value; @@ -507,6 +1138,25 @@ internal override void WriteAnnotationStringAttribute(IEdmDirectValueAnnotation } } + /// + /// Asynchronously Writes Annotation String Attribute. + /// + /// The Edm Direct Value Annotation. + /// Task represents an asynchronous operation that may or may not return a result. + internal override Task WriteAnnotationStringAttributeAsync(IEdmDirectValueAnnotation annotation) + { + if ((IEdmPrimitiveValue)annotation.Value != null) + { + this.jsonWriter.WriteRequiredProperty(annotation.Name, EdmValueWriter.PrimitiveValueAsXml((IEdmPrimitiveValue)annotation.Value)); + } + + return Task.CompletedTask; + } + + /// + /// Writes Annotation String Element. + /// + /// The Edm Direct Value Annotation. internal override void WriteAnnotationStringElement(IEdmDirectValueAnnotation annotation) { var edmValue = (IEdmPrimitiveValue)annotation.Value; @@ -516,6 +1166,27 @@ internal override void WriteAnnotationStringElement(IEdmDirectValueAnnotation an } } + /// + /// Asynchronously Writes Annotation String Element. + /// + /// The Edm Direct Value Annotation. + /// Task represents an asynchronous operation that may or may not return a result. + internal override Task WriteAnnotationStringElementAsync(IEdmDirectValueAnnotation annotation) + { + var edmValue = (IEdmPrimitiveValue)annotation.Value; + if (edmValue != null) + { + this.jsonWriter.WriteStringValue(((IEdmStringValue)edmValue).Value); + } + + return Task.CompletedTask; + } + + /// + /// Writes Schema Operations Header. + /// + /// + /// The Key/Value Pair Operation. An operation is represented as a member of the schema object whose name is the unqualified name of the operation. internal override void WriteSchemaOperationsHeader(KeyValuePair> operation) { // An operation is represented as a member of the schema object whose name is the unqualified name of the operation. @@ -525,13 +1196,52 @@ internal override void WriteSchemaOperationsHeader(KeyValuePair(KeyValuePair> operation) + /// + /// Asynchronously Writes Schema Operations Header. + /// + /// + /// The Key/Value Pair Operation. An operation is represented as a member of the schema object whose name is the unqualified name of the operation. + /// Task represents an asynchronous operation that may or may not return a result. + internal override Task WriteSchemaOperationsHeaderAsync(KeyValuePair> operation) { - this.jsonWriter.WriteEndArray(); - } + // An operation is represented as a member of the schema object whose name is the unqualified name of the operation. + this.jsonWriter.WritePropertyName(operation.Key); - internal override void WriteActionElementHeader(IEdmAction action) - { + // Whose value is an array + this.jsonWriter.WriteStartArray(); + + return Task.CompletedTask; + } + + /// + /// Writes Schema Operations End. + /// + /// + /// The Key/Value Pair Operation. An operation is represented as a member of the schema object whose name is the unqualified name of the operation. + internal override void WriteSchemaOperationsEnd(KeyValuePair> operation) + { + this.jsonWriter.WriteEndArray(); + } + + /// + /// Asynchronously Writes Schema Operations End. + /// + /// + /// The Key/Value Pair Operation. An operation is represented as a member of the schema object whose name is the unqualified name of the operation. + /// Task represents an asynchronous operation that may or may not return a result. + internal override Task WriteSchemaOperationsEndAsync(KeyValuePair> operation) + { + this.jsonWriter.WriteEndArray(); + + return Task.CompletedTask; + } + + /// + /// Writes Action Element Header. $Kind + /// + /// The Edm Action. + internal override void WriteActionElementHeader(IEdmAction action) + { this.jsonWriter.WriteStartObject(); // The action overload object MUST contain the member $Kind with a string value of Action. @@ -540,6 +1250,25 @@ internal override void WriteActionElementHeader(IEdmAction action) this.WriteOperationElementAttributes(action); } + /// + /// Asynchronously Writes Action Element Header. $Kind + /// + /// The Edm Action. + /// Task represents an asynchronous operation. + internal override async Task WriteActionElementHeaderAsync(IEdmAction action) + { + this.jsonWriter.WriteStartObject(); + + // The action overload object MUST contain the member $Kind with a string value of Action. + this.jsonWriter.WriteRequiredProperty("$Kind", CsdlConstants.Element_Action); + + await this.WriteOperationElementAttributesAsync(action).ConfigureAwait(false); + } + + /// + /// Writes Function Element Header. $Kind, $IsComposable + /// + /// The Edm Function. internal override void WriteFunctionElementHeader(IEdmFunction function) { this.jsonWriter.WriteStartObject(); @@ -555,6 +1284,30 @@ internal override void WriteFunctionElementHeader(IEdmFunction function) } } + /// + /// Asynchronously Writes Function Element Header. $Kind, $IsComposable + /// + /// The Edm Function. + /// Task represents an asynchronous operation. + internal override async Task WriteFunctionElementHeaderAsync(IEdmFunction function) + { + this.jsonWriter.WriteStartObject(); + + // The action overload object MUST contain the member $Kind with a string value of Action. + this.jsonWriter.WriteRequiredProperty("$Kind", CsdlConstants.Element_Function); + + await this.WriteOperationElementAttributesAsync(function).ConfigureAwait(false); + + if (function.IsComposable) + { + this.jsonWriter.WriteRequiredProperty("$IsComposable", true); + } + } + + /// + /// Writes Operation Element Attributes. $IsBound, $EntitySetPath + /// + /// The Edm Operation. internal override void WriteOperationElementAttributes(IEdmOperation operation) { if (operation.IsBound) @@ -568,6 +1321,30 @@ internal override void WriteOperationElementAttributes(IEdmOperation operation) } } + /// + /// Asynchronously Writes Operation Element Attributes. $IsBound, $EntitySetPath + /// + /// The Edm Operation. + /// Task represents an asynchronous operation that may or may not return a result. + internal override Task WriteOperationElementAttributesAsync(IEdmOperation operation) + { + if (operation.IsBound) + { + this.jsonWriter.WriteRequiredProperty("$IsBound", true); + } + + if (operation.EntitySetPath != null) + { + this.jsonWriter.WriteRequiredProperty("$EntitySetPath", operation.EntitySetPath.Path); + } + + return Task.CompletedTask; + } + + /// + /// Writes Operation Parameters Begin. $Parameter + /// + /// The Edm Operation parameters. internal override void WriteOperationParametersBegin(IEnumerable parameters) { if (parameters != null && parameters.Any()) @@ -577,6 +1354,26 @@ internal override void WriteOperationParametersBegin(IEnumerable + /// Asynchronously Writes Operation Parameters Begin. $Parameter + /// + /// The Edm Operation parameters. + /// Task represents an asynchronous operation that may or may not return a result. + internal override Task WriteOperationParametersBeginAsync(IEnumerable parameters) + { + if (parameters != null && parameters.Any()) + { + this.jsonWriter.WritePropertyName("$Parameter"); + this.jsonWriter.WriteStartArray(); + } + + return Task.CompletedTask; + } + + /// + /// Writes Operation Parameters End. + /// + /// The Edm Operation parameters. internal override void WriteOperationParametersEnd(IEnumerable parameters) { if (parameters != null && parameters.Any()) @@ -585,6 +1382,25 @@ internal override void WriteOperationParametersEnd(IEnumerable + /// Asynchronously Writes Operation Parameters End. + /// + /// The Edm Operation parameters. + /// Task represents an asynchronous operation that may or may not return a result. + internal override Task WriteOperationParametersEndAsync(IEnumerable parameters) + { + if (parameters != null && parameters.Any()) + { + this.jsonWriter.WriteEndArray(); + } + + return Task.CompletedTask; + } + + /// + /// Writes Return Type Element Header. $ReturnType + /// + /// The Edm Operation Return. internal override void WriteReturnTypeElementHeader(IEdmOperationReturn operationReturn) { // $ReturnType @@ -594,11 +1410,41 @@ internal override void WriteReturnTypeElementHeader(IEdmOperationReturn operatio this.jsonWriter.WriteStartObject(); } + /// + /// Asynchronously Writes Return Type Element Header. $ReturnType + /// + /// The Edm Operation Return. + /// Task represents an asynchronous operation that may or may not return a result. + internal override Task WriteReturnTypeElementHeaderAsync(IEdmOperationReturn operationReturn) + { + // $ReturnType + this.jsonWriter.WritePropertyName("$ReturnType"); + + // The value of $ReturnType is an object. + this.jsonWriter.WriteStartObject(); + + return Task.CompletedTask; + } + + /// + /// Writes Type Attribute + /// + /// The Edm Type Reference. internal override void WriteTypeAttribute(IEdmTypeReference typeReference) { WriteTypeReference(typeReference); } + /// + /// Asynchronously Writes Type Attribute + /// + /// The Edm Type Reference. + /// Task represents an asynchronous operation. + internal override Task WriteTypeAttributeAsync(IEdmTypeReference typeReference) + { + return WriteTypeReferenceAsync(typeReference); + } + /// /// Write Entity Container Object /// @@ -624,6 +1470,34 @@ internal override void WriteEntityContainerElementHeader(IEdmEntityContainer con } } + /// + /// Asynchronously Writes Entity Container Element Header. + /// + /// The Edm Entity Container. + /// Task represents an asynchronous operation that may or may not return a result. + internal override Task WriteEntityContainerElementHeaderAsync(IEdmEntityContainer container) + { + // An entity container is represented as a member of the schema object whose name is the unqualified name of the entity container + this.jsonWriter.WritePropertyName(container.Name); + + // whose value is an object + this.jsonWriter.WriteStartObject(); + + // The entity container object MUST contain the member $Kind with a string value of EntityContainer. + // Be caution: Example 33 has the $Kind, but no other words. + this.jsonWriter.WriteRequiredProperty("$Kind", CsdlConstants.Element_EntityContainer); + + // The entity container object MAY contain the member $Extends, so far it only supports in Csdl Semantics. + CsdlSemanticsEntityContainer tmp = container as CsdlSemanticsEntityContainer; + CsdlEntityContainer csdlContainer; + if (tmp != null && (csdlContainer = tmp.Element as CsdlEntityContainer) != null) + { + this.jsonWriter.WriteOptionalProperty("$Extends", csdlContainer.Extends); + } + + return Task.CompletedTask; + } + /// /// Write Entity Set object /// @@ -646,6 +1520,31 @@ internal override void WriteEntitySetElementHeader(IEdmEntitySet entitySet) this.jsonWriter.WriteOptionalProperty("$IncludeInServiceDocument", entitySet.IncludeInServiceDocument, true); } + /// + /// Asynchronously Writes Entity Set object. + /// + /// The Edm Entity Set. + /// Task represents an asynchronous operation that may or may not return a result. + internal override Task WriteEntitySetElementHeaderAsync(IEdmEntitySet entitySet) + { + // An entity set is represented as a member of the entity container object whose name is the name of the entity set + this.jsonWriter.WritePropertyName(entitySet.Name); + + // whose value is an object + this.jsonWriter.WriteStartObject(); + + // The entity set object MUST contain the members $Collection, it's value as true + this.jsonWriter.WriteRequiredProperty("$Collection", true); + + // The entity set object MUST contain the member $Type whose string value is the qualified name of an entity type. + this.jsonWriter.WriteRequiredProperty("$Type", entitySet.EntityType().FullName()); + + // It MAY contain the members $IncludeInServiceDocument. Absence of the member means true. + this.jsonWriter.WriteOptionalProperty("$IncludeInServiceDocument", entitySet.IncludeInServiceDocument, true); + + return Task.CompletedTask; + } + /// /// Write Singleton object /// @@ -665,6 +1564,28 @@ internal override void WriteSingletonElementHeader(IEdmSingleton singleton) // So far, IEdmSingleton doesn't have the property defined, so skip it now. } + /// + /// Asynchronously Writes Singleton Object. + /// + /// The Edm singleton. + /// Task represents an asynchronous operation that may or may not return a result. + internal override Task WriteSingletonElementHeaderAsync(IEdmSingleton singleton) + { + // A singleton is represented as a member of the entity container object whose name is the name of the singleton + this.jsonWriter.WritePropertyName(singleton.Name); + + // whose value is an object + this.jsonWriter.WriteStartObject(); + + // The singleton object MUST contain the member $Type whose string value is the qualified name of an entity type. + this.jsonWriter.WriteRequiredProperty("$Type", singleton.EntityType().FullName()); + + // The singleton object MAY contain the member $Nullable. In OData 4.0 responses this member MUST NOT be specified. + // So far, IEdmSingleton doesn't have the property defined, so skip it now. + + return Task.CompletedTask; + } + /// /// Write Action Import Object /// @@ -687,6 +1608,29 @@ internal override void WriteActionImportElementHeader(IEdmActionImport actionImp this.WriteOperationImportAttributes(actionImport, CsdlConstants.Attribute_Action); } + /// + /// Asynchronously Writes Action Import Object. + /// + /// The Edm action import. + /// Task represents an asynchronous operation. + internal override async Task WriteActionImportElementHeaderAsync(IEdmActionImport actionImport) + { + // An action import is represented as a member of the entity container object whose name is the name of the action import. + this.jsonWriter.WritePropertyName(actionImport.Name); + + // whose value is an object + this.jsonWriter.WriteStartObject(); + + // The action import object MUST contain the member $Kind with a string value of ActionImport, and the member $Action. + this.jsonWriter.WriteRequiredProperty("$Kind", CsdlConstants.Element_ActionImport); + + // The action import object MUST contain the member $Action. The value of $Action is a string containing the qualified name of an unbound action. + this.jsonWriter.WriteRequiredProperty("$Action", actionImport.Operation.FullName()); + + // The action import object MAY contain the member $EntitySet. + await this.WriteOperationImportAttributesAsync(actionImport, CsdlConstants.Attribute_Action).ConfigureAwait(false); + } + /// /// Write Function Import Object /// @@ -715,6 +1659,40 @@ internal override void WriteFunctionImportElementHeader(IEdmFunctionImport funct } } + /// + /// Asynchronously Writes Function Import Object. + /// + /// The Edm function import. + /// Task represents an asynchronous operation. + internal override async Task WriteFunctionImportElementHeaderAsync(IEdmFunctionImport functionImport) + { + // A function import is represented as a member of the entity container object whose name is the name of the function import + this.jsonWriter.WritePropertyName(functionImport.Name); + + // whose value is an object. + this.jsonWriter.WriteStartObject(); + + // The function import object MUST contain the member $Kind with a string value of FunctionImport + this.jsonWriter.WriteRequiredProperty("$Kind", CsdlConstants.Element_FunctionImport); + + // The function import object MUST contain the member $Function. + this.jsonWriter.WriteRequiredProperty("$Function", functionImport.Operation.FullName()); + + // The function import object MAY contain the member $EntitySet. + await this.WriteOperationImportAttributesAsync(functionImport, CsdlConstants.Attribute_Function).ConfigureAwait(false); + + // The value of $IncludeInServiceDocument is one of the Boolean literals true or false. Absence of the member means false. + if (functionImport.IncludeInServiceDocument) + { + this.jsonWriter.WriteRequiredProperty("$IncludeInServiceDocument", functionImport.IncludeInServiceDocument); + } + } + + /// + /// Writes Operation Parameter Object. + /// + /// The Edm Operation parameter. + /// Is line type or not. internal override void WriteOperationParameterElementHeader(IEdmOperationParameter parameter, bool inlineType) { this.jsonWriter.WriteStartObject(); @@ -728,6 +1706,30 @@ internal override void WriteOperationParameterElementHeader(IEdmOperationParamet } } + /// + /// Asynchronously Writes Operation Parameter Object. + /// + /// The Edm Operation parameter. + /// Is line type or not. + /// Task represents an asynchronous operation. + internal override async Task WriteOperationParameterElementHeaderAsync(IEdmOperationParameter parameter, bool inlineType) + { + this.jsonWriter.WriteStartObject(); + + // A parameter object MUST contain the member $Name + this.jsonWriter.WriteRequiredProperty("$Name", parameter.Name); + + if (inlineType) + { + await WriteTypeReferenceAsync(parameter.Type).ConfigureAwait(false); + } + } + + /// + /// Writes Type Reference. + /// + /// The Edm Type Reference. + /// The Default Type name. Set to "Edm.String" by default. internal void WriteTypeReference(IEdmTypeReference type, string defaultTypeName = "Edm.String") { IEdmTypeReference elementType = type; @@ -748,23 +1750,55 @@ internal void WriteTypeReference(IEdmTypeReference type, string defaultTypeName } } - internal override void WriteOperationParameterEndElement(IEdmOperationParameter parameter) + /// + /// Asynchronously Writes Type Reference. + /// + /// The Edm Type Reference. + /// The Default Type name. Set to "Edm.String" by default. + /// Task represents an asynchronous operation that may or may not return a result. + internal Task WriteTypeReferenceAsync(IEdmTypeReference type, string defaultTypeName = "Edm.String") { - IEdmOptionalParameter optionalParameter = parameter as IEdmOptionalParameter; - if (optionalParameter != null && !(optionalParameter.VocabularyAnnotations(this.Model).Any(a => a.Term == CoreVocabularyModel.OptionalParameterTerm))) + IEdmTypeReference elementType = type; + if (type.IsCollection()) { - string defaultValue = optionalParameter.DefaultValueString; - EdmRecordExpression optionalValue = new EdmRecordExpression(); + this.jsonWriter.WriteRequiredProperty("$Collection", true); - this.WriteVocabularyAnnotationElementHeader(new EdmVocabularyAnnotation(parameter, CoreVocabularyModel.OptionalParameterTerm, optionalValue), false); + IEdmCollectionTypeReference collectionReference = type.AsCollection(); + elementType = collectionReference.ElementType(); + } - if (!String.IsNullOrEmpty(defaultValue)) - { - EdmPropertyConstructor property = new EdmPropertyConstructor(CsdlConstants.Attribute_DefaultValue, new EdmStringConstant(defaultValue)); - this.WriteRecordExpressionElementHeader(optionalValue); - this.WritePropertyValueElementHeader(property, true); - this.WriteEndElement(); - } + // Absence of the $Type member means the type is Edm.String. + // Does it mean to omit the type for Collection(Edm.String)? No, $Collection is used to identify whether it's collection of not. + if (elementType.FullName() != defaultTypeName) + { + string typeName = this.SerializationName((IEdmSchemaElement)elementType.Definition); + this.jsonWriter.WriteRequiredProperty("$Type", typeName); + } + + return Task.CompletedTask; + } + + /// + /// Writes Operation Parameter End Element. + /// + /// The Edm Operation Parameter. + internal override void WriteOperationParameterEndElement(IEdmOperationParameter parameter) + { + IEdmOptionalParameter optionalParameter = parameter as IEdmOptionalParameter; + if (optionalParameter != null && !(optionalParameter.VocabularyAnnotations(this.Model).Any(a => a.Term == CoreVocabularyModel.OptionalParameterTerm))) + { + string defaultValue = optionalParameter.DefaultValueString; + EdmRecordExpression optionalValue = new EdmRecordExpression(); + + this.WriteVocabularyAnnotationElementHeader(new EdmVocabularyAnnotation(parameter, CoreVocabularyModel.OptionalParameterTerm, optionalValue), false); + + if (!String.IsNullOrEmpty(defaultValue)) + { + EdmPropertyConstructor property = new EdmPropertyConstructor(CsdlConstants.Attribute_DefaultValue, new EdmStringConstant(defaultValue)); + this.WriteRecordExpressionElementHeader(optionalValue); + this.WritePropertyValueElementHeader(property, true); + this.WriteEndElement(); + } else { this.jsonWriter.WriteStartObject(); @@ -775,10 +1809,62 @@ internal override void WriteOperationParameterEndElement(IEdmOperationParameter this.WriteEndElement(); } + /// + /// Asynchronously Writes Operation Parameter End Element. + /// + /// The Edm Operation Parameter. + /// Task represents an asynchronous operation. + internal override async Task WriteOperationParameterEndElementAsync(IEdmOperationParameter parameter) + { + IEdmOptionalParameter optionalParameter = parameter as IEdmOptionalParameter; + if (optionalParameter != null && !(optionalParameter.VocabularyAnnotations(this.Model).Any(a => a.Term == CoreVocabularyModel.OptionalParameterTerm))) + { + string defaultValue = optionalParameter.DefaultValueString; + EdmRecordExpression optionalValue = new EdmRecordExpression(); + + await this.WriteVocabularyAnnotationElementHeaderAsync(new EdmVocabularyAnnotation(parameter, CoreVocabularyModel.OptionalParameterTerm, optionalValue), false).ConfigureAwait(false); + + if (!string.IsNullOrEmpty(defaultValue)) + { + var property = new EdmPropertyConstructor(CsdlConstants.Attribute_DefaultValue, new EdmStringConstant(defaultValue)); + await this.WriteRecordExpressionElementHeaderAsync(optionalValue).ConfigureAwait(false); + await this.WritePropertyValueElementHeaderAsync(property, true).ConfigureAwait(false); + await this.WriteEndElementAsync().ConfigureAwait(false); + } + else + { + this.jsonWriter.WriteStartObject(); + this.jsonWriter.WriteEndObject(); + } + } + + await this.WriteEndElementAsync().ConfigureAwait(false); + } + + /// + /// Writes Collection Type Element Header. + /// + /// The Edm Collection Type. + /// Is line type or not. internal override void WriteCollectionTypeElementHeader(IEdmCollectionType collectionType, bool inlineType) { } + /// + /// Asynchronously Writes Collection Type Element Header. + /// + /// The Edm Collection Type. + /// Is line type or not. + /// Task represents an asynchronous operation that may or may not return a result. + internal override Task WriteCollectionTypeElementHeaderAsync(IEdmCollectionType collectionType, bool inlineType) + { + return Task.CompletedTask; + } + + /// + /// Writes Inline Expression. + /// + /// The Edm Expression. internal override void WriteInlineExpression(IEdmExpression expression) { IEdmPathExpression pathExpression = expression as IEdmPathExpression; @@ -835,6 +1921,57 @@ internal override void WriteInlineExpression(IEdmExpression expression) } } + /// + /// Asynchronously Writes Inline Expression. + /// + /// The Edm Expression. + /// Task represents an asynchronous operation. + internal override Task WriteInlineExpressionAsync(IEdmExpression expression) + { + IEdmPathExpression pathExpression = expression as IEdmPathExpression; + switch (expression.ExpressionKind) + { + case EdmExpressionKind.BinaryConstant: + return WriteBinaryConstantExpressionElementAsync((IEdmBinaryConstantExpression)expression); + case EdmExpressionKind.BooleanConstant: + return WriteBooleanConstantExpressionElementAsync((IEdmBooleanConstantExpression)expression); + case EdmExpressionKind.DateTimeOffsetConstant: + return WriteDateTimeOffsetConstantExpressionElementAsync((IEdmDateTimeOffsetConstantExpression)expression); + case EdmExpressionKind.DecimalConstant: + return WriteDecimalConstantExpressionElementAsync((IEdmDecimalConstantExpression)expression); + case EdmExpressionKind.FloatingConstant: + return WriteFloatingConstantExpressionElementAsync((IEdmFloatingConstantExpression)expression); + case EdmExpressionKind.GuidConstant: + return WriteGuidConstantExpressionElementAsync((IEdmGuidConstantExpression)expression); + case EdmExpressionKind.IntegerConstant: + return WriteIntegerConstantExpressionElementAsync((IEdmIntegerConstantExpression)expression); + case EdmExpressionKind.Path: + return WritePathExpressionElementAsync(pathExpression); + case EdmExpressionKind.PropertyPath: + return WritePropertyPathExpressionElementAsync(pathExpression); + case EdmExpressionKind.NavigationPropertyPath: + return WriteNavigationPropertyPathExpressionElementAsync(pathExpression); + case EdmExpressionKind.AnnotationPath: + return WriteAnnotationPathExpressionElementAsync(pathExpression); + case EdmExpressionKind.StringConstant: + return WriteStringConstantExpressionElementAsync((IEdmStringConstantExpression)expression); + case EdmExpressionKind.DurationConstant: + return WriteDurationConstantExpressionElementAsync((IEdmDurationConstantExpression)expression); + case EdmExpressionKind.DateConstant: + return WriteDateConstantExpressionElementAsync((IEdmDateConstantExpression)expression); + case EdmExpressionKind.TimeOfDayConstant: + return WriteTimeOfDayConstantExpressionElementAsync((IEdmTimeOfDayConstantExpression)expression); + default: + Debug.Assert(false, "Attempted to inline an expression that was not one of the expected inlineable types."); + return Task.CompletedTask; + } + } + + /// + /// Writes Vocabulary Annotation Header. + /// + /// The Edm Vocabulary Annotation. + /// Is line type or not. internal override void WriteVocabularyAnnotationElementHeader(IEdmVocabularyAnnotation annotation, bool isInline) { this.jsonWriter.WritePropertyName(AnnotationToString(annotation)); @@ -846,11 +1983,49 @@ internal override void WriteVocabularyAnnotationElementHeader(IEdmVocabularyAnno } } + /// + /// Asynchronously Writes Vocabulary Annotation Header. + /// + /// The Edm Vocabulary Annotation. + /// Is line type or not. + /// Task represents an asynchronous operation. + internal override async Task WriteVocabularyAnnotationElementHeaderAsync(IEdmVocabularyAnnotation annotation, bool isInline) + { + this.jsonWriter.WritePropertyName(AnnotationToString(annotation)); + + if (isInline) + { + // In JSON, we always write the annotation value. + await this.WriteInlineExpressionAsync(annotation.Value).ConfigureAwait(false); + } + } + + /// + /// Writes Vocabulary Annotation End. + /// + /// The Edm Vocabulary Annotation. + /// Is line type or not. internal override void WriteVocabularyAnnotationElementEnd(IEdmVocabularyAnnotation annotation, bool isInline) { // nothing here } + /// + /// Asynchronously Writes Vocabulary Annotation End. + /// + /// The Edm Vocabulary Annotation. + /// Is line type or not. + /// Task represents an asynchronous operation that may or may not return a result. + internal override Task WriteVocabularyAnnotationElementEndAsync(IEdmVocabularyAnnotation annotation, bool isInline) + { + return Task.CompletedTask; + } + + /// + /// Writes Property Value Header. + /// + /// The Edm Property Constructor. + /// Is line type or not. internal override void WritePropertyValueElementHeader(IEdmPropertyConstructor value, bool isInline) { this.jsonWriter.WritePropertyName(value.Name); @@ -861,6 +2036,26 @@ internal override void WritePropertyValueElementHeader(IEdmPropertyConstructor v } } + /// + /// Asynchronously Writes Property Value Header. + /// + /// The Edm Property Constructor. + /// Is line type or not. + /// Task represents an asynchronous operation. + internal override async Task WritePropertyValueElementHeaderAsync(IEdmPropertyConstructor value, bool isInline) + { + this.jsonWriter.WritePropertyName(value.Name); + + if (isInline) + { + await this.WriteInlineExpressionAsync(value.Value).ConfigureAwait(false); + } + } + + /// + /// Writes Record Expression Header. + /// + /// The Edm Record Expression. internal override void WriteRecordExpressionElementHeader(IEdmRecordExpression expression) { // Record expressions are represented as objects with one member per property value expression. @@ -874,6 +2069,31 @@ internal override void WriteRecordExpressionElementHeader(IEdmRecordExpression e // It MAY contain annotations for itself. It's not supported now. } + /// + /// Asynchronously Writes Record Expression Header. + /// + /// The Edm Record Expression. + /// Task represents an asynchronous operation that may or may not return a result. + internal override Task WriteRecordExpressionElementHeaderAsync(IEdmRecordExpression expression) + { + // Record expressions are represented as objects with one member per property value expression. + this.jsonWriter.WriteStartObject(); + + if (expression.DeclaredType != null) + { + // The type of a record expression is represented as the @type control information. + this.jsonWriter.WriteRequiredProperty("@type", expression.DeclaredType.FullName()); + } + // It MAY contain annotations for itself. It's not supported now. + + return Task.CompletedTask; + } + + /// + /// Writes Property Constructor Header. + /// + /// The Edm Property Constructor. + /// Is line type or not. internal override void WritePropertyConstructorElementHeader(IEdmPropertyConstructor constructor, bool isInline) { // The member name is the property name, and the member value is the property value expression. @@ -887,29 +2107,117 @@ internal override void WritePropertyConstructorElementHeader(IEdmPropertyConstru // Annotations for record members are prefixed with the member name. It's not supported now. } + /// + /// Asynchronously Writes Property Constructor Header. + /// + /// The Edm Property Constructor. + /// Is line type or not. + /// Task represents an asynchronous operation. + internal override async Task WritePropertyConstructorElementHeaderAsync(IEdmPropertyConstructor constructor, bool isInline) + { + // The member name is the property name, and the member value is the property value expression. + this.jsonWriter.WritePropertyName(constructor.Name); + + if (isInline) + { + await this.WriteInlineExpressionAsync(constructor.Value).ConfigureAwait(false); + } + + // Annotations for record members are prefixed with the member name. It's not supported now. + } + + /// + /// Writes Property Constructor End. + /// + /// The Edm Property Constructor. internal override void WritePropertyConstructorElementEnd(IEdmPropertyConstructor constructor) { // nothing here. } + /// + /// Asynchronously Writes Property Constructor End. + /// + /// The Edm Property Constructor. + /// Task represents an asynchronous operation that may or may not return a result. + internal override Task WritePropertyConstructorElementEndAsync(IEdmPropertyConstructor constructor) + { + return Task.CompletedTask; + } + + /// + /// Writes String Constant Expression. + /// + /// The Edm String Constant Expression. internal override void WriteStringConstantExpressionElement(IEdmStringConstantExpression expression) { // String expressions are represented as a JSON string. this.jsonWriter.WriteStringValue(expression.Value); } + /// + /// Asynchronously Writes String Constant Expression. + /// + /// The Edm String Constant Expression. + /// Task represents an asynchronous operation that may or may not return a result. + internal override Task WriteStringConstantExpressionElementAsync(IEdmStringConstantExpression expression) + { + // String expressions are represented as a JSON string. + this.jsonWriter.WriteStringValue(expression.Value); + + return Task.CompletedTask; + } + + /// + /// Writes Binary Constant Expression. + /// + /// The Edm String Constant Expression. internal override void WriteBinaryConstantExpressionElement(IEdmBinaryConstantExpression expression) { // Binary expressions are represented as a string containing the base64url-encoded binary value. this.jsonWriter.WriteStringValue(BinaryToString(expression)); } + /// + /// Asynchronously Writes Binary Constant Expression. + /// + /// The Edm String Constant Expression. + /// Task represents an asynchronous operation that may or may not return a result. + internal override Task WriteBinaryConstantExpressionElementAsync(IEdmBinaryConstantExpression expression) + { + // Binary expressions are represented as a string containing the base64url-encoded binary value. + this.jsonWriter.WriteStringValue(BinaryToString(expression)); + + return Task.CompletedTask; + } + + /// + /// Writes Boolean Constant Expression. + /// + /// The Edm Boolean Constant Expression. internal override void WriteBooleanConstantExpressionElement(IEdmBooleanConstantExpression expression) { // Boolean expressions are represented as the literals true or false. this.jsonWriter.WriteBooleanValue(expression.Value); } + /// + /// Asynchronously Writes Boolean Constant Expression. + /// + /// The Edm Boolean Constant Expression. + /// Task represents an asynchronous operation that may or may not return a result. + internal override Task WriteBooleanConstantExpressionElementAsync(IEdmBooleanConstantExpression expression) + { + // Boolean expressions are represented as the literals true or false. + this.jsonWriter.WriteBooleanValue(expression.Value); + + return Task.CompletedTask; + } + + /// + /// Writes Null Constant Expression. + /// + /// The Edm Null Constant Expression. internal override void WriteNullConstantExpressionElement(IEdmNullExpression expression) { // Null expressions that do not contain annotations are represented as the literal null. @@ -919,24 +2227,95 @@ internal override void WriteNullConstantExpressionElement(IEdmNullExpression exp // So far, it's not supported. } + /// + /// Asynchronously Writes Null Constant Expression. + /// + /// The Edm Null Constant Expression. + /// Task represents an asynchronous operation that may or may not return a result. + internal override Task WriteNullConstantExpressionElementAsync(IEdmNullExpression expression) + { + // Null expressions that do not contain annotations are represented as the literal null. + this.jsonWriter.WriteNullValue(); + + // Null expression containing annotations are represented as an object with a member $Null whose value is the literal null. + // So far, it's not supported. + + return Task.CompletedTask; + } + + /// + /// Writes Date Constant Expression. + /// + /// The Edm Date Constant Expression. internal override void WriteDateConstantExpressionElement(IEdmDateConstantExpression expression) { // Date expressions are represented as a string containing the date value. this.jsonWriter.WriteStringValue(expression.Value.ToString()); } + /// + /// Asynchronously Writes Date Constant Expression. + /// + /// The Edm Date Constant Expression. + /// Task represents an asynchronous operation that may or may not return a result. + internal override Task WriteDateConstantExpressionElementAsync(IEdmDateConstantExpression expression) + { + // Date expressions are represented as a string containing the date value. + this.jsonWriter.WriteStringValue(expression.Value.ToString()); + + return Task.CompletedTask; + } + + /// + /// Writes Datetime Offset Constant Expression. + /// + /// The Edm DateTimeOffset Constant Expression. internal override void WriteDateTimeOffsetConstantExpressionElement(IEdmDateTimeOffsetConstantExpression expression) { // Datetimestamp expressions are represented as a string containing the timestamp value. this.jsonWriter.WriteStringValue(EdmValueWriter.DateTimeOffsetAsXml(expression.Value)); } + /// + /// Asynchronously Writes Datetime Offset Constant Expression. + /// + /// The Edm DateTimeOffset Constant Expression. + /// Task represents an asynchronous operation that may or may not return a result. + internal override Task WriteDateTimeOffsetConstantExpressionElementAsync(IEdmDateTimeOffsetConstantExpression expression) + { + // Datetimestamp expressions are represented as a string containing the timestamp value. + this.jsonWriter.WriteStringValue(EdmValueWriter.DateTimeOffsetAsXml(expression.Value)); + + return Task.CompletedTask; + } + + /// + /// Writes Duration Constant Expression. + /// + /// The Edm Duration Constant Expression. internal override void WriteDurationConstantExpressionElement(IEdmDurationConstantExpression expression) { // Duration expressions are represented as a string containing the duration value. this.jsonWriter.WriteStringValue(EdmValueWriter.DurationAsXml(expression.Value)); } + /// + /// Asynchronously Writes Duration Constant Expression. + /// + /// The Edm Duration Constant Expression. + /// Task represents an asynchronous operation that may or may not return a result. + internal override Task WriteDurationConstantExpressionElementAsync(IEdmDurationConstantExpression expression) + { + // Duration expressions are represented as a string containing the duration value. + this.jsonWriter.WriteStringValue(EdmValueWriter.DurationAsXml(expression.Value)); + + return Task.CompletedTask; + } + + /// + /// Writes Decimal Constant Expression. + /// + /// The Edm Decimal Constant Expression. internal override void WriteDecimalConstantExpressionElement(IEdmDecimalConstantExpression expression) { // Decimal expressions are represented as either a number or a string. @@ -951,6 +2330,31 @@ internal override void WriteDecimalConstantExpressionElement(IEdmDecimalConstant } } + /// + /// Asynchronously Writes Decimal Constant Expression. + /// + /// The Edm Decimal Constant Expression. + /// Task represents an asynchronous operation that may or may not return a result. + internal override Task WriteDecimalConstantExpressionElementAsync(IEdmDecimalConstantExpression expression) + { + // Decimal expressions are represented as either a number or a string. + // The special values INF, -INF, or NaN are represented as strings. so far, that's not supported. + if (this.settings.IsIeee754Compatible) + { + this.jsonWriter.WriteStringValue(expression.Value.ToString(CultureInfo.InvariantCulture)); + } + else + { + this.jsonWriter.WriteNumberValue(expression.Value); + } + + return Task.CompletedTask; + } + + /// + /// Writes Floating Constant Expression. + /// + /// The Edm Floating Constant Expression. internal override void WriteFloatingConstantExpressionElement(IEdmFloatingConstantExpression expression) { // Ut8JsonWriter can't write the Infinity double, @@ -973,18 +2377,74 @@ internal override void WriteFloatingConstantExpressionElement(IEdmFloatingConsta } } - internal override void WriteFunctionApplicationElementHeader(IEdmApplyExpression expression) + /// + /// Asynchronously Writes Floating Constant Expression. + /// + /// The Edm Floating Constant Expression. + /// Task represents an asynchronous operation that may or may not return a result. + internal override Task WriteFloatingConstantExpressionElementAsync(IEdmFloatingConstantExpression expression) { - // Apply expressions are represented as an object. - this.jsonWriter.WriteStartObject(); + // Ut8JsonWriter can't write the Infinity double, + // it throws ".NET number values such as positive and negative infinity cannot be written as valid JSON." + if (double.IsNegativeInfinity(expression.Value)) + { + this.jsonWriter.WriteStringValue("-INF"); + } + else if (double.IsPositiveInfinity(expression.Value)) + { + this.jsonWriter.WriteStringValue("INF"); + } + else if (double.IsNaN(expression.Value)) + { + this.jsonWriter.WriteStringValue("NaN"); + } + else + { + this.jsonWriter.WriteNumberValue(expression.Value); + } + + return Task.CompletedTask; + } + + /// + /// Writes Function Application Header. + /// + /// The Edm Apply Expression. + internal override void WriteFunctionApplicationElementHeader(IEdmApplyExpression expression) + { + // Apply expressions are represented as an object. + this.jsonWriter.WriteStartObject(); + + // a member $Apply + this.jsonWriter.WritePropertyName("$Apply"); + + // whose value is an array of annotation expressions. + this.jsonWriter.WriteStartArray(); + } + + /// + /// Asynchronously Writes Function Application Header. + /// + /// The Edm Apply Expression. + /// Task represents an asynchronous operation that may or may not return a result. + internal override Task WriteFunctionApplicationElementHeaderAsync(IEdmApplyExpression expression) + { + // Apply expressions are represented as an object. + this.jsonWriter.WriteStartObject(); // a member $Apply this.jsonWriter.WritePropertyName("$Apply"); // whose value is an array of annotation expressions. this.jsonWriter.WriteStartArray(); + + return Task.CompletedTask; } + /// + /// Writes Function Application End. + /// + /// The Edm Apply Expression. internal override void WriteFunctionApplicationElementEnd(IEdmApplyExpression expression) { // End of $Apply @@ -997,12 +2457,52 @@ internal override void WriteFunctionApplicationElementEnd(IEdmApplyExpression ex this.jsonWriter.WriteEndObject(); } + /// + /// Asynchronously Writes Function Application End. + /// + /// The Edm Apply Expression. + /// Task represents an asynchronous operation that may or may not return a result. + internal override Task WriteFunctionApplicationElementEndAsync(IEdmApplyExpression expression) + { + // End of $Apply + this.jsonWriter.WriteEndArray(); + + // a member $Function whose value is a string containing the qualified name of the client-side function to be applied + this.jsonWriter.WriteRequiredProperty("$Function", expression.AppliedFunction.FullName()); + + // End of Annotation Value. + this.jsonWriter.WriteEndObject(); + + return Task.CompletedTask; + } + + /// + /// Writes Guid Constant Expression. + /// + /// The Edm Guid Constant Expression. internal override void WriteGuidConstantExpressionElement(IEdmGuidConstantExpression expression) { // Guid expressions are represented as a string containing the guid value. this.jsonWriter.WriteStringValue(EdmValueWriter.GuidAsXml(expression.Value)); } + /// + /// Asynchronously Writes Guid Constant Expression. + /// + /// The Edm Guid Constant Expression. + /// Task represents an asynchronous operation that may or may not return a result. + internal override Task WriteGuidConstantExpressionElementAsync(IEdmGuidConstantExpression expression) + { + // Guid expressions are represented as a string containing the guid value. + this.jsonWriter.WriteStringValue(EdmValueWriter.GuidAsXml(expression.Value)); + + return Task.CompletedTask; + } + + /// + /// Writes Integer Constant Expression. + /// + /// The Edm Integer Constant Expression. internal override void WriteIntegerConstantExpressionElement(IEdmIntegerConstantExpression expression) { // Integer expressions are represented as a numbers or strings depending on the media type parameter IEEE754Compatible. @@ -1016,6 +2516,30 @@ internal override void WriteIntegerConstantExpressionElement(IEdmIntegerConstant } } + /// + /// Asynchronously Writes Integer Constant Expression. + /// + /// The Edm Integer Constant Expression. + /// Task represents an asynchronous operation that may or may not return a result. + internal override Task WriteIntegerConstantExpressionElementAsync(IEdmIntegerConstantExpression expression) + { + // Integer expressions are represented as a numbers or strings depending on the media type parameter IEEE754Compatible. + if (this.settings.IsIeee754Compatible) + { + this.jsonWriter.WriteStringValue(expression.Value.ToString(CultureInfo.InvariantCulture)); + } + else + { + this.jsonWriter.WriteNumberValue(expression.Value); + } + + return Task.CompletedTask; + } + + /// + /// Writes Path Expression. + /// + /// The Edm Path Expression. internal override void WritePathExpressionElement(IEdmPathExpression expression) { // We consider base Path expression is a value path, not same as NavigationPropertyPath, we don't have a value path. @@ -1025,24 +2549,95 @@ internal override void WritePathExpressionElement(IEdmPathExpression expression) this.jsonWriter.WriteEndObject(); } + /// + /// Asynchronously Writes Path Expression. + /// + /// The Edm Path Expression. + /// Task represents an asynchronous operation that may or may not return a result. + internal override Task WritePathExpressionElementAsync(IEdmPathExpression expression) + { + // We consider base Path expression is a value path, not same as NavigationPropertyPath, we don't have a value path. + // Path expressions are represented as an object with a single member $Path whose value is a string containing a path. + this.jsonWriter.WriteStartObject(); + this.jsonWriter.WriteRequiredProperty("$Path", PathAsXml(expression.PathSegments)); + this.jsonWriter.WriteEndObject(); + + return Task.CompletedTask; + } + + /// + /// Writes Property Path Expression. + /// + /// The Edm Path Expression. internal override void WritePropertyPathExpressionElement(IEdmPathExpression expression) { // Navigation property path expressions are represented as a string containing a path. this.jsonWriter.WriteStringValue(PathAsXml(expression.PathSegments)); } + /// + /// Asynchronously Writes Property Path Expression. + /// + /// The Edm Path Expression. + /// Task represents an asynchronous operation that may or may not return a result. + internal override Task WritePropertyPathExpressionElementAsync(IEdmPathExpression expression) + { + // Navigation property path expressions are represented as a string containing a path. + this.jsonWriter.WriteStringValue(PathAsXml(expression.PathSegments)); + + return Task.CompletedTask; + } + + /// + /// Writes Navigation Property Path Expression. + /// + /// The Edm Path Expression. internal override void WriteNavigationPropertyPathExpressionElement(IEdmPathExpression expression) { // Property path expressions are represented as a string containing a path. this.jsonWriter.WriteStringValue(PathAsXml(expression.PathSegments)); } + /// + /// Asynchronously Writes Navigation Property Path Expression. + /// + /// The Edm Path Expression. + /// Task represents an asynchronous operation that may or may not return a result. + internal override Task WriteNavigationPropertyPathExpressionElementAsync(IEdmPathExpression expression) + { + // Property path expressions are represented as a string containing a path. + this.jsonWriter.WriteStringValue(PathAsXml(expression.PathSegments)); + + return Task.CompletedTask; + } + + /// + /// Writes Annotation Path Expression. + /// + /// The Edm Path Expression. internal override void WriteAnnotationPathExpressionElement(IEdmPathExpression expression) { // Annotation path expressions are represented as a string containing a path. this.jsonWriter.WriteStringValue(PathAsXml(expression.PathSegments)); } + /// + /// Asynchronously Writes Annotation Path Expression. + /// + /// The Edm Path Expression. + /// Task represents an asynchronous operation that may or may not return a result. + internal override Task WriteAnnotationPathExpressionElementAsync(IEdmPathExpression expression) + { + // Annotation path expressions are represented as a string containing a path. + this.jsonWriter.WriteStringValue(PathAsXml(expression.PathSegments)); + + return Task.CompletedTask; + } + + /// + /// Writes 'If' Expression Header. + /// + /// The If Expression. internal override void WriteIfExpressionElementHeader(IEdmIfExpression expression) { // Is-of expressions are represented as an object @@ -1055,6 +2650,29 @@ internal override void WriteIfExpressionElementHeader(IEdmIfExpression expressio this.jsonWriter.WriteStartArray(); } + /// + /// Asynchronously Writes 'If' Expression Header. + /// + /// The If Expression. + /// Task represents an asynchronous operation that may or may not return a result. + internal override Task WriteIfExpressionElementHeaderAsync(IEdmIfExpression expression) + { + // Is-of expressions are represented as an object + this.jsonWriter.WriteStartObject(); + + // Conditional expressions are represented as an object with a member $If + this.jsonWriter.WritePropertyName("$If"); + + // whose value is an array of two or three annotation expressions + this.jsonWriter.WriteStartArray(); + + return Task.CompletedTask; + } + + /// + /// Writes 'If' Expression End. + /// + /// The Edm If Expression. internal override void WriteIfExpressionElementEnd(IEdmIfExpression expression) { this.jsonWriter.WriteEndArray(); @@ -1062,16 +2680,66 @@ internal override void WriteIfExpressionElementEnd(IEdmIfExpression expression) this.jsonWriter.WriteEndObject(); } + /// + /// Asynchronously Writes 'If' Expression End. + /// + /// The Edm If Expression. + /// Task represents an asynchronous operation that may or may not return a result. + internal override Task WriteIfExpressionElementEndAsync(IEdmIfExpression expression) + { + this.jsonWriter.WriteEndArray(); + + this.jsonWriter.WriteEndObject(); + + return Task.CompletedTask; + } + + /// + /// Writes Collection Expression Header. + /// + /// The Edm Collection Expression. internal override void WriteCollectionExpressionElementHeader(IEdmCollectionExpression expression) { this.jsonWriter.WriteStartArray(); } + /// + /// Asynchronously Writes Collection Expression Header. + /// + /// The Edm Collection Expression. + /// Task represents an asynchronous operation that may or may not return a result. + internal override Task WriteCollectionExpressionElementHeaderAsync(IEdmCollectionExpression expression) + { + this.jsonWriter.WriteStartArray(); + + return Task.CompletedTask; + } + + /// + /// Writes Collection Expression End. + /// + /// The Edm Collection Expression. internal override void WriteCollectionExpressionElementEnd(IEdmCollectionExpression expression) { this.jsonWriter.WriteEndArray(); } + /// + /// Asynchronously Writes Collection Expression End. + /// + /// The Edm Collection Expression. + /// Task represents an asynchronous operation that may or may not return a result. + internal override Task WriteCollectionExpressionElementEndAsync(IEdmCollectionExpression expression) + { + this.jsonWriter.WriteEndArray(); + + return Task.CompletedTask; + } + + /// + /// Writes Labeled Header. + /// + /// The Edm Labeled Expression. internal override void WriteLabeledElementHeader(IEdmLabeledExpression labeledElement) { // Labeled element expressions are represented as an object with a member $LabeledElement whose value is an annotation expression. @@ -1084,6 +2752,29 @@ internal override void WriteLabeledElementHeader(IEdmLabeledExpression labeledEl this.jsonWriter.WritePropertyName("$LabeledElement"); } + /// + /// Asynchronously Writes Labeled Header. + /// + /// The Edm Labeled Expression. + /// Task represents an asynchronous operation that may or may not return a result. + internal override Task WriteLabeledElementHeaderAsync(IEdmLabeledExpression labeledElement) + { + // Labeled element expressions are represented as an object with a member $LabeledElement whose value is an annotation expression. + this.jsonWriter.WriteStartObject(); + + // a member $Name whose value is a string containing the labeled element’s name + this.jsonWriter.WriteRequiredProperty("$Name", labeledElement.Name); + + // an object with a member $LabeledElement + this.jsonWriter.WritePropertyName("$LabeledElement"); + + return Task.CompletedTask; + } + + /// + /// Write Labeled Expression Reference. + /// + /// The Edm Labeled Expression Reference. internal override void WriteLabeledExpressionReferenceExpression(IEdmLabeledExpressionReferenceExpression labeledExpressionReference) { // Labeled element reference expressions are represented as an object @@ -1101,12 +2792,58 @@ internal override void WriteLabeledExpressionReferenceExpression(IEdmLabeledExpr this.jsonWriter.WriteEndObject(); } + /// + /// Asynchronously Writes Labeled Expression Reference. + /// + /// The Edm Labeled Expression Reference. + /// Task represents an asynchronous operation that may or may not return a result. + internal override Task WriteLabeledExpressionReferenceExpressionAsync(IEdmLabeledExpressionReferenceExpression labeledExpressionReference) + { + // Labeled element reference expressions are represented as an object + this.jsonWriter.WriteStartObject(); + + // with a member $LabeledElementReference + this.jsonWriter.WritePropertyName("$LabeledElementReference"); + + // Whose value is a string containing an qualified name. + // Here's the problem that we don't have the namespace for the labeled expression, + // Even though we can get the namespace from the up-level schema, we can't query it here. + // So, leave it using the name. + this.jsonWriter.WriteStringValue(labeledExpressionReference.ReferencedLabeledExpression.Name); + + this.jsonWriter.WriteEndObject(); + + return Task.CompletedTask; + } + + /// + /// Write Time Of Day Constant Expression. + /// + /// The Edm TimeOfDay Constant Expression. internal override void WriteTimeOfDayConstantExpressionElement(IEdmTimeOfDayConstantExpression expression) { // Time-of-day expressions are represented as a string containing the time-of-day value. this.jsonWriter.WriteStringValue(EdmValueWriter.TimeOfDayAsXml(expression.Value)); } + /// + /// Asynchronously Writes Time Of Day Constant Expression. + /// + /// The Edm TimeOfDay Constant Expression. + /// Task represents an asynchronous operation that may or may not return a result. + internal override Task WriteTimeOfDayConstantExpressionElementAsync(IEdmTimeOfDayConstantExpression expression) + { + // Time-of-day expressions are represented as a string containing the time-of-day value. + this.jsonWriter.WriteStringValue(EdmValueWriter.TimeOfDayAsXml(expression.Value)); + + return Task.CompletedTask; + } + + /// + /// Writes Is Type Expression Header. + /// + /// The Edm IsOf Type Expression. + /// Is line type or not. internal override void WriteIsTypeExpressionElementHeader(IEdmIsTypeExpression expression, bool inlineType) { // Is-of expressions are represented as an object @@ -1117,6 +2854,29 @@ internal override void WriteIsTypeExpressionElementHeader(IEdmIsTypeExpression e this.jsonWriter.WritePropertyName("$IsOf"); } + /// + /// Asynchronously Writes Is Type Expression Header. + /// + /// The Edm IsOf Type Expression. + /// Is line type or not. + /// Task represents an asynchronous operation that may or may not return a result. + internal override Task WriteIsTypeExpressionElementHeaderAsync(IEdmIsTypeExpression expression, bool inlineType) + { + // Is-of expressions are represented as an object + this.jsonWriter.WriteStartObject(); + + // a member $IsOf whose value is an annotation expression + // fix it using $IsOf, not using $IsType + this.jsonWriter.WritePropertyName("$IsOf"); + + return Task.CompletedTask; + } + + /// + /// Write 'Is Of' Expression Type. + /// + /// The Edm IsOf Type Expression. + /// Is line type or not. internal override void WriteIsOfExpressionType(IEdmIsTypeExpression expression, bool inlineType) { if (inlineType) @@ -1125,11 +2885,28 @@ internal override void WriteIsOfExpressionType(IEdmIsTypeExpression expression, } } + /// + /// Asynchronously Writes 'Is Of' Expression Type. + /// + /// The Edm IsOf Type Expression. + /// Is line type or not. + /// Task represents an asynchronous operation that may or may not return a result. + internal override Task WriteIsOfExpressionTypeAsync(IEdmIsTypeExpression expression, bool inlineType) + { + if (inlineType) + { + return WriteTypeReferenceAsync(expression.Type, "None"); + } + + return Task.CompletedTask; + } + /// /// Cast /// /// The cast expression /// Is inline type. + /// Task represents an asynchronous operation that may or may not return a result. internal override void WriteCastExpressionElementHeader(IEdmCastExpression expression, bool inlineType) { // Cast expressions are represented as an object with a member $Cast whose value is an annotation expression, @@ -1138,11 +2915,45 @@ internal override void WriteCastExpressionElementHeader(IEdmCastExpression expre this.jsonWriter.WritePropertyName("$Cast"); } + /// + /// Asynchronously Writes Cast Expression Header. + /// + /// The cast expression + /// Is inline type. + /// Task represents an asynchronous operation that may or may not return a result. + internal override Task WriteCastExpressionElementHeaderAsync(IEdmCastExpression expression, bool inlineType) + { + // Cast expressions are represented as an object with a member $Cast whose value is an annotation expression, + this.jsonWriter.WriteStartObject(); + + this.jsonWriter.WritePropertyName("$Cast"); + + return Task.CompletedTask; + } + + /// + /// Writes Cast Expression End. + /// + /// The Edm cast expression + /// Is inline type. internal override void WriteCastExpressionElementEnd(IEdmCastExpression expression, bool inlineType) { this.jsonWriter.WriteEndObject(); } + /// + /// Asynchronously Writes Cast Expression End. + /// + /// The Edm cast expression + /// Is inline type. + /// Task represents an asynchronous operation that may or may not return a result. + internal override Task WriteCastExpressionElementEndAsync(IEdmCastExpression expression, bool inlineType) + { + this.jsonWriter.WriteEndObject(); + + return Task.CompletedTask; + } + internal override void WriteCastExpressionType(IEdmCastExpression expression, bool inlineType) { if (inlineType) @@ -1151,6 +2962,22 @@ internal override void WriteCastExpressionType(IEdmCastExpression expression, bo } } + /// + /// Asynchronously Writes Cast Expression Type. + /// + /// The Edm cast expression + /// Is inline type. + /// Task represents an asynchronous operation that may or may not return a result. + internal override Task WriteCastExpressionTypeAsync(IEdmCastExpression expression, bool inlineType) + { + if (inlineType) + { + return WriteTypeReferenceAsync(expression.Type, "None"); + } + + return Task.CompletedTask; + } + /// /// Enumeration Member Expression /// @@ -1161,6 +2988,18 @@ internal override void WriteEnumMemberExpressionElement(IEdmEnumMemberExpression this.jsonWriter.WriteStringValue(EnumMemberExpressionAsJson(expression.EnumMembers)); } + /// + /// Asynchronously Enumeration Member Expression + /// + /// The Edm Enumeration member expression. + internal override Task WriteEnumMemberExpressionElementAsync(IEdmEnumMemberExpression expression) + { + // Enumeration member expressions are represented as a string containing the numeric or symbolic enumeration value. + this.jsonWriter.WriteStringValue(EnumMemberExpressionAsJson(expression.EnumMembers)); + + return Task.CompletedTask; + } + /// /// Write Type Definition Object /// @@ -1180,6 +3019,28 @@ internal override void WriteTypeDefinitionElementHeader(IEdmTypeDefinition typeD this.jsonWriter.WriteRequiredProperty("$UnderlyingType", typeDefinition.UnderlyingType, TypeDefinitionAsJson); } + /// + /// Asynchronously Writes Type Definition Object + /// + /// The Edm type definition. + /// Task represents an asynchronous operation that may or may not return a result. + internal override Task WriteTypeDefinitionElementHeaderAsync(IEdmTypeDefinition typeDefinition) + { + // A type definition is represented as a member of the schema object whose name is the unqualified name of the type definition + this.jsonWriter.WritePropertyName(typeDefinition.Name); + + // whose value is an object. + this.jsonWriter.WriteStartObject(); + + // The type definition object MUST contain the member $Kind with a string value of TypeDefinition + this.jsonWriter.WriteRequiredProperty("$Kind", CsdlConstants.Element_TypeDefinition); + + // The type definition object MUST contain he member $UnderlyingType. + this.jsonWriter.WriteRequiredProperty("$UnderlyingType", typeDefinition.UnderlyingType, TypeDefinitionAsJson); + + return Task.CompletedTask; + } + /// /// Start $NavigationPropertyBinding /// @@ -1193,6 +3054,22 @@ internal override void WriteNavigationPropertyBindingsBegin(IEnumerable + /// Asynchronously Writes Navigation Property Bindings Begin $NavigationPropertyBinding + /// + /// The collection the bindings. + /// Task represents an asynchronous operation that may or may not return a result. + internal override Task WriteNavigationPropertyBindingsBeginAsync(IEnumerable bindings) + { + // It MAY contain the member $NavigationPropertyBinding + this.jsonWriter.WritePropertyName("$NavigationPropertyBinding"); + + // whose value is an object. + this.jsonWriter.WriteStartObject(); + + return Task.CompletedTask; + } + /// /// End $NavigationPropertyBinding /// @@ -1202,10 +3079,22 @@ internal override void WriteNavigationPropertyBindingsEnd(IEnumerable + /// Asynchronously Writes Navigation Property Bindings End $NavigationPropertyBinding + /// + /// The collection the bindings. + /// Task represents an asynchronous operation that may or may not return a result. + internal override Task WriteNavigationPropertyBindingsEndAsync(IEnumerable bindings) + { + this.jsonWriter.WriteEndObject(); + + return Task.CompletedTask; + } + /// /// Write the navigation property binding member in $NavigationPropertyBinding object. /// - /// + /// The Edm Navigation Property binding. internal override void WriteNavigationPropertyBinding(IEdmNavigationPropertyBinding binding) { // whose name is the navigation property binding path. @@ -1223,16 +3112,73 @@ internal override void WriteNavigationPropertyBinding(IEdmNavigationPropertyBind } } + /// + /// Asynchronously Writes the navigation property binding member in $NavigationPropertyBinding object. + /// + /// The Edm Navigation Property binding. + /// Task represents an asynchronous operation that may or may not return a result. + internal override Task WriteNavigationPropertyBindingAsync(IEdmNavigationPropertyBinding binding) + { + // whose name is the navigation property binding path. + this.jsonWriter.WritePropertyName(binding.Path.Path); + + // whose value is a string containing the navigation property binding target. + if (binding.Target is IEdmContainedEntitySet containedEntitySet) + { + this.jsonWriter.WriteStringValue(containedEntitySet.Path.Path); + } + else + { + this.jsonWriter.WriteStringValue(binding.Target.Name); + } + + return Task.CompletedTask; + } + + /// + /// Writes End Element. + /// internal override void WriteEndElement() { this.jsonWriter.WriteEndObject(); } + /// + /// Asynchronously Writes End Element. + /// + /// Task represents an asynchronous operation that may or may not return a result. + internal override Task WriteEndElementAsync() + { + this.jsonWriter.WriteEndObject(); + + return Task.CompletedTask; + } + + /// + /// Writes Array End Element. + /// internal override void WriteArrayEndElement() { this.jsonWriter.WriteEndArray(); } + /// + /// Asynchronously Writes Array End Element. + /// + /// Task represents an asynchronous operation that may or may not return a result. + internal override Task WriteArrayEndElementAsync() + { + this.jsonWriter.WriteEndArray(); + + return Task.CompletedTask; + } + + /// + /// Writes Operation Import Attributes. + /// + /// The Edm Operation import. + /// Operation Attribute name. + /// internal override void WriteOperationImportAttributes(IEdmOperationImport operationImport, string operationAttributeName) { if (operationImport.EntitySet != null) @@ -1249,6 +3195,30 @@ internal override void WriteOperationImportAttributes(IEdmOperationImport operat } } + /// + /// Asynchronously Writes Operation Import Attributes. + /// + /// The Edm Operation import. + /// Operation Attribute name. + /// + /// Task represents an asynchronous operation that may or may not return a result. + internal override Task WriteOperationImportAttributesAsync(IEdmOperationImport operationImport, string operationAttributeName) + { + if (operationImport.EntitySet != null) + { + if (operationImport.EntitySet is IEdmPathExpression pathExpression) + { + this.jsonWriter.WriteRequiredProperty("$EntitySet", pathExpression.PathSegments, PathAsXml); + } + else + { + throw new InvalidOperationException(Strings.EdmModel_Validator_Semantic_OperationImportEntitySetExpressionIsInvalid(operationImport.Name)); + } + } + + return Task.CompletedTask; + } + private static string BinaryToString(IEdmBinaryConstantExpression binary) { // whose value is a string containing the base64url-encoded binary value. diff --git a/src/Microsoft.OData.Edm/Csdl/Serialization/EdmModelCsdlSchemaWriter.cs b/src/Microsoft.OData.Edm/Csdl/Serialization/EdmModelCsdlSchemaWriter.cs index a299e6a77e..f796e4360e 100644 --- a/src/Microsoft.OData.Edm/Csdl/Serialization/EdmModelCsdlSchemaWriter.cs +++ b/src/Microsoft.OData.Edm/Csdl/Serialization/EdmModelCsdlSchemaWriter.cs @@ -7,6 +7,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; using Microsoft.OData.Edm.Vocabularies; namespace Microsoft.OData.Edm.Csdl.Serialization @@ -47,227 +48,372 @@ internal EdmModelCsdlSchemaWriter(IEdmModel model, Version edmVersion) public VersioningDictionary NamespaceAliasMappings { get; } internal abstract void WriteReferenceElementHeader(IEdmReference reference); + internal abstract Task WriteReferenceElementHeaderAsync(IEdmReference reference); internal abstract void WriteReferenceElementEnd(IEdmReference reference); + internal abstract Task WriteReferenceElementEndAsync(IEdmReference reference); internal abstract void WritIncludeElementHeader(IEdmInclude include); + internal abstract Task WritIncludeElementHeaderAsync(IEdmInclude include); internal abstract void WriteIncludeElementEnd(IEdmInclude include); + internal abstract Task WriteIncludeElementEndAsync(IEdmInclude include); internal abstract void WriteTermElementHeader(IEdmTerm term, bool inlineType); + internal abstract Task WriteTermElementHeaderAsync(IEdmTerm term, bool inlineType); internal abstract void WriteComplexTypeElementHeader(IEdmComplexType complexType); + internal abstract Task WriteComplexTypeElementHeaderAsync(IEdmComplexType complexType); internal abstract void WriteEntityTypeElementHeader(IEdmEntityType entityType); + internal abstract Task WriteEntityTypeElementHeaderAsync(IEdmEntityType entityType); internal abstract void WriteEnumTypeElementHeader(IEdmEnumType enumType); + internal abstract Task WriteEnumTypeElementHeaderAsync(IEdmEnumType enumType); internal abstract void WriteEnumTypeElementEnd(IEdmEnumType enumType); + internal abstract Task WriteEnumTypeElementEndAsync(IEdmEnumType enumType); internal abstract void WriteEntityContainerElementHeader(IEdmEntityContainer container); + internal abstract Task WriteEntityContainerElementHeaderAsync(IEdmEntityContainer container); internal abstract void WriteEntitySetElementHeader(IEdmEntitySet entitySet); + internal abstract Task WriteEntitySetElementHeaderAsync(IEdmEntitySet entitySet); internal abstract void WriteSingletonElementHeader(IEdmSingleton singleton); + internal abstract Task WriteSingletonElementHeaderAsync(IEdmSingleton singleton); internal abstract void WriteDeclaredKeyPropertiesElementHeader(); + internal abstract Task WriteDeclaredKeyPropertiesElementHeaderAsync(); internal abstract void WritePropertyRefElement(IEdmStructuralProperty property); + internal abstract Task WritePropertyRefElementAsync(IEdmStructuralProperty property); internal abstract void WriteNavigationPropertyElementHeader(IEdmNavigationProperty property); + internal abstract Task WriteNavigationPropertyElementHeaderAsync(IEdmNavigationProperty property); internal abstract void WriteNavigationOnDeleteActionElement(EdmOnDeleteAction operationAction); + internal abstract Task WriteNavigationOnDeleteActionElementAsync(EdmOnDeleteAction operationAction); internal abstract void WriteSchemaElementHeader(EdmSchema schema, string alias, IEnumerable> mappings); + internal abstract Task WriteSchemaElementHeaderAsync(EdmSchema schema, string alias, IEnumerable> mappings); internal abstract void WriteAnnotationsElementHeader(KeyValuePair> annotationsForTarget); + internal abstract Task WriteAnnotationsElementHeaderAsync(KeyValuePair> annotationsForTarget); internal virtual void WriteOutOfLineAnnotationsBegin(IEnumerable>> outOfLineAnnotations) { // nothing here } + internal virtual Task WriteOutOfLineAnnotationsBeginAsync(IEnumerable>> outOfLineAnnotations) + { + return Task.FromResult(0); + } + internal virtual void WriteOutOfLineAnnotationsEnd(IEnumerable>> outOfLineAnnotations) { // nothing here } + internal virtual Task WriteOutOfLineAnnotationsEndAsync(IEnumerable>> outOfLineAnnotations) + { + return Task.FromResult(0); + } + internal abstract void WriteStructuralPropertyElementHeader(IEdmStructuralProperty property, bool inlineType); + internal abstract Task WriteStructuralPropertyElementHeaderAsync(IEdmStructuralProperty property, bool inlineType); internal abstract void WriteEnumMemberElementHeader(IEdmEnumMember member); + internal abstract Task WriteEnumMemberElementHeaderAsync(IEdmEnumMember member); internal virtual void WriteEnumMemberElementEnd(IEdmEnumMember member) { // Nothing here } + internal virtual Task WriteEnumMemberElementEndAsync(IEdmEnumMember member) + { + return Task.FromResult(0); + } + internal abstract void WriteNavigationPropertyBinding(IEdmNavigationPropertyBinding binding); + internal abstract Task WriteNavigationPropertyBindingAsync(IEdmNavigationPropertyBinding binding); internal virtual void WriteNavigationPropertyBindingsBegin(IEnumerable bindings) { // Nothing here } + internal virtual Task WriteNavigationPropertyBindingsBeginAsync(IEnumerable bindings) + { + return Task.FromResult(0); + } + internal virtual void WriteNavigationPropertyBindingsEnd(IEnumerable bindings) { // Nothing here } + internal virtual Task WriteNavigationPropertyBindingsEndAsync(IEnumerable bindings) + { + return Task.FromResult(0); + } + internal abstract void WriteNullableAttribute(IEdmTypeReference reference); + internal abstract Task WriteNullableAttributeAsync(IEdmTypeReference reference); internal abstract void WriteTypeDefinitionAttributes(IEdmTypeDefinitionReference reference); + internal abstract Task WriteTypeDefinitionAttributesAsync(IEdmTypeDefinitionReference reference); internal abstract void WriteBinaryTypeAttributes(IEdmBinaryTypeReference reference); + internal abstract Task WriteBinaryTypeAttributesAsync(IEdmBinaryTypeReference reference); internal abstract void WriteDecimalTypeAttributes(IEdmDecimalTypeReference reference); + internal abstract Task WriteDecimalTypeAttributesAsync(IEdmDecimalTypeReference reference); internal abstract void WriteSpatialTypeAttributes(IEdmSpatialTypeReference reference); + internal abstract Task WriteSpatialTypeAttributesAsync(IEdmSpatialTypeReference reference); internal abstract void WriteStringTypeAttributes(IEdmStringTypeReference reference); + internal abstract Task WriteStringTypeAttributesAsync(IEdmStringTypeReference reference); internal abstract void WriteTemporalTypeAttributes(IEdmTemporalTypeReference reference); + internal abstract Task WriteTemporalTypeAttributesAsync(IEdmTemporalTypeReference reference); internal virtual void WriteReferentialConstraintBegin(IEdmReferentialConstraint referentialConstraint) { // nothing here } + internal virtual Task WriteReferentialConstraintBeginAsync(IEdmReferentialConstraint referentialConstraint) + { + return Task.FromResult(0); + } + internal virtual void WriteReferentialConstraintEnd(IEdmReferentialConstraint referentialConstraint) { // nothing here } + internal virtual Task WriteReferentialConstraintEndAsync(IEdmReferentialConstraint referentialConstraint) + { + return Task.FromResult(0); + } + internal abstract void WriteReferentialConstraintPair(EdmReferentialConstraintPropertyPair pair); + internal abstract Task WriteReferentialConstraintPairAsync(EdmReferentialConstraintPropertyPair pair); internal abstract void WriteAnnotationStringAttribute(IEdmDirectValueAnnotation annotation); + internal abstract Task WriteAnnotationStringAttributeAsync(IEdmDirectValueAnnotation annotation); internal abstract void WriteAnnotationStringElement(IEdmDirectValueAnnotation annotation); + internal abstract Task WriteAnnotationStringElementAsync(IEdmDirectValueAnnotation annotation); internal abstract void WriteActionElementHeader(IEdmAction action); + internal abstract Task WriteActionElementHeaderAsync(IEdmAction action); internal abstract void WriteFunctionElementHeader(IEdmFunction function); + internal abstract Task WriteFunctionElementHeaderAsync(IEdmFunction function); internal abstract void WriteReturnTypeElementHeader(IEdmOperationReturn operationReturn); + internal abstract Task WriteReturnTypeElementHeaderAsync(IEdmOperationReturn operationReturn); internal abstract void WriteTypeAttribute(IEdmTypeReference typeReference); + internal abstract Task WriteTypeAttributeAsync(IEdmTypeReference typeReference); internal abstract void WriteActionImportElementHeader(IEdmActionImport actionImport); + internal abstract Task WriteActionImportElementHeaderAsync(IEdmActionImport actionImport); internal abstract void WriteFunctionImportElementHeader(IEdmFunctionImport functionImport); + internal abstract Task WriteFunctionImportElementHeaderAsync(IEdmFunctionImport functionImport); internal abstract void WriteOperationParameterElementHeader(IEdmOperationParameter parameter, bool inlineType); + internal abstract Task WriteOperationParameterElementHeaderAsync(IEdmOperationParameter parameter, bool inlineType); internal abstract void WriteOperationParameterEndElement(IEdmOperationParameter parameter); + internal abstract Task WriteOperationParameterEndElementAsync(IEdmOperationParameter parameter); internal abstract void WriteCollectionTypeElementHeader(IEdmCollectionType collectionType, bool inlineType); + internal abstract Task WriteCollectionTypeElementHeaderAsync(IEdmCollectionType collectionType, bool inlineType); internal abstract void WriteInlineExpression(IEdmExpression expression); + internal abstract Task WriteInlineExpressionAsync(IEdmExpression expression); internal abstract void WriteVocabularyAnnotationElementHeader(IEdmVocabularyAnnotation annotation, bool isInline); + internal abstract Task WriteVocabularyAnnotationElementHeaderAsync(IEdmVocabularyAnnotation annotation, bool isInline); internal abstract void WriteVocabularyAnnotationElementEnd(IEdmVocabularyAnnotation annotation, bool isInline); + internal abstract Task WriteVocabularyAnnotationElementEndAsync(IEdmVocabularyAnnotation annotation, bool isInline); internal abstract void WritePropertyValueElementHeader(IEdmPropertyConstructor value, bool isInline); + internal abstract Task WritePropertyValueElementHeaderAsync(IEdmPropertyConstructor value, bool isInline); internal abstract void WriteRecordExpressionElementHeader(IEdmRecordExpression expression); + internal abstract Task WriteRecordExpressionElementHeaderAsync(IEdmRecordExpression expression); internal abstract void WritePropertyConstructorElementHeader(IEdmPropertyConstructor constructor, bool isInline); + internal abstract Task WritePropertyConstructorElementHeaderAsync(IEdmPropertyConstructor constructor, bool isInline); internal abstract void WritePropertyConstructorElementEnd(IEdmPropertyConstructor constructor); + internal abstract Task WritePropertyConstructorElementEndAsync(IEdmPropertyConstructor constructor); internal abstract void WriteStringConstantExpressionElement(IEdmStringConstantExpression expression); + internal abstract Task WriteStringConstantExpressionElementAsync(IEdmStringConstantExpression expression); internal abstract void WriteBinaryConstantExpressionElement(IEdmBinaryConstantExpression expression); + internal abstract Task WriteBinaryConstantExpressionElementAsync(IEdmBinaryConstantExpression expression); internal abstract void WriteBooleanConstantExpressionElement(IEdmBooleanConstantExpression expression); + internal abstract Task WriteBooleanConstantExpressionElementAsync(IEdmBooleanConstantExpression expression); internal abstract void WriteNullConstantExpressionElement(IEdmNullExpression expression); + internal abstract Task WriteNullConstantExpressionElementAsync(IEdmNullExpression expression); internal abstract void WriteDateConstantExpressionElement(IEdmDateConstantExpression expression); + internal abstract Task WriteDateConstantExpressionElementAsync(IEdmDateConstantExpression expression); internal virtual void WriteSchemaOperationsHeader(KeyValuePair> operations) { // nothing here } + internal virtual Task WriteSchemaOperationsHeaderAsync(KeyValuePair> operations) + { + return Task.FromResult(0); + } + internal virtual void WriteSchemaOperationsEnd(KeyValuePair> operation) { // nothing here } + internal virtual Task WriteSchemaOperationsEndAsync(KeyValuePair> operation) + { + return Task.FromResult(0); + } + internal virtual void WriteOperationParametersBegin(IEnumerable parameters) { // nothing here } + internal virtual Task WriteOperationParametersBeginAsync(IEnumerable parameters) + { + return Task.FromResult(0); + } + internal virtual void WriteOperationParametersEnd(IEnumerable parameters) { // nothing here } + internal virtual Task WriteOperationParametersEndAsync(IEnumerable parameters) + { + return Task.FromResult(0); + } + internal abstract void WriteDateTimeOffsetConstantExpressionElement(IEdmDateTimeOffsetConstantExpression expression); + internal abstract Task WriteDateTimeOffsetConstantExpressionElementAsync(IEdmDateTimeOffsetConstantExpression expression); internal abstract void WriteDurationConstantExpressionElement(IEdmDurationConstantExpression expression); + internal abstract Task WriteDurationConstantExpressionElementAsync(IEdmDurationConstantExpression expression); internal abstract void WriteDecimalConstantExpressionElement(IEdmDecimalConstantExpression expression); + internal abstract Task WriteDecimalConstantExpressionElementAsync(IEdmDecimalConstantExpression expression); internal abstract void WriteFloatingConstantExpressionElement(IEdmFloatingConstantExpression expression); + internal abstract Task WriteFloatingConstantExpressionElementAsync(IEdmFloatingConstantExpression expression); internal abstract void WriteFunctionApplicationElementHeader(IEdmApplyExpression expression); + internal abstract Task WriteFunctionApplicationElementHeaderAsync(IEdmApplyExpression expression); internal abstract void WriteFunctionApplicationElementEnd(IEdmApplyExpression expression); + internal abstract Task WriteFunctionApplicationElementEndAsync(IEdmApplyExpression expression); internal abstract void WriteGuidConstantExpressionElement(IEdmGuidConstantExpression expression); + internal abstract Task WriteGuidConstantExpressionElementAsync(IEdmGuidConstantExpression expression); internal abstract void WriteIntegerConstantExpressionElement(IEdmIntegerConstantExpression expression); + internal abstract Task WriteIntegerConstantExpressionElementAsync(IEdmIntegerConstantExpression expression); internal abstract void WritePathExpressionElement(IEdmPathExpression expression); + internal abstract Task WritePathExpressionElementAsync(IEdmPathExpression expression); internal abstract void WriteAnnotationPathExpressionElement(IEdmPathExpression expression); + internal abstract Task WriteAnnotationPathExpressionElementAsync(IEdmPathExpression expression); internal abstract void WritePropertyPathExpressionElement(IEdmPathExpression expression); + internal abstract Task WritePropertyPathExpressionElementAsync(IEdmPathExpression expression); internal abstract void WriteNavigationPropertyPathExpressionElement(IEdmPathExpression expression); + internal abstract Task WriteNavigationPropertyPathExpressionElementAsync(IEdmPathExpression expression); internal abstract void WriteIfExpressionElementHeader(IEdmIfExpression expression); + internal abstract Task WriteIfExpressionElementHeaderAsync(IEdmIfExpression expression); internal abstract void WriteIfExpressionElementEnd(IEdmIfExpression expression); + internal abstract Task WriteIfExpressionElementEndAsync(IEdmIfExpression expression); internal abstract void WriteCollectionExpressionElementHeader(IEdmCollectionExpression expression); + internal abstract Task WriteCollectionExpressionElementHeaderAsync(IEdmCollectionExpression expression); internal abstract void WriteCollectionExpressionElementEnd(IEdmCollectionExpression expression); + internal abstract Task WriteCollectionExpressionElementEndAsync(IEdmCollectionExpression expression); internal abstract void WriteLabeledElementHeader(IEdmLabeledExpression labeledElement); + internal abstract Task WriteLabeledElementHeaderAsync(IEdmLabeledExpression labeledElement); internal abstract void WriteLabeledExpressionReferenceExpression(IEdmLabeledExpressionReferenceExpression labeledExpressionReference); + internal abstract Task WriteLabeledExpressionReferenceExpressionAsync(IEdmLabeledExpressionReferenceExpression labeledExpressionReference); internal abstract void WriteTimeOfDayConstantExpressionElement(IEdmTimeOfDayConstantExpression expression); + internal abstract Task WriteTimeOfDayConstantExpressionElementAsync(IEdmTimeOfDayConstantExpression expression); internal abstract void WriteIsTypeExpressionElementHeader(IEdmIsTypeExpression expression, bool inlineType); + internal abstract Task WriteIsTypeExpressionElementHeaderAsync(IEdmIsTypeExpression expression, bool inlineType); internal virtual void WriteIsOfExpressionType(IEdmIsTypeExpression expression, bool inlineType) { // nothing here } + internal virtual Task WriteIsOfExpressionTypeAsync(IEdmIsTypeExpression expression, bool inlineType) + { + return Task.FromResult(0); + } + internal abstract void WriteCastExpressionElementHeader(IEdmCastExpression expression, bool inlineType); + internal abstract Task WriteCastExpressionElementHeaderAsync(IEdmCastExpression expression, bool inlineType); internal abstract void WriteCastExpressionElementEnd(IEdmCastExpression expression, bool inlineType); + internal abstract Task WriteCastExpressionElementEndAsync(IEdmCastExpression expression, bool inlineType); internal virtual void WriteCastExpressionType(IEdmCastExpression expression, bool inlineType) { // nothing here } + internal virtual Task WriteCastExpressionTypeAsync(IEdmCastExpression expression, bool inlineType) + { + return Task.FromResult(0); + } + internal abstract void WriteEnumMemberExpressionElement(IEdmEnumMemberExpression expression); + internal abstract Task WriteEnumMemberExpressionElementAsync(IEdmEnumMemberExpression expression); internal abstract void WriteTypeDefinitionElementHeader(IEdmTypeDefinition typeDefinition); + internal abstract Task WriteTypeDefinitionElementHeaderAsync(IEdmTypeDefinition typeDefinition); internal abstract void WriteEndElement(); + internal abstract Task WriteEndElementAsync(); internal abstract void WriteArrayEndElement(); + internal abstract Task WriteArrayEndElementAsync(); internal abstract void WriteOperationElementAttributes(IEdmOperation operation); + internal abstract Task WriteOperationElementAttributesAsync(IEdmOperation operation); internal abstract void WriteOperationImportAttributes(IEdmOperationImport operationImport, string operationAttributeName); + internal abstract Task WriteOperationImportAttributesAsync(IEdmOperationImport operationImport, string operationAttributeName); internal static string PathAsXml(IEnumerable path) { diff --git a/src/Microsoft.OData.Edm/Csdl/Serialization/EdmModelCsdlSchemaXmlWriter.cs b/src/Microsoft.OData.Edm/Csdl/Serialization/EdmModelCsdlSchemaXmlWriter.cs index 075c68d7f8..3f563b8302 100644 --- a/src/Microsoft.OData.Edm/Csdl/Serialization/EdmModelCsdlSchemaXmlWriter.cs +++ b/src/Microsoft.OData.Edm/Csdl/Serialization/EdmModelCsdlSchemaXmlWriter.cs @@ -9,6 +9,7 @@ using System.Diagnostics; using System.Globalization; using System.Linq; +using System.Threading.Tasks; using System.Xml; using Microsoft.OData.Edm.Csdl.CsdlSemantics; using Microsoft.OData.Edm.Csdl.Parsing.Ast; @@ -32,6 +33,10 @@ internal EdmModelCsdlSchemaXmlWriter(IEdmModel model, XmlWriter xmlWriter, Versi this.edmxNamespace = CsdlConstants.SupportedEdmxVersions[edmVersion]; } + /// + /// Writes Reference element header. + /// + /// edmx:reference element internal override void WriteReferenceElementHeader(IEdmReference reference) { // e.g. @@ -39,11 +44,41 @@ internal override void WriteReferenceElementHeader(IEdmReference reference) this.WriteRequiredAttribute(CsdlConstants.Attribute_Uri, reference.Uri, EdmValueWriter.UriAsXml); } + /// + /// Asynchronously writes Reference element header. + /// + /// edmx:reference element + /// Task represents an asynchronous operation. + internal override async Task WriteReferenceElementHeaderAsync(IEdmReference reference) + { + // e.g. + await this.xmlWriter.WriteStartElementAsync(CsdlConstants.Prefix_Edmx, CsdlConstants.Element_Reference, this.edmxNamespace).ConfigureAwait(false); + await this.WriteRequiredAttributeAsync(CsdlConstants.Attribute_Uri, reference.Uri, EdmValueWriter.UriAsXml).ConfigureAwait(false); + } + + /// + /// Writes the reference element end. + /// + /// edmx:reference element internal override void WriteReferenceElementEnd(IEdmReference reference) { this.xmlWriter.WriteEndElement(); } + /// + /// Asynchronously writes the reference element end. + /// + /// edmx:reference element + /// Task represents an asynchronous operation. + internal override Task WriteReferenceElementEndAsync(IEdmReference reference) + { + return this.xmlWriter.WriteEndElementAsync(); + } + + /// + /// Writes the Include element header. + /// + /// The Edm Include information. internal override void WritIncludeElementHeader(IEdmInclude include) { // e.g. @@ -52,11 +87,42 @@ internal override void WritIncludeElementHeader(IEdmInclude include) this.WriteRequiredAttribute(CsdlConstants.Attribute_Alias, include.Alias, EdmValueWriter.StringAsXml); } + /// + /// Asynchronously writes the Include element header. + /// + /// The Edm Include information. + /// Task represents an asynchronous operation. + internal override async Task WritIncludeElementHeaderAsync(IEdmInclude include) + { + // e.g. + await this.xmlWriter.WriteStartElementAsync(CsdlConstants.Prefix_Edmx, CsdlConstants.Element_Include, this.edmxNamespace).ConfigureAwait(false); + await this.WriteRequiredAttributeAsync(CsdlConstants.Attribute_Namespace, include.Namespace, EdmValueWriter.StringAsXml).ConfigureAwait(false); + await this.WriteRequiredAttributeAsync(CsdlConstants.Attribute_Alias, include.Alias, EdmValueWriter.StringAsXml).ConfigureAwait(false); + } + + /// + /// Writes the IncludeAnnotations end. + /// + /// The Edm Include information. internal override void WriteIncludeElementEnd(IEdmInclude include) { this.xmlWriter.WriteEndElement(); } + /// + /// Asynchronously writes the IncludeAnnotations end. + /// + /// The Edm Include information. + /// Task represents an asynchronous operation. + internal override Task WriteIncludeElementEndAsync(IEdmInclude include) + { + return this.xmlWriter.WriteEndElementAsync(); + } + + /// + /// Writes the IncludeAnnotations element. + /// + /// The Edm Include annotations. internal void WriteIncludeAnnotationsElement(IEdmIncludeAnnotations includeAnnotations) { // e.g. @@ -67,6 +133,26 @@ internal void WriteIncludeAnnotationsElement(IEdmIncludeAnnotations includeAnnot this.xmlWriter.WriteEndElement(); } + /// + /// Asynchronously writes the IncludeAnnotations element. + /// + /// The Edm Include annotations. + /// Task represents an asynchronous operation. + internal async Task WriteIncludeAnnotationsElementAsync(IEdmIncludeAnnotations includeAnnotations) + { + // e.g. + await this.xmlWriter.WriteStartElementAsync(CsdlConstants.Prefix_Edmx, CsdlConstants.Element_IncludeAnnotations, this.edmxNamespace).ConfigureAwait(false); + await this.WriteRequiredAttributeAsync(CsdlConstants.Attribute_TermNamespace, includeAnnotations.TermNamespace, EdmValueWriter.StringAsXml).ConfigureAwait(false); + await this.WriteOptionalAttributeAsync(CsdlConstants.Attribute_Qualifier, includeAnnotations.Qualifier, EdmValueWriter.StringAsXml).ConfigureAwait(false); + await this.WriteOptionalAttributeAsync(CsdlConstants.Attribute_TargetNamespace, includeAnnotations.TargetNamespace, EdmValueWriter.StringAsXml).ConfigureAwait(false); + await this.xmlWriter.WriteEndElementAsync().ConfigureAwait(false); + } + + /// + /// Writes Term element header. + /// + /// The Edm term. + /// Is inline type or not. internal override void WriteTermElementHeader(IEdmTerm term, bool inlineType) { this.xmlWriter.WriteStartElement(CsdlConstants.Element_Term); @@ -80,6 +166,28 @@ internal override void WriteTermElementHeader(IEdmTerm term, bool inlineType) this.WriteOptionalAttribute(CsdlConstants.Attribute_AppliesTo, term.AppliesTo, EdmValueWriter.StringAsXml); } + /// + /// Asynchronously writes Term element header. + /// + /// The Edm term. + /// Is inline type or not. + /// Task represents an asynchronous operation. + internal override async Task WriteTermElementHeaderAsync(IEdmTerm term, bool inlineType) + { + await this.xmlWriter.WriteStartElementAsync(null, CsdlConstants.Element_Term, null).ConfigureAwait(false); + await this.WriteRequiredAttributeAsync(CsdlConstants.Attribute_Name, term.Name, EdmValueWriter.StringAsXml).ConfigureAwait(false); + if (inlineType && term.Type != null) + { + await this.WriteRequiredAttributeAsync(CsdlConstants.Attribute_Type, term.Type, this.TypeReferenceAsXml).ConfigureAwait(false); + } + await this.WriteOptionalAttributeAsync(CsdlConstants.Attribute_DefaultValue, term.DefaultValue, EdmValueWriter.StringAsXml).ConfigureAwait(false); + await this.WriteOptionalAttributeAsync(CsdlConstants.Attribute_AppliesTo, term.AppliesTo, EdmValueWriter.StringAsXml).ConfigureAwait(false); + } + + /// + /// Writes the ComplexType element header. + /// + /// The Edm Complex type. internal override void WriteComplexTypeElementHeader(IEdmComplexType complexType) { this.xmlWriter.WriteStartElement(CsdlConstants.Element_ComplexType); @@ -89,6 +197,24 @@ internal override void WriteComplexTypeElementHeader(IEdmComplexType complexType this.WriteOptionalAttribute(CsdlConstants.Attribute_OpenType, complexType.IsOpen, CsdlConstants.Default_OpenType, EdmValueWriter.BooleanAsXml); } + /// + /// Asynchronously writes the ComplexType element header. + /// + /// The Edm Complex type. + /// Task represents an asynchronous operation. + internal override async Task WriteComplexTypeElementHeaderAsync(IEdmComplexType complexType) + { + await this.xmlWriter.WriteStartElementAsync(null, CsdlConstants.Element_ComplexType, null).ConfigureAwait(false); + await this.WriteRequiredAttributeAsync(CsdlConstants.Attribute_Name, complexType.Name, EdmValueWriter.StringAsXml).ConfigureAwait(false); + await this.WriteOptionalAttributeAsync(CsdlConstants.Attribute_BaseType, complexType.BaseComplexType(), this.TypeDefinitionAsXml).ConfigureAwait(false); + await this.WriteOptionalAttributeAsync(CsdlConstants.Attribute_Abstract, complexType.IsAbstract, CsdlConstants.Default_Abstract, EdmValueWriter.BooleanAsXml).ConfigureAwait(false); + await this.WriteOptionalAttributeAsync(CsdlConstants.Attribute_OpenType, complexType.IsOpen, CsdlConstants.Default_OpenType, EdmValueWriter.BooleanAsXml).ConfigureAwait(false); + } + + /// + /// Writes the EnumType element header. + /// + /// The Edm enumaration type. internal override void WriteEnumTypeElementHeader(IEdmEnumType enumType) { this.xmlWriter.WriteStartElement(CsdlConstants.Element_EnumType); @@ -101,11 +227,46 @@ internal override void WriteEnumTypeElementHeader(IEdmEnumType enumType) this.WriteOptionalAttribute(CsdlConstants.Attribute_IsFlags, enumType.IsFlags, CsdlConstants.Default_IsFlags, EdmValueWriter.BooleanAsXml); } + /// + /// Asynchronously writes the EnumType element header. + /// + /// The Edm enumaration type. + /// Task represents an asynchronous operation. + internal override async Task WriteEnumTypeElementHeaderAsync(IEdmEnumType enumType) + { + await this.xmlWriter.WriteStartElementAsync(null, CsdlConstants.Element_EnumType, null).ConfigureAwait(false); + await this.WriteRequiredAttributeAsync(CsdlConstants.Attribute_Name, enumType.Name, EdmValueWriter.StringAsXml).ConfigureAwait(false); + if (enumType.UnderlyingType.PrimitiveKind != EdmPrimitiveTypeKind.Int32) + { + await this.WriteRequiredAttributeAsync(CsdlConstants.Attribute_UnderlyingType, enumType.UnderlyingType, this.TypeDefinitionAsXml).ConfigureAwait(false); + } + + await this.WriteOptionalAttributeAsync(CsdlConstants.Attribute_IsFlags, enumType.IsFlags, CsdlConstants.Default_IsFlags, EdmValueWriter.BooleanAsXml).ConfigureAwait(false); + } + + /// + /// Writes the EnumContainer element end. + /// + /// The Edm enumaration type. internal override void WriteEnumTypeElementEnd(IEdmEnumType enumType) { this.WriteEndElement(); } + /// + /// Asynchronously writes the EnumContainer element end. + /// + /// The Edm enumaration type. + /// Task represents an asynchronous operation. + internal override Task WriteEnumTypeElementEndAsync(IEdmEnumType enumType) + { + return this.WriteEndElementAsync(); + } + + /// + /// Writes the EntityContainer element header. + /// + /// The Edm Entity container. internal override void WriteEntityContainerElementHeader(IEdmEntityContainer container) { this.xmlWriter.WriteStartElement(CsdlConstants.Element_EntityContainer); @@ -118,6 +279,25 @@ internal override void WriteEntityContainerElementHeader(IEdmEntityContainer con } } + /// + /// Asynchronously writes the EntityContainer element header. + /// + /// The Edm Entity container. + /// Task represents an asynchronous operation. + internal override async Task WriteEntityContainerElementHeaderAsync(IEdmEntityContainer container) + { + await this.xmlWriter.WriteStartElementAsync(null, CsdlConstants.Element_EntityContainer, null).ConfigureAwait(false); + await this.WriteRequiredAttributeAsync(CsdlConstants.Attribute_Name, container.Name, EdmValueWriter.StringAsXml).ConfigureAwait(false); + if (container is CsdlSemanticsEntityContainer tmp && tmp.Element is CsdlEntityContainer csdlContainer) + { + await this.WriteOptionalAttributeAsync(CsdlConstants.Attribute_Extends, csdlContainer.Extends, EdmValueWriter.StringAsXml).ConfigureAwait(false); + } + } + + /// + /// Writes the EntitySet element header. + /// + /// The Edm Entity set. internal override void WriteEntitySetElementHeader(IEdmEntitySet entitySet) { this.xmlWriter.WriteStartElement(CsdlConstants.Element_EntitySet); @@ -125,6 +305,22 @@ internal override void WriteEntitySetElementHeader(IEdmEntitySet entitySet) this.WriteRequiredAttribute(CsdlConstants.Attribute_EntityType, entitySet.EntityType().FullName(), EdmValueWriter.StringAsXml); } + /// + /// Asynchronously writes the EntitySet element header. + /// + /// The Edm Entity set. + /// Task represents an asynchronous operation. + internal override async Task WriteEntitySetElementHeaderAsync(IEdmEntitySet entitySet) + { + await this.xmlWriter.WriteStartElementAsync(null, CsdlConstants.Element_EntitySet, null).ConfigureAwait(false); + await this.WriteRequiredAttributeAsync(CsdlConstants.Attribute_Name, entitySet.Name, EdmValueWriter.StringAsXml).ConfigureAwait(false); + await this.WriteRequiredAttributeAsync(CsdlConstants.Attribute_EntityType, entitySet.EntityType().FullName(), EdmValueWriter.StringAsXml).ConfigureAwait(false); + } + + /// + /// Writes the Singleton element header. + /// + /// The Edm singleton. internal override void WriteSingletonElementHeader(IEdmSingleton singleton) { this.xmlWriter.WriteStartElement(CsdlConstants.Element_Singleton); @@ -132,6 +328,22 @@ internal override void WriteSingletonElementHeader(IEdmSingleton singleton) this.WriteRequiredAttribute(CsdlConstants.Attribute_Type, singleton.EntityType().FullName(), EdmValueWriter.StringAsXml); } + /// + /// Asynchronously writes the Singleton element header. + /// + /// The Edm singleton. + /// Task represents an asynchronous operation. + internal override async Task WriteSingletonElementHeaderAsync(IEdmSingleton singleton) + { + await this.xmlWriter.WriteStartElementAsync(null, CsdlConstants.Element_Singleton, null).ConfigureAwait(false); + await this.WriteRequiredAttributeAsync(CsdlConstants.Attribute_Name, singleton.Name, EdmValueWriter.StringAsXml).ConfigureAwait(false); + await this.WriteRequiredAttributeAsync(CsdlConstants.Attribute_Type, singleton.EntityType().FullName(), EdmValueWriter.StringAsXml).ConfigureAwait(false); + } + + /// + /// Writes the EntityType element header. + /// + /// The Edm entity type. internal override void WriteEntityTypeElementHeader(IEdmEntityType entityType) { this.xmlWriter.WriteStartElement(CsdlConstants.Element_EntityType); @@ -145,11 +357,45 @@ internal override void WriteEntityTypeElementHeader(IEdmEntityType entityType) this.WriteOptionalAttribute(CsdlConstants.Attribute_HasStream, writeHasStream, CsdlConstants.Default_HasStream, EdmValueWriter.BooleanAsXml); } + /// + /// Asynchronously writes the EntityType element header. + /// + /// The Edm entity type. + /// Task represents an asynchronous operation. + internal override async Task WriteEntityTypeElementHeaderAsync(IEdmEntityType entityType) + { + await this.xmlWriter.WriteStartElementAsync(null, CsdlConstants.Element_EntityType, null).ConfigureAwait(false); + await this.WriteRequiredAttributeAsync(CsdlConstants.Attribute_Name, entityType.Name, EdmValueWriter.StringAsXml).ConfigureAwait(false); + await this.WriteOptionalAttributeAsync(CsdlConstants.Attribute_BaseType, entityType.BaseEntityType(), this.TypeDefinitionAsXml).ConfigureAwait(false); + await this.WriteOptionalAttributeAsync(CsdlConstants.Attribute_Abstract, entityType.IsAbstract, CsdlConstants.Default_Abstract, EdmValueWriter.BooleanAsXml).ConfigureAwait(false); + await this.WriteOptionalAttributeAsync(CsdlConstants.Attribute_OpenType, entityType.IsOpen, CsdlConstants.Default_OpenType, EdmValueWriter.BooleanAsXml).ConfigureAwait(false); + + // HasStream value should be inherited. Only have it on base type is sufficient. + bool writeHasStream = entityType.HasStream && (entityType.BaseEntityType() == null || (entityType.BaseEntityType() != null && !entityType.BaseEntityType().HasStream)); + await this.WriteOptionalAttributeAsync(CsdlConstants.Attribute_HasStream, writeHasStream, CsdlConstants.Default_HasStream, EdmValueWriter.BooleanAsXml).ConfigureAwait(false); + } + + /// + /// Asynchronously writes the key properties element header. + /// internal override void WriteDeclaredKeyPropertiesElementHeader() { this.xmlWriter.WriteStartElement(CsdlConstants.Element_Key); } + /// + /// Asynchronously writes the key properties element header. + /// + /// Task represents an asynchronous operation. + internal override Task WriteDeclaredKeyPropertiesElementHeaderAsync() + { + return this.xmlWriter.WriteStartElementAsync(null, CsdlConstants.Element_Key, null); + } + + /// + /// Writes the PropertyRef element. + /// + /// The Edm Structural Property. internal override void WritePropertyRefElement(IEdmStructuralProperty property) { this.xmlWriter.WriteStartElement(CsdlConstants.Element_PropertyRef); @@ -157,6 +403,22 @@ internal override void WritePropertyRefElement(IEdmStructuralProperty property) this.WriteEndElement(); } + /// + /// Asynchronously writes the PropertyRef element. + /// + /// The Edm Structural Property. + /// Task represents an asynchronous operation. + internal override async Task WritePropertyRefElementAsync(IEdmStructuralProperty property) + { + await this.xmlWriter.WriteStartElementAsync(null, CsdlConstants.Element_PropertyRef, null).ConfigureAwait(false); + await this.WriteRequiredAttributeAsync(CsdlConstants.Attribute_Name, property.Name, EdmValueWriter.StringAsXml).ConfigureAwait(false); + await this.WriteEndElementAsync().ConfigureAwait(false); + } + + /// + /// Writes the NavigationProperty element header. + /// + /// The Edm navigation property. internal override void WriteNavigationPropertyElementHeader(IEdmNavigationProperty property) { this.xmlWriter.WriteStartElement(CsdlConstants.Element_NavigationProperty); @@ -176,6 +438,34 @@ internal override void WriteNavigationPropertyElementHeader(IEdmNavigationProper this.WriteOptionalAttribute(CsdlConstants.Attribute_ContainsTarget, property.ContainsTarget, CsdlConstants.Default_ContainsTarget, EdmValueWriter.BooleanAsXml); } + /// + /// Asynchronously writes the NavigationProperty element header. + /// + /// The Edm navigation property. + /// Task represents an asynchronous operation. + internal override async Task WriteNavigationPropertyElementHeaderAsync(IEdmNavigationProperty property) + { + await this.xmlWriter.WriteStartElementAsync(null, CsdlConstants.Element_NavigationProperty, null).ConfigureAwait(false); + await this.WriteRequiredAttributeAsync(CsdlConstants.Attribute_Name, property.Name, EdmValueWriter.StringAsXml).ConfigureAwait(false); + + await this.WriteRequiredAttributeAsync(CsdlConstants.Attribute_Type, property.Type, this.TypeReferenceAsXml).ConfigureAwait(false); + if (!property.Type.IsCollection() && property.Type.IsNullable != CsdlConstants.Default_Nullable) + { + await this.WriteRequiredAttributeAsync(CsdlConstants.Attribute_Nullable, property.Type.IsNullable, EdmValueWriter.BooleanAsXml).ConfigureAwait(false); + } + + if (property.Partner != null) + { + await this.WriteRequiredAttributeAsync(CsdlConstants.Attribute_Partner, property.GetPartnerPath()?.Path, EdmValueWriter.StringAsXml).ConfigureAwait(false); + } + + await this.WriteOptionalAttributeAsync(CsdlConstants.Attribute_ContainsTarget, property.ContainsTarget, CsdlConstants.Default_ContainsTarget, EdmValueWriter.BooleanAsXml).ConfigureAwait(false); + } + + /// + /// Writes the NavigationOnDeleteAction element. + /// + /// The Edm OnDelete action. internal override void WriteNavigationOnDeleteActionElement(EdmOnDeleteAction operationAction) { this.xmlWriter.WriteStartElement(CsdlConstants.Element_OnDelete); @@ -183,6 +473,24 @@ internal override void WriteNavigationOnDeleteActionElement(EdmOnDeleteAction op this.WriteEndElement(); } + /// + /// Asynchronously writes the NavigationOnDeleteAction element. + /// + /// The Edm OnDelete action. + /// Task represents an asynchronous operation. + internal override async Task WriteNavigationOnDeleteActionElementAsync(EdmOnDeleteAction operationAction) + { + await this.xmlWriter.WriteStartElementAsync(null, CsdlConstants.Element_OnDelete, null).ConfigureAwait(false); + await this.WriteRequiredAttributeAsync(CsdlConstants.Attribute_Action, operationAction.ToString(), EdmValueWriter.StringAsXml).ConfigureAwait(false); + await this.WriteEndElementAsync().ConfigureAwait(false); + } + + /// + /// Writes the Schema element header. + /// + /// The Edm schema. + /// The alias. + /// Collection of mappings. internal override void WriteSchemaElementHeader(EdmSchema schema, string alias, IEnumerable> mappings) { string xmlNamespace = GetCsdlNamespace(EdmVersion); @@ -198,12 +506,55 @@ internal override void WriteSchemaElementHeader(EdmSchema schema, string alias, } } + /// + /// Asynchronously writes the Schema element header. + /// + /// The Edm schema. + /// The alias. + /// Collection of mappings. + /// Task represents an asynchronous operation. + internal override async Task WriteSchemaElementHeaderAsync(EdmSchema schema, string alias, IEnumerable> mappings) + { + string xmlNamespace = GetCsdlNamespace(EdmVersion); + await this.xmlWriter.WriteStartElementAsync(null, CsdlConstants.Element_Schema, xmlNamespace).ConfigureAwait(false); + await this.WriteOptionalAttributeAsync(CsdlConstants.Attribute_Namespace, schema.Namespace, string.Empty, EdmValueWriter.StringAsXml).ConfigureAwait(false); + await this.WriteOptionalAttributeAsync(CsdlConstants.Attribute_Alias, alias, EdmValueWriter.StringAsXml).ConfigureAwait(false); + if (mappings != null) + { + foreach (KeyValuePair mapping in mappings) + { + await this.xmlWriter.WriteAttributeStringAsync(EdmConstants.XmlNamespacePrefix, mapping.Key, null, mapping.Value).ConfigureAwait(false); + } + } + } + + /// + /// Writes the Annotations element header. + /// + /// The Key/Value of Edm Vocabulary annotation. internal override void WriteAnnotationsElementHeader(KeyValuePair> annotationsForTarget) { this.xmlWriter.WriteStartElement(CsdlConstants.Element_Annotations); this.WriteRequiredAttribute(CsdlConstants.Attribute_Target, annotationsForTarget.Key, EdmValueWriter.StringAsXml); } + + /// + /// Asynchronously writes the Annotations element header. + /// + /// The Key/Value of Edm Vocabulary annotation. + /// Task represents an asynchronous operation. + internal override async Task WriteAnnotationsElementHeaderAsync(KeyValuePair> annotationsForTarget) + { + await this.xmlWriter.WriteStartElementAsync(null, CsdlConstants.Element_Annotations, null).ConfigureAwait(false); + await this.WriteRequiredAttributeAsync(CsdlConstants.Attribute_Target, annotationsForTarget.Key, EdmValueWriter.StringAsXml).ConfigureAwait(false); + } + + /// + /// Writes the StructuralProperty element header. + /// + /// The Edm Structural Property. + /// Is inline type or not. internal override void WriteStructuralPropertyElementHeader(IEdmStructuralProperty property, bool inlineType) { this.xmlWriter.WriteStartElement(CsdlConstants.Element_Property); @@ -216,6 +567,28 @@ internal override void WriteStructuralPropertyElementHeader(IEdmStructuralProper this.WriteOptionalAttribute(CsdlConstants.Attribute_DefaultValue, property.DefaultValueString, EdmValueWriter.StringAsXml); } + /// + /// Asynchronously writes the StructuralProperty element header. + /// + /// The Edm Structural Property. + /// Is inline type or not. + /// Task represents an asynchronous operation. + internal override async Task WriteStructuralPropertyElementHeaderAsync(IEdmStructuralProperty property, bool inlineType) + { + await this.xmlWriter.WriteStartElementAsync(null, CsdlConstants.Element_Property, null).ConfigureAwait(false); + await this.WriteRequiredAttributeAsync(CsdlConstants.Attribute_Name, property.Name, EdmValueWriter.StringAsXml).ConfigureAwait(false); + if (inlineType) + { + await this.WriteRequiredAttributeAsync(CsdlConstants.Attribute_Type, property.Type, this.TypeReferenceAsXml).ConfigureAwait(false); + } + + await this.WriteOptionalAttributeAsync(CsdlConstants.Attribute_DefaultValue, property.DefaultValueString, EdmValueWriter.StringAsXml).ConfigureAwait(false); + } + + /// + /// Writes Enum Member element header. + /// + /// The Edm enumeration member. internal override void WriteEnumMemberElementHeader(IEdmEnumMember member) { this.xmlWriter.WriteStartElement(CsdlConstants.Element_Member); @@ -227,16 +600,64 @@ internal override void WriteEnumMemberElementHeader(IEdmEnumMember member) } } + /// + /// Asynchronously writes enumeration member element header. + /// + /// The Edm enumeration member. + /// Task represents an asynchronous operation. + internal override async Task WriteEnumMemberElementHeaderAsync(IEdmEnumMember member) + { + await this.xmlWriter.WriteStartElementAsync(null, CsdlConstants.Element_Member, null).ConfigureAwait(false); + await this.WriteRequiredAttributeAsync(CsdlConstants.Attribute_Name, member.Name, EdmValueWriter.StringAsXml).ConfigureAwait(false); + bool? isExplicit = member.IsValueExplicit(this.Model); + if (!isExplicit.HasValue || isExplicit.Value) + { + await this.xmlWriter.WriteAttributeStringAsync(null, CsdlConstants.Attribute_Value, null, EdmValueWriter.LongAsXml(member.Value.Value)).ConfigureAwait(false); + } + } + + /// + /// Writes enumeration member element end. + /// + /// The Edm enumeration member. internal override void WriteEnumMemberElementEnd(IEdmEnumMember member) { this.xmlWriter.WriteEndElement(); } + /// + /// Asynchronously writes EnumMember element end. + /// + /// The Edm enumeration member. + /// Task represents an asynchronous operation. + internal override Task WriteEnumMemberElementEndAsync(IEdmEnumMember member) + { + return this.xmlWriter.WriteEndElementAsync(); + } + + /// + /// Writes Nullable attribute. + /// + /// The Edm type reference. internal override void WriteNullableAttribute(IEdmTypeReference reference) { this.WriteOptionalAttribute(CsdlConstants.Attribute_Nullable, reference.IsNullable, CsdlConstants.Default_Nullable, EdmValueWriter.BooleanAsXml); } + /// + /// Asynchronously writes Nullable attribute. + /// + /// The Edm type reference. + /// Task represents an asynchronous operation. + internal override Task WriteNullableAttributeAsync(IEdmTypeReference reference) + { + return this.WriteOptionalAttributeAsync(CsdlConstants.Attribute_Nullable, reference.IsNullable, CsdlConstants.Default_Nullable, EdmValueWriter.BooleanAsXml); + } + + /// + /// Writes TypeDefinition attributes. + /// + /// The Edm type defination reference. internal override void WriteTypeDefinitionAttributes(IEdmTypeDefinitionReference reference) { IEdmTypeReference actualTypeReference = reference.AsActualTypeReference(); @@ -263,6 +684,47 @@ internal override void WriteTypeDefinitionAttributes(IEdmTypeDefinitionReference } } + /// + /// Asynchronously writes TypeDefinition attributes. + /// + /// The Edm type defination reference. + /// Task represents an asynchronous operation. + internal override Task WriteTypeDefinitionAttributesAsync(IEdmTypeDefinitionReference reference) + { + IEdmTypeReference actualTypeReference = reference.AsActualTypeReference(); + + if (actualTypeReference.IsBinary()) + { + return this.WriteBinaryTypeAttributesAsync(actualTypeReference.AsBinary()); + } + + if (actualTypeReference.IsString()) + { + return this.WriteStringTypeAttributesAsync(actualTypeReference.AsString()); + } + + if (actualTypeReference.IsTemporal()) + { + return this.WriteTemporalTypeAttributesAsync(actualTypeReference.AsTemporal()); + } + + if (actualTypeReference.IsDecimal()) + { + return this.WriteDecimalTypeAttributesAsync(actualTypeReference.AsDecimal()); + } + + if (actualTypeReference.IsSpatial()) + { + return this.WriteSpatialTypeAttributesAsync(actualTypeReference.AsSpatial()); + } + + return Task.FromResult(0); + } + + /// + /// Writes BinaryType attributes. + /// + /// The Edm binary type reference. internal override void WriteBinaryTypeAttributes(IEdmBinaryTypeReference reference) { if (reference.IsUnbounded) @@ -275,6 +737,25 @@ internal override void WriteBinaryTypeAttributes(IEdmBinaryTypeReference referen } } + /// + /// Asynchronously writes BinaryType attributes. + /// + /// The Edm binary type reference. + /// Task represents an asynchronous operation. + internal override Task WriteBinaryTypeAttributesAsync(IEdmBinaryTypeReference reference) + { + if (reference.IsUnbounded) + { + return this.WriteRequiredAttributeAsync(CsdlConstants.Attribute_MaxLength, CsdlConstants.Value_Max, EdmValueWriter.StringAsXml); + } + + return this.WriteOptionalAttributeAsync(CsdlConstants.Attribute_MaxLength, reference.MaxLength, EdmValueWriter.IntAsXml); + } + + /// + /// Writes DecimalType attributes. + /// + /// The Edm Decimal type reference. internal override void WriteDecimalTypeAttributes(IEdmDecimalTypeReference reference) { this.WriteOptionalAttribute(CsdlConstants.Attribute_Precision, reference.Precision, EdmValueWriter.IntAsXml); @@ -286,6 +767,21 @@ internal override void WriteDecimalTypeAttributes(IEdmDecimalTypeReference refer this.WriteRequiredAttribute(CsdlConstants.Attribute_Scale, reference.Scale, ScaleAsXml); } + /// + /// Asynchronously writes DecimalType attributes. + /// + /// The Edm Decimal type reference. + /// Task represents an asynchronous operation. + internal override async Task WriteDecimalTypeAttributesAsync(IEdmDecimalTypeReference reference) + { + await this.WriteOptionalAttributeAsync(CsdlConstants.Attribute_Precision, reference.Precision, EdmValueWriter.IntAsXml).ConfigureAwait(false); + await this.WriteRequiredAttributeAsync(CsdlConstants.Attribute_Scale, reference.Scale, ScaleAsXml).ConfigureAwait(false); + } + + /// + /// Writes SpatialType attributes. + /// + /// The Edm Spatial type reference. internal override void WriteSpatialTypeAttributes(IEdmSpatialTypeReference reference) { if (reference.IsGeography()) @@ -298,6 +794,30 @@ internal override void WriteSpatialTypeAttributes(IEdmSpatialTypeReference refer } } + /// + /// Asynchronously writes SpatialType attributes. + /// + /// The Edm Spatial type reference. + /// Task represents an asynchronous operation. + internal override Task WriteSpatialTypeAttributesAsync(IEdmSpatialTypeReference reference) + { + if (reference.IsGeography()) + { + return this.WriteOptionalAttributeAsync(CsdlConstants.Attribute_Srid, reference.SpatialReferenceIdentifier, CsdlConstants.Default_SpatialGeographySrid, SridAsXml); + } + + if (reference.IsGeometry()) + { + return this.WriteOptionalAttributeAsync(CsdlConstants.Attribute_Srid, reference.SpatialReferenceIdentifier, CsdlConstants.Default_SpatialGeometrySrid, SridAsXml); + } + + return Task.FromResult(0); + } + + /// + /// Writes StringType attributes. + /// + /// The Edm String type reference. internal override void WriteStringTypeAttributes(IEdmStringTypeReference reference) { if (reference.IsUnbounded) @@ -315,6 +835,32 @@ internal override void WriteStringTypeAttributes(IEdmStringTypeReference referen } } + /// + /// Asynchronously writes StringType attributes. + /// + /// The Edm String type reference. + /// Task represents an asynchronous operation. + internal override async Task WriteStringTypeAttributesAsync(IEdmStringTypeReference reference) + { + if (reference.IsUnbounded) + { + await this.WriteRequiredAttributeAsync(CsdlConstants.Attribute_MaxLength, CsdlConstants.Value_Max, EdmValueWriter.StringAsXml).ConfigureAwait(false); + } + else + { + await this.WriteOptionalAttributeAsync(CsdlConstants.Attribute_MaxLength, reference.MaxLength, EdmValueWriter.IntAsXml).ConfigureAwait(false); + } + + if (reference.IsUnicode != null) + { + await this.WriteOptionalAttributeAsync(CsdlConstants.Attribute_Unicode, reference.IsUnicode, CsdlConstants.Default_IsUnicode, EdmValueWriter.BooleanAsXml).ConfigureAwait(false); + } + } + + /// + /// Writes TemporalType attributes. + /// + /// The Edm Temporal type reference. internal override void WriteTemporalTypeAttributes(IEdmTemporalTypeReference reference) { if (reference.Precision != null) @@ -323,6 +869,23 @@ internal override void WriteTemporalTypeAttributes(IEdmTemporalTypeReference ref } } + /// + /// Asynchronously writes TemporalType attributes. + /// + /// The Edm Temporal type reference. + /// Task represents an asynchronous operation. + internal override async Task WriteTemporalTypeAttributesAsync(IEdmTemporalTypeReference reference) + { + if (reference.Precision != null) + { + await this.WriteOptionalAttributeAsync(CsdlConstants.Attribute_Precision, reference.Precision, CsdlConstants.Default_TemporalPrecision, EdmValueWriter.IntAsXml).ConfigureAwait(false); + } + } + + /// + /// Writes ReferentialConstraint pair. + /// + /// The Edm Referential Constraint property pair. internal override void WriteReferentialConstraintPair(EdmReferentialConstraintPropertyPair pair) { this.xmlWriter.WriteStartElement(CsdlConstants.Element_ReferentialConstraint); @@ -340,6 +903,32 @@ internal override void WriteReferentialConstraintPair(EdmReferentialConstraintPr this.WriteEndElement(); } + /// + /// Asynchronously writes ReferentialConstraint pair. + /// + /// The Edm Referential Constraint property pair. + /// Task represents an asynchronous operation. + internal override async Task WriteReferentialConstraintPairAsync(EdmReferentialConstraintPropertyPair pair) + { + await this.xmlWriter.WriteStartElementAsync(null, CsdlConstants.Element_ReferentialConstraint, null).ConfigureAwait(false); + + // + // ... + // + // + // + // + // + // the above CategoryID is DependentProperty, ID is PrincipalProperty. + await this.WriteRequiredAttributeAsync(CsdlConstants.Attribute_Property, pair.DependentProperty.Name, EdmValueWriter.StringAsXml).ConfigureAwait(false); + await this.WriteRequiredAttributeAsync(CsdlConstants.Attribute_ReferencedProperty, pair.PrincipalProperty.Name, EdmValueWriter.StringAsXml).ConfigureAwait(false); + await this.WriteEndElementAsync().ConfigureAwait(false); + } + + /// + /// Writes Annotation string attribute. + /// + /// The Edm Direct value annotation. internal override void WriteAnnotationStringAttribute(IEdmDirectValueAnnotation annotation) { var edmValue = (IEdmPrimitiveValue)annotation.Value; @@ -349,6 +938,23 @@ internal override void WriteAnnotationStringAttribute(IEdmDirectValueAnnotation } } + /// + /// Asynchronously writes Annotation string attribute. + /// + /// The Edm Direct value annotation. + /// Task represents an asynchronous operation. + internal override async Task WriteAnnotationStringAttributeAsync(IEdmDirectValueAnnotation annotation) + { + if (annotation.Value is IEdmPrimitiveValue edmValue) + { + await this.xmlWriter.WriteAttributeStringAsync(null, annotation.Name, annotation.NamespaceUri, EdmValueWriter.PrimitiveValueAsXml(edmValue)).ConfigureAwait(false); + } + } + + /// + /// Writes Annotation string element. + /// + /// The Edm Direct value annotation. internal override void WriteAnnotationStringElement(IEdmDirectValueAnnotation annotation) { var edmValue = (IEdmPrimitiveValue)annotation.Value; @@ -358,12 +964,44 @@ internal override void WriteAnnotationStringElement(IEdmDirectValueAnnotation an } } + /// + /// Asynchronously writes Annotation string element. + /// + /// The Edm Direct value annotation. + /// Task represents an asynchronous operation. + internal override async Task WriteAnnotationStringElementAsync(IEdmDirectValueAnnotation annotation) + { + if (annotation.Value is IEdmPrimitiveValue edmValue) + { + await this.xmlWriter.WriteRawAsync(((IEdmStringValue)edmValue).Value).ConfigureAwait(false); + } + } + + /// + /// Writes Action element header. + /// + /// The Edm Action. internal override void WriteActionElementHeader(IEdmAction action) { this.xmlWriter.WriteStartElement(CsdlConstants.Element_Action); this.WriteOperationElementAttributes(action); } + /// + /// Asynchronously writes Action element header. + /// + /// The Edm Action. + /// Task represents an asynchronous operation. + internal override async Task WriteActionElementHeaderAsync(IEdmAction action) + { + await this.xmlWriter.WriteStartElementAsync(null, CsdlConstants.Element_Action, null).ConfigureAwait(false); + await this.WriteOperationElementAttributesAsync(action).ConfigureAwait(false); + } + + /// + /// Writes Function element header. + /// + /// The Edm Function. internal override void WriteFunctionElementHeader(IEdmFunction function) { this.xmlWriter.WriteStartElement(CsdlConstants.Element_Function); @@ -375,22 +1013,85 @@ internal override void WriteFunctionElementHeader(IEdmFunction function) } } + /// + /// Asynchronously writes Function element header. + /// + /// The Edm Function. + /// Task represents an asynchronous operation. + internal override async Task WriteFunctionElementHeaderAsync(IEdmFunction function) + { + await this.xmlWriter.WriteStartElementAsync(null, CsdlConstants.Element_Function, null).ConfigureAwait(false); + await this.WriteOperationElementAttributesAsync(function).ConfigureAwait(false); + + if (function.IsComposable) + { + await this.WriteOptionalAttributeAsync(CsdlConstants.Attribute_IsComposable, function.IsComposable, EdmValueWriter.BooleanAsXml).ConfigureAwait(false); + } + } + + /// + /// Writes ReturnType element header. + /// + /// The Edm Operation return. internal override void WriteReturnTypeElementHeader(IEdmOperationReturn operationReturn) { this.xmlWriter.WriteStartElement(CsdlConstants.Element_ReturnType); } + /// + /// Asynchronously writes ReturnType element header. + /// + /// The Edm Operation return. + /// Task represents an asynchronous operation. + internal override Task WriteReturnTypeElementHeaderAsync(IEdmOperationReturn operationReturn) + { + return this.xmlWriter.WriteStartElementAsync(null, CsdlConstants.Element_ReturnType, null); + } + + /// + /// Writes Type attribute. + /// + /// The Edm type reference. internal override void WriteTypeAttribute(IEdmTypeReference typeReference) { this.WriteRequiredAttribute(CsdlConstants.Attribute_Type, typeReference, this.TypeReferenceAsXml); } + /// + /// Asynchronously writes Type attribute. + /// + /// The Edm type reference. + /// Task represents an asynchronous operation. + internal override Task WriteTypeAttributeAsync(IEdmTypeReference typeReference) + { + return this.WriteRequiredAttributeAsync(CsdlConstants.Attribute_Type, typeReference, this.TypeReferenceAsXml); + } + + /// + /// Writes ActionImport element header. + /// + /// The Edm action import. internal override void WriteActionImportElementHeader(IEdmActionImport actionImport) { this.xmlWriter.WriteStartElement(CsdlConstants.Element_ActionImport); this.WriteOperationImportAttributes(actionImport, CsdlConstants.Attribute_Action); } + /// + /// Asynchronously writes ActionImport element header. + /// + /// The Edm action import. + /// Task represents an asynchronous operation. + internal override async Task WriteActionImportElementHeaderAsync(IEdmActionImport actionImport) + { + await this.xmlWriter.WriteStartElementAsync(null, CsdlConstants.Element_ActionImport, null).ConfigureAwait(false); + await this.WriteOperationImportAttributesAsync(actionImport, CsdlConstants.Attribute_Action).ConfigureAwait(false); + } + + /// + /// Writes FunctionImport element header. + /// + /// The Edm function import. internal override void WriteFunctionImportElementHeader(IEdmFunctionImport functionImport) { this.xmlWriter.WriteStartElement(CsdlConstants.Element_FunctionImport); @@ -398,6 +1099,23 @@ internal override void WriteFunctionImportElementHeader(IEdmFunctionImport funct this.WriteOptionalAttribute(CsdlConstants.Attribute_IncludeInServiceDocument, functionImport.IncludeInServiceDocument, CsdlConstants.Default_IncludeInServiceDocument, EdmValueWriter.BooleanAsXml); } + /// + /// Asynchronously writes FunctionImport element header. + /// + /// The Edm function import. + /// Task represents an asynchronous operation. + internal override async Task WriteFunctionImportElementHeaderAsync(IEdmFunctionImport functionImport) + { + await this.xmlWriter.WriteStartElementAsync(null, CsdlConstants.Element_FunctionImport, null).ConfigureAwait(false); + await this.WriteOperationImportAttributesAsync(functionImport, CsdlConstants.Attribute_Function).ConfigureAwait(false); + await this.WriteOptionalAttributeAsync(CsdlConstants.Attribute_IncludeInServiceDocument, functionImport.IncludeInServiceDocument, CsdlConstants.Default_IncludeInServiceDocument, EdmValueWriter.BooleanAsXml).ConfigureAwait(false); + } + + /// + /// Writes OperationParameter element header. + /// + /// The Edm Operation parameter + /// Is inline type or not. internal override void WriteOperationParameterElementHeader(IEdmOperationParameter parameter, bool inlineType) { this.xmlWriter.WriteStartElement(CsdlConstants.Element_Parameter); @@ -408,6 +1126,26 @@ internal override void WriteOperationParameterElementHeader(IEdmOperationParamet } } + /// + /// Asynchronously writes OperationParameter element header. + /// + /// The Edm Operation parameter + /// Is inline type or not. + /// Task represents an asynchronous operation. + internal override async Task WriteOperationParameterElementHeaderAsync(IEdmOperationParameter parameter, bool inlineType) + { + await this.xmlWriter.WriteStartElementAsync(null, CsdlConstants.Element_Parameter, null).ConfigureAwait(false); + await this.WriteRequiredAttributeAsync(CsdlConstants.Attribute_Name, parameter.Name, EdmValueWriter.StringAsXml).ConfigureAwait(false); + if (inlineType) + { + await this.WriteRequiredAttributeAsync(CsdlConstants.Attribute_Type, parameter.Type, this.TypeReferenceAsXml).ConfigureAwait(false); + } + } + + /// + /// Writes OperationParameter end element. + /// + /// The Edm operation paramater. internal override void WriteOperationParameterEndElement(IEdmOperationParameter parameter) { IEdmOptionalParameter optionalParameter = parameter as IEdmOptionalParameter; @@ -432,6 +1170,41 @@ internal override void WriteOperationParameterEndElement(IEdmOperationParameter this.WriteEndElement(); } + /// + /// Asynchronously writes OperationParameter end element. + /// + /// The Edm operation paramater. + /// Task represents an asynchronous operation. + internal override async Task WriteOperationParameterEndElementAsync(IEdmOperationParameter parameter) + { + if (parameter is IEdmOptionalParameter optionalParameter && + !(optionalParameter.VocabularyAnnotations(this.Model).Any(a => a.Term == CoreVocabularyModel.OptionalParameterTerm))) + { + var optionalValue = new EdmRecordExpression(); + + var vocabularyAnnotation = new EdmVocabularyAnnotation(parameter, CoreVocabularyModel.OptionalParameterTerm, optionalValue); + await this.WriteVocabularyAnnotationElementHeaderAsync(vocabularyAnnotation, false).ConfigureAwait(false); + + if (!string.IsNullOrEmpty(optionalParameter.DefaultValueString)) + { + var property = new EdmPropertyConstructor(CsdlConstants.Attribute_DefaultValue, new EdmStringConstant(optionalParameter.DefaultValueString)); + await this.WriteRecordExpressionElementHeaderAsync(optionalValue).ConfigureAwait(false); + await this.WritePropertyValueElementHeaderAsync(property, true).ConfigureAwait(false); + await this.WriteEndElementAsync().ConfigureAwait(false); + await this.WriteEndElementAsync().ConfigureAwait(false); + } + + await this.WriteEndElementAsync().ConfigureAwait(false); + } + + await this.WriteEndElementAsync().ConfigureAwait(false); + } + + /// + /// Writes CollectionType element header. + /// + /// The Edm Collection type. + /// Is inline type or not. internal override void WriteCollectionTypeElementHeader(IEdmCollectionType collectionType, bool inlineType) { this.xmlWriter.WriteStartElement(CsdlConstants.Element_CollectionType); @@ -458,6 +1231,25 @@ internal static bool IsUsingDefaultValue(IEdmVocabularyAnnotation annotation) return false; } + /// + /// Asynchronously writes CollectionType element header. + /// + /// The Edm Collection type. + /// Is inline type or not. + /// Task represents an asynchronous operation. + internal override async Task WriteCollectionTypeElementHeaderAsync(IEdmCollectionType collectionType, bool inlineType) + { + await this.xmlWriter.WriteStartElementAsync(null, CsdlConstants.Element_CollectionType, null).ConfigureAwait(false); + if (inlineType) + { + await this.WriteRequiredAttributeAsync(CsdlConstants.Attribute_ElementType, collectionType.ElementType, this.TypeReferenceAsXml).ConfigureAwait(false); + } + } + + /// + /// Writes Inline expression. + /// + /// The Edm expression. internal override void WriteInlineExpression(IEdmExpression expression) { IEdmPathExpression pathExpression = expression as IEdmPathExpression; @@ -514,11 +1306,76 @@ internal override void WriteInlineExpression(IEdmExpression expression) } } + /// + /// Asynchronously writes Inline expression. + /// + /// The Edm expression. + /// Task represents an asynchronous operation. + internal override Task WriteInlineExpressionAsync(IEdmExpression expression) + { + IEdmPathExpression pathExpression = expression as IEdmPathExpression; + switch (expression.ExpressionKind) + { + case EdmExpressionKind.BinaryConstant: + return this.WriteRequiredAttributeAsync(CsdlConstants.Attribute_Binary, ((IEdmBinaryConstantExpression)expression).Value, EdmValueWriter.BinaryAsXml); + case EdmExpressionKind.BooleanConstant: + return this.WriteRequiredAttributeAsync(CsdlConstants.Attribute_Bool, ((IEdmBooleanConstantExpression)expression).Value, EdmValueWriter.BooleanAsXml); + case EdmExpressionKind.DateTimeOffsetConstant: + return this.WriteRequiredAttributeAsync(CsdlConstants.Attribute_DateTimeOffset, ((IEdmDateTimeOffsetConstantExpression)expression).Value, EdmValueWriter.DateTimeOffsetAsXml); + case EdmExpressionKind.DecimalConstant: + return this.WriteRequiredAttributeAsync(CsdlConstants.Attribute_Decimal, ((IEdmDecimalConstantExpression)expression).Value, EdmValueWriter.DecimalAsXml); + case EdmExpressionKind.FloatingConstant: + return this.WriteRequiredAttributeAsync(CsdlConstants.Attribute_Float, ((IEdmFloatingConstantExpression)expression).Value, EdmValueWriter.FloatAsXml); + case EdmExpressionKind.GuidConstant: + return this.WriteRequiredAttributeAsync(CsdlConstants.Attribute_Guid, ((IEdmGuidConstantExpression)expression).Value, EdmValueWriter.GuidAsXml); + case EdmExpressionKind.IntegerConstant: + return this.WriteRequiredAttributeAsync(CsdlConstants.Attribute_Int, ((IEdmIntegerConstantExpression)expression).Value, EdmValueWriter.LongAsXml); + case EdmExpressionKind.Path: + return this.WriteRequiredAttributeAsync(CsdlConstants.Attribute_Path, pathExpression.PathSegments, PathAsXml); + case EdmExpressionKind.PropertyPath: + return this.WriteRequiredAttributeAsync(CsdlConstants.Attribute_PropertyPath, pathExpression.PathSegments, PathAsXml); + case EdmExpressionKind.NavigationPropertyPath: + return this.WriteRequiredAttributeAsync(CsdlConstants.Attribute_NavigationPropertyPath, pathExpression.PathSegments, PathAsXml); + case EdmExpressionKind.AnnotationPath: + return this.WriteRequiredAttributeAsync(CsdlConstants.Attribute_AnnotationPath, pathExpression.PathSegments, PathAsXml); + case EdmExpressionKind.StringConstant: + return this.WriteRequiredAttributeAsync(CsdlConstants.Attribute_String, ((IEdmStringConstantExpression)expression).Value, EdmValueWriter.StringAsXml); + case EdmExpressionKind.DurationConstant: + return this.WriteRequiredAttributeAsync(CsdlConstants.Attribute_Duration, ((IEdmDurationConstantExpression)expression).Value, EdmValueWriter.DurationAsXml); + case EdmExpressionKind.DateConstant: + return this.WriteRequiredAttributeAsync(CsdlConstants.Attribute_Date, ((IEdmDateConstantExpression)expression).Value, EdmValueWriter.DateAsXml); + case EdmExpressionKind.TimeOfDayConstant: + return this.WriteRequiredAttributeAsync(CsdlConstants.Attribute_TimeOfDay, ((IEdmTimeOfDayConstantExpression)expression).Value, EdmValueWriter.TimeOfDayAsXml); + default: + Debug.Assert(false, "Attempted to inline an expression that was not one of the expected inlineable types."); + return Task.FromResult(0); + } + } + + /// + /// Writes PropertyConstructor element end. + /// + /// The Edm property constructor. internal override void WritePropertyConstructorElementEnd(IEdmPropertyConstructor constructor) { this.WriteEndElement(); } + /// + /// Asynchronously writes PropertyConstructor element end. + /// + /// The Edm property constructor. + /// Task represents an asynchronous operation. + internal override Task WritePropertyConstructorElementEndAsync(IEdmPropertyConstructor constructor) + { + return this.WriteEndElementAsync(); + } + + /// + /// Writes VocabularyAnnotation element header. + /// + /// The Edm vocabulary annotation. + /// Is inline type or not. internal override void WriteVocabularyAnnotationElementHeader(IEdmVocabularyAnnotation annotation, bool isInline) { this.xmlWriter.WriteStartElement(CsdlConstants.Element_Annotation); @@ -532,11 +1389,51 @@ internal override void WriteVocabularyAnnotationElementHeader(IEdmVocabularyAnno } } + /// + /// Asynchronously writes VocabularyAnnotation element header. + /// + /// The Edm vocabulary annotation. + /// Is inline type or not. + /// Task represents an asynchronous operation. + internal override async Task WriteVocabularyAnnotationElementHeaderAsync(IEdmVocabularyAnnotation annotation, bool isInline) + { + await this.xmlWriter.WriteStartElementAsync(null, CsdlConstants.Element_Annotation, null).ConfigureAwait(false); + await this.WriteRequiredAttributeAsync(CsdlConstants.Attribute_Term, annotation.Term, this.TermAsXml).ConfigureAwait(false); + await this.WriteOptionalAttributeAsync(CsdlConstants.Attribute_Qualifier, annotation.Qualifier, EdmValueWriter.StringAsXml).ConfigureAwait(false); + + if (isInline && !IsUsingDefaultValue(annotation)) + { + // in xml format, we can (should) skip writing the expression value if it matches the term default value. + await this.WriteInlineExpressionAsync(annotation.Value).ConfigureAwait(false); + } + } + + /// + /// Writes VocabularyAnnotation element end. + /// + /// The Edm vocabulary annotation. + /// Is inline type or not. internal override void WriteVocabularyAnnotationElementEnd(IEdmVocabularyAnnotation annotation, bool isInline) { WriteEndElement(); } + /// + /// Asynchronously writes VocabularyAnnotation element end. + /// + /// The Edm vocabulary annotation. + /// Is inline type or not. + /// Task represents an asynchronous operation. + internal override Task WriteVocabularyAnnotationElementEndAsync(IEdmVocabularyAnnotation annotation, bool isInline) + { + return WriteEndElementAsync(); + } + + /// + /// Writes PropertyValue element header. + /// + /// The Edm property constructor. + /// Is inline type or not. internal override void WritePropertyValueElementHeader(IEdmPropertyConstructor value, bool isInline) { this.xmlWriter.WriteStartElement(CsdlConstants.Element_PropertyValue); @@ -547,12 +1444,48 @@ internal override void WritePropertyValueElementHeader(IEdmPropertyConstructor v } } + /// + /// Asynchronously writes PropertyValue element header. + /// + /// The Edm property constructor. + /// Is inline type or not. + /// Task represents an asynchronous operation. + internal override async Task WritePropertyValueElementHeaderAsync(IEdmPropertyConstructor value, bool isInline) + { + await this.xmlWriter.WriteStartElementAsync(null, CsdlConstants.Element_PropertyValue, null).ConfigureAwait(false); + await this.WriteRequiredAttributeAsync(CsdlConstants.Attribute_Property, value.Name, EdmValueWriter.StringAsXml).ConfigureAwait(false); + if (isInline) + { + await this.WriteInlineExpressionAsync(value.Value).ConfigureAwait(false); + } + } + + /// + /// Writes RecordExpression element header. + /// + /// The Edm Record expression. internal override void WriteRecordExpressionElementHeader(IEdmRecordExpression expression) { this.xmlWriter.WriteStartElement(CsdlConstants.Element_Record); this.WriteOptionalAttribute(CsdlConstants.Attribute_Type, expression.DeclaredType, this.TypeReferenceAsXml); } + /// + /// Asynchronously writes RecordExpression element header. + /// + /// The Edm Record expression. + /// Task represents an asynchronous operation. + internal override async Task WriteRecordExpressionElementHeaderAsync(IEdmRecordExpression expression) + { + await this.xmlWriter.WriteStartElementAsync(null, CsdlConstants.Element_Record, null).ConfigureAwait(false); + await this.WriteOptionalAttributeAsync(CsdlConstants.Attribute_Type, expression.DeclaredType, this.TypeReferenceAsXml).ConfigureAwait(false); + } + + /// + /// Writes PropertyConstructor element header. + /// + /// The Edm property constructor. + /// Is inline type or not. internal override void WritePropertyConstructorElementHeader(IEdmPropertyConstructor constructor, bool isInline) { this.xmlWriter.WriteStartElement(CsdlConstants.Element_PropertyValue); @@ -563,6 +1496,26 @@ internal override void WritePropertyConstructorElementHeader(IEdmPropertyConstru } } + /// + /// Asynchronously writes PropertyConstructor element header. + /// + /// The Edm property constructor. + /// Is inline type or not. + /// Task represents an asynchronous operation. + internal override async Task WritePropertyConstructorElementHeaderAsync(IEdmPropertyConstructor constructor, bool isInline) + { + await this.xmlWriter.WriteStartElementAsync(null, CsdlConstants.Element_PropertyValue, null).ConfigureAwait(false); + await this.WriteRequiredAttributeAsync(CsdlConstants.Attribute_Property, constructor.Name, EdmValueWriter.StringAsXml).ConfigureAwait(false); + if (isInline) + { + await this.WriteInlineExpressionAsync(constructor.Value).ConfigureAwait(false); + } + } + + /// + /// Writes StringConstantExpression element. + /// + /// The Edm String constant expression. internal override void WriteStringConstantExpressionElement(IEdmStringConstantExpression expression) { this.xmlWriter.WriteStartElement(CsdlConstants.Element_String); @@ -571,6 +1524,23 @@ internal override void WriteStringConstantExpressionElement(IEdmStringConstantEx this.WriteEndElement(); } + /// + /// Asynchronously writes StringConstantExpression element. + /// + /// The Edm String constant expression. + /// Task represents an asynchronous operation. + internal override async Task WriteStringConstantExpressionElementAsync(IEdmStringConstantExpression expression) + { + await this.xmlWriter.WriteStartElementAsync(null, CsdlConstants.Element_String, null).ConfigureAwait(false); + + await this.xmlWriter.WriteStringAsync(EdmValueWriter.StringAsXml(expression.Value)).ConfigureAwait(false); + await this.WriteEndElementAsync().ConfigureAwait(false); + } + + /// + /// Writes BinaryConstantExpression element. + /// + /// The Edm Binary constant expression. internal override void WriteBinaryConstantExpressionElement(IEdmBinaryConstantExpression expression) { this.xmlWriter.WriteStartElement(CsdlConstants.Element_Binary); @@ -578,6 +1548,22 @@ internal override void WriteBinaryConstantExpressionElement(IEdmBinaryConstantEx this.WriteEndElement(); } + /// + /// Asynchronously writes BinaryConstantExpression element. + /// + /// The Edm Binary constant expression. + /// Task represents an asynchronous operation. + internal override async Task WriteBinaryConstantExpressionElementAsync(IEdmBinaryConstantExpression expression) + { + await this.xmlWriter.WriteStartElementAsync(null, CsdlConstants.Element_Binary, null).ConfigureAwait(false); + await this.xmlWriter.WriteStringAsync(EdmValueWriter.BinaryAsXml(expression.Value)).ConfigureAwait(false); + await this.WriteEndElementAsync().ConfigureAwait(false); + } + + /// + /// Writes BooleanConstantExpression element. + /// + /// The Edm Boolean constant expression. internal override void WriteBooleanConstantExpressionElement(IEdmBooleanConstantExpression expression) { this.xmlWriter.WriteStartElement(CsdlConstants.Element_Bool); @@ -585,12 +1571,43 @@ internal override void WriteBooleanConstantExpressionElement(IEdmBooleanConstant this.WriteEndElement(); } + /// + /// Asynchronously writes BooleanConstantExpression element. + /// + /// The Edm Boolean constant expression. + /// Task represents an asynchronous operation. + internal override async Task WriteBooleanConstantExpressionElementAsync(IEdmBooleanConstantExpression expression) + { + await this.xmlWriter.WriteStartElementAsync(null, CsdlConstants.Element_Bool, null).ConfigureAwait(false); + await this.xmlWriter.WriteStringAsync(EdmValueWriter.BooleanAsXml(expression.Value)).ConfigureAwait(false); + await this.WriteEndElementAsync().ConfigureAwait(false); + } + + /// + /// Writes NullConstantExpression element. + /// + /// The Edm Null expression. internal override void WriteNullConstantExpressionElement(IEdmNullExpression expression) { this.xmlWriter.WriteStartElement(CsdlConstants.Element_Null); this.WriteEndElement(); } + /// + /// Asynchronously writes NullConstantExpression element. + /// + /// The Edm Null expression. + /// Task represents an asynchronous operation. + internal override async Task WriteNullConstantExpressionElementAsync(IEdmNullExpression expression) + { + await this.xmlWriter.WriteStartElementAsync(null, CsdlConstants.Element_Null, null).ConfigureAwait(false); + await this.WriteEndElementAsync().ConfigureAwait(false); + } + + /// + /// Writes DateConstantExpression element. + /// + /// The Edm Date constant expression. internal override void WriteDateConstantExpressionElement(IEdmDateConstantExpression expression) { this.xmlWriter.WriteStartElement(CsdlConstants.Element_Date); @@ -598,6 +1615,22 @@ internal override void WriteDateConstantExpressionElement(IEdmDateConstantExpres this.WriteEndElement(); } + /// + /// Asynchronously writes DateConstantExpression element. + /// + /// The Edm Date constant expression. + /// Task represents an asynchronous operation. + internal override async Task WriteDateConstantExpressionElementAsync(IEdmDateConstantExpression expression) + { + await this.xmlWriter.WriteStartElementAsync(null, CsdlConstants.Element_Date, null).ConfigureAwait(false); + await this.xmlWriter.WriteStringAsync(EdmValueWriter.DateAsXml(expression.Value)).ConfigureAwait(false); + await this.WriteEndElementAsync().ConfigureAwait(false); + } + + /// + /// Writes DateTimeOffsetConstantExpression element. + /// + /// The Edm DateTimeOffset constant expression. internal override void WriteDateTimeOffsetConstantExpressionElement(IEdmDateTimeOffsetConstantExpression expression) { this.xmlWriter.WriteStartElement(CsdlConstants.Element_DateTimeOffset); @@ -605,6 +1638,22 @@ internal override void WriteDateTimeOffsetConstantExpressionElement(IEdmDateTime this.WriteEndElement(); } + /// + /// Asynchronously writes DateTimeOffsetConstantExpression element. + /// + /// The Edm DateTimeOffset constant expression. + /// Task represents an asynchronous operation. + internal override async Task WriteDateTimeOffsetConstantExpressionElementAsync(IEdmDateTimeOffsetConstantExpression expression) + { + await this.xmlWriter.WriteStartElementAsync(null, CsdlConstants.Element_DateTimeOffset, null).ConfigureAwait(false); + await this.xmlWriter.WriteStringAsync(EdmValueWriter.DateTimeOffsetAsXml(expression.Value)).ConfigureAwait(false); + await this.WriteEndElementAsync().ConfigureAwait(false); + } + + /// + /// Writes DurationConstantExpression element. + /// + /// The Edm Duration constant expression. internal override void WriteDurationConstantExpressionElement(IEdmDurationConstantExpression expression) { this.xmlWriter.WriteStartElement(CsdlConstants.Element_Duration); @@ -612,6 +1661,22 @@ internal override void WriteDurationConstantExpressionElement(IEdmDurationConsta this.WriteEndElement(); } + /// + /// Asynchronously writes DurationConstantExpression element. + /// + /// The Edm Duration constant expression. + /// Task represents an asynchronous operation. + internal override async Task WriteDurationConstantExpressionElementAsync(IEdmDurationConstantExpression expression) + { + await this.xmlWriter.WriteStartElementAsync(null, CsdlConstants.Element_Duration, null).ConfigureAwait(false); + await this.xmlWriter.WriteStringAsync(EdmValueWriter.DurationAsXml(expression.Value)).ConfigureAwait(false); + await this.WriteEndElementAsync().ConfigureAwait(false); + } + + /// + /// Writes DecimalConstantExpression element. + /// + /// The Edm Decimal constant expression. internal override void WriteDecimalConstantExpressionElement(IEdmDecimalConstantExpression expression) { this.xmlWriter.WriteStartElement(CsdlConstants.Element_Decimal); @@ -619,6 +1684,22 @@ internal override void WriteDecimalConstantExpressionElement(IEdmDecimalConstant this.WriteEndElement(); } + /// + /// Asynchronously writes DecimalConstantExpression element. + /// + /// The Edm Decimal constant expression. + /// Task represents an asynchronous operation. + internal override async Task WriteDecimalConstantExpressionElementAsync(IEdmDecimalConstantExpression expression) + { + await this.xmlWriter.WriteStartElementAsync(null, CsdlConstants.Element_Decimal, null).ConfigureAwait(false); + await this.xmlWriter.WriteStringAsync(EdmValueWriter.DecimalAsXml(expression.Value)).ConfigureAwait(false); + await this.WriteEndElementAsync().ConfigureAwait(false); + } + + /// + /// Writes FloatingConstantExpression element. + /// + /// The Edm Floating constant expression. internal override void WriteFloatingConstantExpressionElement(IEdmFloatingConstantExpression expression) { this.xmlWriter.WriteStartElement(CsdlConstants.Element_Float); @@ -626,17 +1707,62 @@ internal override void WriteFloatingConstantExpressionElement(IEdmFloatingConsta this.WriteEndElement(); } + /// + /// Asynchronously writes FloatingConstantExpression element. + /// + /// The Edm Floating constant expression. + /// Task represents an asynchronous operation. + internal override async Task WriteFloatingConstantExpressionElementAsync(IEdmFloatingConstantExpression expression) + { + await this.xmlWriter.WriteStartElementAsync(null, CsdlConstants.Element_Float, null).ConfigureAwait(false); + await this.xmlWriter.WriteStringAsync(EdmValueWriter.FloatAsXml(expression.Value)).ConfigureAwait(false); + await this.WriteEndElementAsync().ConfigureAwait(false); + } + + /// + /// Writes FunctionApplication element header. + /// + /// The Edm apply expression. internal override void WriteFunctionApplicationElementHeader(IEdmApplyExpression expression) { this.xmlWriter.WriteStartElement(CsdlConstants.Element_Apply); this.WriteRequiredAttribute(CsdlConstants.Attribute_Function, expression.AppliedFunction, this.FunctionAsXml); } + /// + /// Asynchronously writes FunctionApplication element header. + /// + /// The Edm apply expression. + /// Task represents an asynchronous operation. + internal override async Task WriteFunctionApplicationElementHeaderAsync(IEdmApplyExpression expression) + { + await this.xmlWriter.WriteStartElementAsync(null, CsdlConstants.Element_Apply, null).ConfigureAwait(false); + await this.WriteRequiredAttributeAsync(CsdlConstants.Attribute_Function, expression.AppliedFunction, this.FunctionAsXml).ConfigureAwait(false); + } + + /// + /// Writes FunctionApplication element end. + /// + /// The Edm Apply expression. internal override void WriteFunctionApplicationElementEnd(IEdmApplyExpression expression) { this.WriteEndElement(); } + /// + /// Asynchronously writes FunctionApplication element end. + /// + /// The Edm Apply expression. + /// Task represents an asynchronous operation. + internal override Task WriteFunctionApplicationElementEndAsync(IEdmApplyExpression expression) + { + return this.WriteEndElementAsync(); + } + + /// + /// Writes GuidConstantExpression element. + /// + /// The Edm Guid constant expression. internal override void WriteGuidConstantExpressionElement(IEdmGuidConstantExpression expression) { this.xmlWriter.WriteStartElement(CsdlConstants.Element_Guid); @@ -644,6 +1770,22 @@ internal override void WriteGuidConstantExpressionElement(IEdmGuidConstantExpres this.WriteEndElement(); } + /// + /// Asynchronously writes GuidConstantExpression element. + /// + /// The Edm Guid constant expression. + /// Task represents an asynchronous operation. + internal override async Task WriteGuidConstantExpressionElementAsync(IEdmGuidConstantExpression expression) + { + await this.xmlWriter.WriteStartElementAsync(null, CsdlConstants.Element_Guid, null).ConfigureAwait(false); + await this.xmlWriter.WriteStringAsync(EdmValueWriter.GuidAsXml(expression.Value)).ConfigureAwait(false); + await this.WriteEndElementAsync().ConfigureAwait(false); + } + + /// + /// Writes IntegerConstant Expression element. + /// + /// The Edm Integer constant expression. internal override void WriteIntegerConstantExpressionElement(IEdmIntegerConstantExpression expression) { this.xmlWriter.WriteStartElement(CsdlConstants.Element_Int); @@ -651,6 +1793,22 @@ internal override void WriteIntegerConstantExpressionElement(IEdmIntegerConstant this.WriteEndElement(); } + /// + /// Asynchronously writes IntegerConstant Expression element. + /// + /// The Edm Integer constant expression. + /// Task represents an asynchronous operation. + internal override async Task WriteIntegerConstantExpressionElementAsync(IEdmIntegerConstantExpression expression) + { + await this.xmlWriter.WriteStartElementAsync(null, CsdlConstants.Element_Int, null).ConfigureAwait(false); + await this.xmlWriter.WriteStringAsync(EdmValueWriter.LongAsXml(expression.Value)).ConfigureAwait(false); + await this.WriteEndElementAsync().ConfigureAwait(false); + } + + /// + /// Writes Path Expression element. + /// + /// The Edm path expression. internal override void WritePathExpressionElement(IEdmPathExpression expression) { this.xmlWriter.WriteStartElement(CsdlConstants.Element_Path); @@ -658,6 +1816,22 @@ internal override void WritePathExpressionElement(IEdmPathExpression expression) this.WriteEndElement(); } + /// + /// Asynchronously writes Path Expression element. + /// + /// The Edm path expression. + /// Task represents an asynchronous operation. + internal override async Task WritePathExpressionElementAsync(IEdmPathExpression expression) + { + await this.xmlWriter.WriteStartElementAsync(null, CsdlConstants.Element_Path, null).ConfigureAwait(false); + await this.xmlWriter.WriteStringAsync(PathAsXml(expression.PathSegments)).ConfigureAwait(false); + await this.WriteEndElementAsync().ConfigureAwait(false); + } + + /// + /// Writes PropertyPath Expression element. + /// + /// The Edm path expression. internal override void WritePropertyPathExpressionElement(IEdmPathExpression expression) { this.xmlWriter.WriteStartElement(CsdlConstants.Element_PropertyPath); @@ -665,6 +1839,23 @@ internal override void WritePropertyPathExpressionElement(IEdmPathExpression exp this.WriteEndElement(); } + + /// + /// Asynchronously writes PropertyPath Expression element. + /// + /// The Edm path expression. + /// Task represents an asynchronous operation. + internal override async Task WritePropertyPathExpressionElementAsync(IEdmPathExpression expression) + { + await this.xmlWriter.WriteStartElementAsync(null, CsdlConstants.Element_PropertyPath, null).ConfigureAwait(false); + await this.xmlWriter.WriteStringAsync(PathAsXml(expression.PathSegments)).ConfigureAwait(false); + await this.WriteEndElementAsync().ConfigureAwait(false); + } + + /// + /// Writes NavigationPropertyPath Expression element. + /// + /// The Edm path expression. internal override void WriteNavigationPropertyPathExpressionElement(IEdmPathExpression expression) { this.xmlWriter.WriteStartElement(CsdlConstants.Element_NavigationPropertyPath); @@ -672,6 +1863,22 @@ internal override void WriteNavigationPropertyPathExpressionElement(IEdmPathExpr this.WriteEndElement(); } + /// + /// Asynchronously writes NavigationPropertyPath Expression element. + /// + /// The Edm path expression. + /// Task represents an asynchronous operation. + internal override async Task WriteNavigationPropertyPathExpressionElementAsync(IEdmPathExpression expression) + { + await this.xmlWriter.WriteStartElementAsync(null, CsdlConstants.Element_NavigationPropertyPath, null).ConfigureAwait(false); + await this.xmlWriter.WriteStringAsync(PathAsXml(expression.PathSegments)).ConfigureAwait(false); + await this.WriteEndElementAsync().ConfigureAwait(false); + } + + /// + /// Writes AnnotationPath Expression element. + /// + /// The Edm path expression. internal override void WriteAnnotationPathExpressionElement(IEdmPathExpression expression) { this.xmlWriter.WriteStartElement(CsdlConstants.Element_AnnotationPath); @@ -679,32 +1886,119 @@ internal override void WriteAnnotationPathExpressionElement(IEdmPathExpression e this.WriteEndElement(); } + /// + /// Asynchronously writes AnnotationPath Expression element. + /// + /// The Edm path expression. + /// Task represents an asynchronous operation. + internal override async Task WriteAnnotationPathExpressionElementAsync(IEdmPathExpression expression) + { + await this.xmlWriter.WriteStartElementAsync(null, CsdlConstants.Element_AnnotationPath, null).ConfigureAwait(false); + await this.xmlWriter.WriteStringAsync(PathAsXml(expression.PathSegments)).ConfigureAwait(false); + await this.WriteEndElementAsync().ConfigureAwait(false); + } + + /// + /// Writes If Expression element header. + /// + /// EDM if Expression internal override void WriteIfExpressionElementHeader(IEdmIfExpression expression) { this.xmlWriter.WriteStartElement(CsdlConstants.Element_If); } + /// + /// Asynchronously writes If Expression element header. + /// + /// EDM if Expression + /// Task represents an asynchronous operation. + internal override Task WriteIfExpressionElementHeaderAsync(IEdmIfExpression expression) + { + return this.xmlWriter.WriteStartElementAsync(null, CsdlConstants.Element_If, null); + } + + /// + /// Writes If Expression element end. + /// + /// EDM if Expression internal override void WriteIfExpressionElementEnd(IEdmIfExpression expression) { this.WriteEndElement(); } + /// + /// Asynchronously writes If Expression element end. + /// + /// EDM if Expression + /// Task represents an asynchronous operation. + internal override Task WriteIfExpressionElementEndAsync(IEdmIfExpression expression) + { + return this.WriteEndElementAsync(); + } + + /// + /// Writes Collection Expression element header. + /// + /// The Edm collection expression. internal override void WriteCollectionExpressionElementHeader(IEdmCollectionExpression expression) { this.xmlWriter.WriteStartElement(CsdlConstants.Element_Collection); } + /// + /// Asynchronously writes Collection Expression element header. + /// + /// The Edm collection expression. + /// Task represents an asynchronous operation. + internal override Task WriteCollectionExpressionElementHeaderAsync(IEdmCollectionExpression expression) + { + return this.xmlWriter.WriteStartElementAsync(null, CsdlConstants.Element_Collection, null); + } + + /// + /// Writes Collection Expression element end. + /// + /// The Edm collection expression. internal override void WriteCollectionExpressionElementEnd(IEdmCollectionExpression expression) { this.WriteEndElement(); } + /// + /// Asynchronously writes Collection Expression element end. + /// + /// The Edm collection expression. + /// Task represents an asynchronous operation. + internal override Task WriteCollectionExpressionElementEndAsync(IEdmCollectionExpression expression) + { + return this.WriteEndElementAsync(); + } + + /// + /// Writes LabeledElement header. + /// + /// The Edm Labeled expression. internal override void WriteLabeledElementHeader(IEdmLabeledExpression labeledElement) { this.xmlWriter.WriteStartElement(CsdlConstants.Element_LabeledElement); this.WriteRequiredAttribute(CsdlConstants.Attribute_Name, labeledElement.Name, EdmValueWriter.StringAsXml); } + /// + /// Asynchronously writes LabeledElement header. + /// + /// The Edm Labeled expression. + /// Task represents an asynchronous operation. + internal override async Task WriteLabeledElementHeaderAsync(IEdmLabeledExpression labeledElement) + { + await this.xmlWriter.WriteStartElementAsync(null, CsdlConstants.Element_LabeledElement, null).ConfigureAwait(false); + await this.WriteRequiredAttributeAsync(CsdlConstants.Attribute_Name, labeledElement.Name, EdmValueWriter.StringAsXml).ConfigureAwait(false); + } + + /// + /// Writes LabeledExpressionReference Expression. + /// + /// The Edm Labeled Expression expression. internal override void WriteLabeledExpressionReferenceExpression(IEdmLabeledExpressionReferenceExpression labeledExpressionReference) { this.xmlWriter.WriteStartElement(CsdlConstants.Element_LabeledElementReference); @@ -712,6 +2006,22 @@ internal override void WriteLabeledExpressionReferenceExpression(IEdmLabeledExpr this.WriteEndElement(); } + /// + /// Asynchronously writes LabeledExpressionReference Expression. + /// + /// The Edm Labeled Expression expression. + /// Task represents an asynchronous operation. + internal override async Task WriteLabeledExpressionReferenceExpressionAsync(IEdmLabeledExpressionReferenceExpression labeledExpressionReference) + { + await this.xmlWriter.WriteStartElementAsync(null, CsdlConstants.Element_LabeledElementReference, null).ConfigureAwait(false); + await this.WriteRequiredAttributeAsync(CsdlConstants.Attribute_Name, labeledExpressionReference.ReferencedLabeledExpression.Name, EdmValueWriter.StringAsXml).ConfigureAwait(false); + await this.WriteEndElementAsync().ConfigureAwait(false); + } + + /// + /// Writes TimeOfDay Constant Expression element. + /// + /// The Edm TimeOfDay constant expression. internal override void WriteTimeOfDayConstantExpressionElement(IEdmTimeOfDayConstantExpression expression) { this.xmlWriter.WriteStartElement(CsdlConstants.Element_TimeOfDay); @@ -719,6 +2029,23 @@ internal override void WriteTimeOfDayConstantExpressionElement(IEdmTimeOfDayCons this.WriteEndElement(); } + /// + /// Asynchronously writes TimeOfDay Constant Expression element. + /// + /// The Edm TimeOfDay constant expression. + /// Task represents an asynchronous operation. + internal override async Task WriteTimeOfDayConstantExpressionElementAsync(IEdmTimeOfDayConstantExpression expression) + { + await this.xmlWriter.WriteStartElementAsync(null, CsdlConstants.Element_TimeOfDay, null).ConfigureAwait(false); + await this.xmlWriter.WriteStringAsync(EdmValueWriter.TimeOfDayAsXml(expression.Value)).ConfigureAwait(false); + await this.WriteEndElementAsync().ConfigureAwait(false); + } + + /// + /// Writes IsType Expression header. + /// + /// The Edm IsType expression. + /// Is inline type or not. internal override void WriteIsTypeExpressionElementHeader(IEdmIsTypeExpression expression, bool inlineType) { this.xmlWriter.WriteStartElement(CsdlConstants.Element_IsType); @@ -728,6 +2055,27 @@ internal override void WriteIsTypeExpressionElementHeader(IEdmIsTypeExpression e } } + + /// + /// Asynchronously writes IsType Expression header. + /// + /// The Edm IsType expression. + /// Is inline type or not. + /// Task represents an asynchronous operation. + internal override async Task WriteIsTypeExpressionElementHeaderAsync(IEdmIsTypeExpression expression, bool inlineType) + { + await this.xmlWriter.WriteStartElementAsync(null, CsdlConstants.Element_IsType, null).ConfigureAwait(false); + if (inlineType) + { + await this.WriteRequiredAttributeAsync(CsdlConstants.Attribute_Type, expression.Type, this.TypeReferenceAsXml).ConfigureAwait(false); + } + } + + /// + /// Writes Cast Expression header. + /// + /// The Edm Cast expression. + /// Is inline type or not. internal override void WriteCastExpressionElementHeader(IEdmCastExpression expression, bool inlineType) { this.xmlWriter.WriteStartElement(CsdlConstants.Element_Cast); @@ -737,11 +2085,46 @@ internal override void WriteCastExpressionElementHeader(IEdmCastExpression expre } } + /// + /// Asynchronously writes Cast Expression header. + /// + /// The Edm Cast expression. + /// Is inline type or not. + /// Task represents an asynchronous operation. + internal override async Task WriteCastExpressionElementHeaderAsync(IEdmCastExpression expression, bool inlineType) + { + await this.xmlWriter.WriteStartElementAsync(null, CsdlConstants.Element_Cast, null).ConfigureAwait(false); + if (inlineType) + { + await this.WriteRequiredAttributeAsync(CsdlConstants.Attribute_Type, expression.Type, this.TypeReferenceAsXml).ConfigureAwait(false); + } + } + + /// + /// Writes Cast Expression end. + /// + /// The Edm Cast expression. + /// Is inline type or not. internal override void WriteCastExpressionElementEnd(IEdmCastExpression expression, bool inlineType) { this.WriteEndElement(); } + /// + /// Asynchronously writes Cast Expression end. + /// + /// The Edm Cast expression. + /// Is inline type or not. + /// Task represents an asynchronous operation. + internal override Task WriteCastExpressionElementEndAsync(IEdmCastExpression expression, bool inlineType) + { + return this.WriteEndElementAsync(); + } + + /// + /// Writes EnumMember Expression element. + /// + /// The Edm enumaration member expression. internal override void WriteEnumMemberExpressionElement(IEdmEnumMemberExpression expression) { this.xmlWriter.WriteStartElement(CsdlConstants.Element_EnumMember); @@ -749,6 +2132,22 @@ internal override void WriteEnumMemberExpressionElement(IEdmEnumMemberExpression this.WriteEndElement(); } + /// + /// Asynchronously writes EnumMember Expression element. + /// + /// The Edm enumaration member expression. + /// Task represents an asynchronous operation. + internal override async Task WriteEnumMemberExpressionElementAsync(IEdmEnumMemberExpression expression) + { + await this.xmlWriter.WriteStartElementAsync(null, CsdlConstants.Element_EnumMember, null).ConfigureAwait(false); + await this.xmlWriter.WriteStringAsync(EnumMemberAsXmlOrJson(expression.EnumMembers)).ConfigureAwait(false); + await this.WriteEndElementAsync().ConfigureAwait(false); + } + + /// + /// Writes TypeDefinition element header. + /// + /// The Edm Type definition. internal override void WriteTypeDefinitionElementHeader(IEdmTypeDefinition typeDefinition) { this.xmlWriter.WriteStartElement(CsdlConstants.Element_TypeDefinition); @@ -756,16 +2155,61 @@ internal override void WriteTypeDefinitionElementHeader(IEdmTypeDefinition typeD this.WriteRequiredAttribute(CsdlConstants.Attribute_UnderlyingType, typeDefinition.UnderlyingType, this.TypeDefinitionAsXml); } + /// + /// Asynchronously writes TypeDefinition element header. + /// + /// The Edm Type definition. + /// Task represents an asynchronous operation. + internal override async Task WriteTypeDefinitionElementHeaderAsync(IEdmTypeDefinition typeDefinition) + { + await this.xmlWriter.WriteStartElementAsync(null, CsdlConstants.Element_TypeDefinition, null).ConfigureAwait(false); + await this.WriteRequiredAttributeAsync(CsdlConstants.Attribute_Name, typeDefinition.Name, EdmValueWriter.StringAsXml).ConfigureAwait(false); + await this.WriteRequiredAttributeAsync(CsdlConstants.Attribute_UnderlyingType, typeDefinition.UnderlyingType, this.TypeDefinitionAsXml).ConfigureAwait(false); + } + + /// + /// Writes End Element. + /// + /// Task represents an asynchronous operation. internal override void WriteEndElement() { this.xmlWriter.WriteEndElement(); } + /// + /// Asynchronously writes End Element. + /// + internal override Task WriteEndElementAsync() + { + return this.xmlWriter.WriteEndElementAsync(); + } + + /// + /// Writes Array End Element. + /// internal override void WriteArrayEndElement() { this.xmlWriter.WriteEndElement(); } + /// + /// Asynchronously writes Array End Element. + /// + /// Task represents an asynchronous operation. + internal override Task WriteArrayEndElementAsync() + { + return this.xmlWriter.WriteEndElementAsync(); + } + + /// + /// Writes Optional attribute. + /// + /// + /// The attribute name. + /// The attribute value. + /// The attribute default value. + /// Get string Function. + /// Task represents an asynchronous operation. internal void WriteOptionalAttribute(string attribute, T value, T defaultValue, Func getStringFunc) { if (!value.Equals(defaultValue)) @@ -774,6 +2218,30 @@ internal void WriteOptionalAttribute(string attribute, T value, T defaultValu } } + /// + /// Asynchronously writes Optional attribute. + /// + /// + /// The attribute name. + /// The attribute value. + /// The attribute default value. + /// Get string Function. + internal async Task WriteOptionalAttributeAsync(string attribute, T value, T defaultValue, Func getStringFunc) + { + if (!value.Equals(defaultValue)) + { + await this.xmlWriter.WriteAttributeStringAsync(null, attribute, null, getStringFunc(value)).ConfigureAwait(false); + } + } + + /// + /// Writes Optional attribute. + /// + /// + /// The attribute name. + /// The attribute value. + /// Get string Function. + /// Task represents an asynchronous operation. internal void WriteOptionalAttribute(string attribute, T value, Func getStringFunc) { if (value != null) @@ -782,11 +2250,51 @@ internal void WriteOptionalAttribute(string attribute, T value, Func + /// Asynchronously writes Optional attribute. + /// + /// + /// The attribute name. + /// The attribute value. + /// Get string Function. + /// Task represents an asynchronous operation. + internal async Task WriteOptionalAttributeAsync(string attribute, T value, Func getStringFunc) + { + if (value != null) + { + await this.xmlWriter.WriteAttributeStringAsync(null, attribute, null, getStringFunc(value)).ConfigureAwait(false); + } + } + + /// + /// Writes Required attribute. + /// + /// + /// The attribute name. + /// The attribute value. + /// Value to xml function. internal void WriteRequiredAttribute(string attribute, T value, Func toXml) { this.xmlWriter.WriteAttributeString(attribute, toXml(value)); } + /// + /// Asynchronously writes Required attribute. + /// + /// + /// The attribute name. + /// The attribute value. + /// Value to xml function. + /// Task represents an asynchronous operation. + internal Task WriteRequiredAttributeAsync(string attribute, T value, Func toXml) + { + return this.xmlWriter.WriteAttributeStringAsync(null, attribute, null, toXml(value)); + } + + /// + /// Writes Operation element attributes. + /// + /// The Edm operation. internal override void WriteOperationElementAttributes(IEdmOperation operation) { this.WriteRequiredAttribute(CsdlConstants.Attribute_Name, operation.Name, EdmValueWriter.StringAsXml); @@ -802,6 +2310,30 @@ internal override void WriteOperationElementAttributes(IEdmOperation operation) } } + /// + /// Asynchronously writes Operation element attributes. + /// + /// The Edm operation. + /// Task represents an asynchronous operation. + internal override async Task WriteOperationElementAttributesAsync(IEdmOperation operation) + { + await this.WriteRequiredAttributeAsync(CsdlConstants.Attribute_Name, operation.Name, EdmValueWriter.StringAsXml).ConfigureAwait(false); + + if (operation.IsBound) + { + await this.WriteOptionalAttributeAsync(CsdlConstants.Attribute_IsBound, operation.IsBound, EdmValueWriter.BooleanAsXml).ConfigureAwait(false); + } + + if (operation.EntitySetPath != null) + { + await this.WriteOptionalAttributeAsync(CsdlConstants.Attribute_EntitySetPath, operation.EntitySetPath.PathSegments, PathAsXml).ConfigureAwait(false); + } + } + + /// + /// Writes NavigationPropertyBinding. + /// + /// The Edm navigation property binding. internal override void WriteNavigationPropertyBinding(IEdmNavigationPropertyBinding binding) { // For backwards compatability, only write annotations that vary by type cast in versions > 4.0 @@ -826,6 +2358,34 @@ internal override void WriteNavigationPropertyBinding(IEdmNavigationPropertyBind } } + /// + /// Asynchronously writes NavigationPropertyBinding. + /// + /// The Edm navigation property binding. + /// Task represents an asynchronous operation. + internal override async Task WriteNavigationPropertyBindingAsync(IEdmNavigationPropertyBinding binding) + { + // For backwards compatability, only write annotations that vary by type cast in versions > 4.0 + if (this.Model.GetEdmVersion() > EdmConstants.EdmVersion4 || binding.Path.PathSegments.Last().IndexOf('.') < 0) + { + await this.xmlWriter.WriteStartElementAsync(null, CsdlConstants.Element_NavigationPropertyBinding, null).ConfigureAwait(false); + + await this.WriteRequiredAttributeAsync(CsdlConstants.Attribute_Path, binding.Path.Path, EdmValueWriter.StringAsXml).ConfigureAwait(false); + + // TODO: handle container names, etc. + if (binding.Target is IEdmContainedEntitySet containedEntitySet) + { + await this.WriteRequiredAttributeAsync(CsdlConstants.Attribute_Target, containedEntitySet.Path.Path, EdmValueWriter.StringAsXml).ConfigureAwait(false); + } + else + { + await this.WriteRequiredAttributeAsync(CsdlConstants.Attribute_Target, binding.Target.Name, EdmValueWriter.StringAsXml).ConfigureAwait(false); + } + + await this.xmlWriter.WriteEndElementAsync().ConfigureAwait(false); + } + } + private static string SridAsXml(int? i) { return i.HasValue ? Convert.ToString(i.Value, CultureInfo.InvariantCulture) : CsdlConstants.Value_SridVariable; @@ -847,6 +2407,12 @@ private static string GetCsdlNamespace(Version edmVersion) throw new InvalidOperationException(Strings.Serializer_UnknownEdmVersion(edmVersion.ToString())); } + /// + /// Writes OperationImport attributes. + /// + /// The Edm Operation import. + /// Operation attribute name. + /// internal override void WriteOperationImportAttributes(IEdmOperationImport operationImport, string operationAttributeName) { this.WriteRequiredAttribute(CsdlConstants.Attribute_Name, operationImport.Name, EdmValueWriter.StringAsXml); @@ -866,6 +2432,31 @@ internal override void WriteOperationImportAttributes(IEdmOperationImport operat } } + /// + /// Asynchronously writes OperationImport attributes. + /// + /// The Edm Operation import. + /// Operation attribute name. + /// + /// Task represents an asynchronous operation. + internal override async Task WriteOperationImportAttributesAsync(IEdmOperationImport operationImport, string operationAttributeName) + { + await this.WriteRequiredAttributeAsync(CsdlConstants.Attribute_Name, operationImport.Name, EdmValueWriter.StringAsXml).ConfigureAwait(false); + await this.WriteRequiredAttributeAsync(operationAttributeName, operationImport.Operation.FullName(), EdmValueWriter.StringAsXml).ConfigureAwait(false); + + if (operationImport.EntitySet != null) + { + if (operationImport.EntitySet is IEdmPathExpression pathExpression) + { + await this.WriteOptionalAttributeAsync(CsdlConstants.Attribute_EntitySet, pathExpression.PathSegments, PathAsXml).ConfigureAwait(false); + } + else + { + throw new InvalidOperationException(Strings.EdmModel_Validator_Semantic_OperationImportEntitySetExpressionIsInvalid(operationImport.Name)); + } + } + } + private string SerializationName(IEdmSchemaElement element) { if (this.NamespaceAliasMappings != null) diff --git a/src/Microsoft.OData.Edm/Csdl/Serialization/EdmModelCsdlSerializationVisitor.cs b/src/Microsoft.OData.Edm/Csdl/Serialization/EdmModelCsdlSerializationVisitor.cs index 45b618822f..f238c4d915 100644 --- a/src/Microsoft.OData.Edm/Csdl/Serialization/EdmModelCsdlSerializationVisitor.cs +++ b/src/Microsoft.OData.Edm/Csdl/Serialization/EdmModelCsdlSerializationVisitor.cs @@ -7,6 +7,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; using Microsoft.OData.Edm.Vocabularies; namespace Microsoft.OData.Edm.Csdl.Serialization @@ -72,6 +73,59 @@ public override void VisitEntityContainerElements(IEnumerable + /// Asynchronously visits the elements of the entity container. + /// + /// Collection of Edm Entity container elements. + /// + public override async Task VisitEntityContainerElementsAsync(IEnumerable elements) + { + var functionImportsWritten = new HashSet(); + var actionImportsWritten = new HashSet(); + + foreach (IEdmEntityContainerElement element in elements) + { + switch (element.ContainerElementKind) + { + case EdmContainerElementKind.EntitySet: + await this.ProcessEntitySetAsync((IEdmEntitySet)element).ConfigureAwait(false); + break; + case EdmContainerElementKind.Singleton: + await this.ProcessSingletonAsync((IEdmSingleton)element).ConfigureAwait(false); + break; + case EdmContainerElementKind.ActionImport: + // Skip actionImports that have the same name for same overloads of a action. + IEdmActionImport actionImport = (IEdmActionImport)element; + + var uniqueActionName = string.Concat(actionImport.Name, "_", actionImport.Action.FullName(), GetEntitySetString(actionImport)); + if (!actionImportsWritten.Contains(uniqueActionName)) + { + actionImportsWritten.Add(uniqueActionName); + await this.ProcessActionImportAsync(actionImport).ConfigureAwait(false); + } + + break; + case EdmContainerElementKind.FunctionImport: + // Skip functionImports that have the same name for same overloads of a function. + IEdmFunctionImport functionImport = (IEdmFunctionImport)element; + + var uniqueFunctionName = string.Concat(functionImport.Name, "_", functionImport.Function.FullName(), GetEntitySetString(functionImport)); + if (!functionImportsWritten.Contains(uniqueFunctionName)) + { + functionImportsWritten.Add(uniqueFunctionName); + await this.ProcessFunctionImportAsync(functionImport).ConfigureAwait(false); + } + + break; + case EdmContainerElementKind.None: + await this.ProcessEntityContainerElementAsync(element).ConfigureAwait(false); + break; + default: + throw new InvalidOperationException(Edm.Strings.UnknownEnumVal_ContainerElementKind(element.ContainerElementKind.ToString())); + } + } + } + internal void VisitEdmSchema(EdmSchema element, IEnumerable> mappings) { string alias = null; @@ -111,6 +165,50 @@ internal void VisitEdmSchema(EdmSchema element, IEnumerable + /// Asynchronously visits Edm Schema. + /// + /// The Edm Schema. + /// Collection of prefix mappings. + internal async Task VisitEdmSchemaAsync(EdmSchema element, IEnumerable> mappings) + { + string alias = null; + if (this.namespaceAliasMappings != null) + { + this.namespaceAliasMappings.TryGetValue(element.Namespace, out alias); + } + + await this.schemaWriter.WriteSchemaElementHeaderAsync(element, alias, mappings).ConfigureAwait(false); + + VisitSchemaElements(element.SchemaElements); + + // process the functions/actions seperately + foreach (var operation in element.SchemaOperations) + { + await this.schemaWriter.WriteSchemaOperationsHeaderAsync(operation).ConfigureAwait(false); + VisitSchemaElements(operation.Value.AsEnumerable()); // Call AsEnumerable() to make .net 3.5 happy + 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)); + + if (element.OutOfLineAnnotations.Any()) + { + await this.schemaWriter.WriteOutOfLineAnnotationsBeginAsync(element.OutOfLineAnnotations).ConfigureAwait(false); + foreach (KeyValuePair> annotationsForTarget in element.OutOfLineAnnotations) + { + await this.schemaWriter.WriteAnnotationsElementHeaderAsync(annotationsForTarget).ConfigureAwait(false); + VisitVocabularyAnnotations(annotationsForTarget.Value); + await this.schemaWriter.WriteEndElementAsync().ConfigureAwait(false); + } + + await this.schemaWriter.WriteOutOfLineAnnotationsEndAsync(element.OutOfLineAnnotations).ConfigureAwait(false); + } + + await this.schemaWriter.WriteEndElementAsync().ConfigureAwait(false); + } + protected override void ProcessEntityContainer(IEdmEntityContainer element) { this.BeginElement(element, this.schemaWriter.WriteEntityContainerElementHeader); @@ -118,6 +216,17 @@ protected override void ProcessEntityContainer(IEdmEntityContainer element) this.EndElement(element); } + /// + /// Asynchronously processes the entity container. + /// + /// The Edm entity container. + protected override async Task ProcessEntityContainerAsync(IEdmEntityContainer element) + { + await this.BeginElementAsync(element, this.schemaWriter.WriteEntityContainerElementHeaderAsync).ConfigureAwait(false); + await base.ProcessEntityContainerAsync(element).ConfigureAwait(false); + await this.EndElementAsync(element).ConfigureAwait(false); + } + protected override void ProcessEntitySet(IEdmEntitySet element) { this.BeginElement(element, this.schemaWriter.WriteEntitySetElementHeader); @@ -129,6 +238,21 @@ protected override void ProcessEntitySet(IEdmEntitySet element) this.EndElement(element); } + /// + /// Asynchronously processes the entity set. + /// + /// The Edm Entity set. + protected override async Task ProcessEntitySetAsync(IEdmEntitySet element) + { + await this.BeginElementAsync(element, this.schemaWriter.WriteEntitySetElementHeaderAsync).ConfigureAwait(false); + + await base.ProcessEntitySetAsync(element).ConfigureAwait(false); + + await ProcessNavigationPropertyBindingsAsync(element).ConfigureAwait(false); + + await this.EndElementAsync(element).ConfigureAwait(false); + } + protected override void ProcessSingleton(IEdmSingleton element) { this.BeginElement(element, this.schemaWriter.WriteSingletonElementHeader); @@ -140,6 +264,21 @@ protected override void ProcessSingleton(IEdmSingleton element) this.EndElement(element); } + /// + /// Asynchronously processes the singleton. + /// + /// The Edm Singleton + protected override async Task ProcessSingletonAsync(IEdmSingleton element) + { + await this.BeginElementAsync(element, this.schemaWriter.WriteSingletonElementHeaderAsync).ConfigureAwait(false); + + await base.ProcessSingletonAsync(element).ConfigureAwait(false); + + await ProcessNavigationPropertyBindingsAsync(element).ConfigureAwait(false); + + await this.EndElementAsync(element).ConfigureAwait(false); + } + protected override void ProcessEntityType(IEdmEntityType element) { this.BeginElement(element, this.schemaWriter.WriteEntityTypeElementHeader); @@ -153,6 +292,23 @@ protected override void ProcessEntityType(IEdmEntityType element) this.EndElement(element); } + /// + /// Asynchronously processes the entity type. + /// + /// The Edm entity type. + protected override async Task ProcessEntityTypeAsync(IEdmEntityType element) + { + await this.BeginElementAsync(element, this.schemaWriter.WriteEntityTypeElementHeaderAsync).ConfigureAwait(false); + if (element.DeclaredKey != null && element.DeclaredKey.Any()) + { + await this.VisitEntityTypeDeclaredKeyAsync(element.DeclaredKey).ConfigureAwait(false); + } + + this.VisitProperties(element.DeclaredStructuralProperties().Cast()); + this.VisitProperties(element.DeclaredNavigationProperties().Cast()); + await this.EndElementAsync(element).ConfigureAwait(false); + } + protected override void ProcessStructuralProperty(IEdmStructuralProperty element) { bool inlineType = IsInlineType(element.Type); @@ -165,36 +321,106 @@ protected override void ProcessStructuralProperty(IEdmStructuralProperty element this.EndElement(element); } + /// + /// Asynchronously processes the structural property. + /// + /// The Edm structural property. + protected override async Task ProcessStructuralPropertyAsync(IEdmStructuralProperty element) + { + bool inlineType = IsInlineType(element.Type); + 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 this.EndElementAsync(element).ConfigureAwait(false); + } + protected override void ProcessTypeDefinitionReference(IEdmTypeDefinitionReference element) { this.schemaWriter.WriteTypeDefinitionAttributes(element); } + /// + /// Asynchronously processes the type definition reference. + /// + /// The Edm Type definition reference. + protected override Task ProcessTypeDefinitionReferenceAsync(IEdmTypeDefinitionReference element) + { + return this.schemaWriter.WriteTypeDefinitionAttributesAsync(element); + } + protected override void ProcessBinaryTypeReference(IEdmBinaryTypeReference element) { this.schemaWriter.WriteBinaryTypeAttributes(element); } + /// + /// Asynchronously processes the binary type reference. + /// + /// The Edm binary type reference. + protected override Task ProcessBinaryTypeReferenceAsync(IEdmBinaryTypeReference element) + { + return this.schemaWriter.WriteBinaryTypeAttributesAsync(element); + } + protected override void ProcessDecimalTypeReference(IEdmDecimalTypeReference element) { this.schemaWriter.WriteDecimalTypeAttributes(element); } + /// + /// Asynchronously processes the decimal type reference. + /// + /// The Edm Decimal type reference. + protected override Task ProcessDecimalTypeReferenceAsync(IEdmDecimalTypeReference element) + { + return this.schemaWriter.WriteDecimalTypeAttributesAsync(element); + } + protected override void ProcessSpatialTypeReference(IEdmSpatialTypeReference element) { this.schemaWriter.WriteSpatialTypeAttributes(element); } + /// + /// Asynchronously processes the spatial type reference. + /// + /// The Edm spatial type reference. + protected override Task ProcessSpatialTypeReferenceAsync(IEdmSpatialTypeReference element) + { + return this.schemaWriter.WriteSpatialTypeAttributesAsync(element); + } + protected override void ProcessStringTypeReference(IEdmStringTypeReference element) { this.schemaWriter.WriteStringTypeAttributes(element); } + /// + /// Asynchronously processes the string type reference. + /// + /// The Edm string type reference. + protected override Task ProcessStringTypeReferenceAsync(IEdmStringTypeReference element) + { + return this.schemaWriter.WriteStringTypeAttributesAsync(element); + } + protected override void ProcessTemporalTypeReference(IEdmTemporalTypeReference element) { this.schemaWriter.WriteTemporalTypeAttributes(element); } + /// + /// Asynchronously processes the temporal type reference. + /// + /// The Edm temporal type reference. + protected override Task ProcessTemporalTypeReferenceAsync(IEdmTemporalTypeReference element) + { + return this.schemaWriter.WriteTemporalTypeAttributesAsync(element); + } + protected override void ProcessNavigationProperty(IEdmNavigationProperty element) { this.BeginElement(element, this.schemaWriter.WriteNavigationPropertyElementHeader); @@ -213,6 +439,28 @@ protected override void ProcessNavigationProperty(IEdmNavigationProperty element this.navigationProperties.Add(element); } + /// + /// Asynchronously processes the navigation property. + /// + /// The Edm navigation property. + protected override async Task ProcessNavigationPropertyAsync(IEdmNavigationProperty element) + { + await this.BeginElementAsync(element, this.schemaWriter.WriteNavigationPropertyElementHeaderAsync).ConfigureAwait(false); + + // From 4.0.1 spec says: + // "OnDelete" has "None, Cascade, SetNull, SetDefault" values defined in 4.01. + // But, ODL now only process "Cascade" and "None".So we should fix it to support the others. + if (element.OnDelete != EdmOnDeleteAction.None) + { + await this.schemaWriter.WriteNavigationOnDeleteActionElementAsync(element.OnDelete).ConfigureAwait(false); + } + + await this.ProcessReferentialConstraintAsync(element.ReferentialConstraint).ConfigureAwait(false); + + await this.EndElementAsync(element).ConfigureAwait(false); + this.navigationProperties.Add(element); + } + protected override void ProcessComplexType(IEdmComplexType element) { this.BeginElement(element, this.schemaWriter.WriteComplexTypeElementHeader); @@ -220,6 +468,17 @@ protected override void ProcessComplexType(IEdmComplexType element) this.EndElement(element); } + /// + /// Asynchronously processes the complex type. + /// + /// The Edm complex type. + protected override async Task ProcessComplexTypeAsync(IEdmComplexType element) + { + await this.BeginElementAsync(element, this.schemaWriter.WriteComplexTypeElementHeaderAsync).ConfigureAwait(false); + await base.ProcessComplexTypeAsync(element).ConfigureAwait(false); + await this.EndElementAsync(element).ConfigureAwait(false); + } + protected override void ProcessEnumType(IEdmEnumType element) { this.BeginElement(element, this.schemaWriter.WriteEnumTypeElementHeader); @@ -227,12 +486,33 @@ protected override void ProcessEnumType(IEdmEnumType element) this.EndElement(element, this.schemaWriter.WriteEnumTypeElementEnd); } + /// + /// Asynchronously processes the enum type. + /// + /// The Edm enumeration type. + protected override async Task ProcessEnumTypeAsync(IEdmEnumType element) + { + await this.BeginElementAsync(element, this.schemaWriter.WriteEnumTypeElementHeaderAsync).ConfigureAwait(false); + await base.ProcessEnumTypeAsync(element).ConfigureAwait(false); + await this.EndElementAsync(element, this.schemaWriter.WriteEnumTypeElementEndAsync).ConfigureAwait(false); + } + protected override void ProcessEnumMember(IEdmEnumMember element) { this.BeginElement(element, this.schemaWriter.WriteEnumMemberElementHeader); this.EndElement(element, this.schemaWriter.WriteEnumMemberElementEnd); } + /// + /// Asynchronously processes the enum member. + /// + /// The Edm enumeration member. + protected override async Task ProcessEnumMemberAsync(IEdmEnumMember element) + { + await this.BeginElementAsync(element, this.schemaWriter.WriteEnumMemberElementHeaderAsync).ConfigureAwait(false); + await this.EndElementAsync(element, this.schemaWriter.WriteEnumMemberElementEndAsync).ConfigureAwait(false); + } + protected override void ProcessTypeDefinition(IEdmTypeDefinition element) { this.BeginElement(element, this.schemaWriter.WriteTypeDefinitionElementHeader); @@ -240,6 +520,17 @@ protected override void ProcessTypeDefinition(IEdmTypeDefinition element) this.EndElement(element); } + /// + /// Asynchronously processes the type definition. + /// + /// The Edm type definition. + protected override async Task ProcessTypeDefinitionAsync(IEdmTypeDefinition element) + { + await this.BeginElementAsync(element, this.schemaWriter.WriteTypeDefinitionElementHeaderAsync).ConfigureAwait(false); + await base.ProcessTypeDefinitionAsync(element).ConfigureAwait(false); + await this.EndElementAsync(element).ConfigureAwait(false); + } + protected override void ProcessTerm(IEdmTerm term) { bool inlineType = term.Type != null && IsInlineType(term.Type); @@ -255,16 +546,53 @@ protected override void ProcessTerm(IEdmTerm term) this.EndElement(term); } + /// + /// Asynchronously processes the term. + /// + /// The Edm term. + protected override async Task ProcessTermAsync(IEdmTerm term) + { + bool inlineType = term.Type != null && IsInlineType(term.Type); + await this.BeginElementAsync(term, (IEdmTerm t) => this.schemaWriter.WriteTermElementHeaderAsync(t, inlineType), e => this.ProcessFacetsAsync(e.Type, inlineType)).ConfigureAwait(false); + if (!inlineType) + { + if (term.Type != null) + { + VisitTypeReference(term.Type); + } + } + + await this.EndElementAsync(term).ConfigureAwait(false); + } + protected override void ProcessAction(IEdmAction action) { this.ProcessOperation(action, this.schemaWriter.WriteActionElementHeader); } + /// + /// Asynchronously processes the action. + /// + /// The Edm action. + protected override Task ProcessActionAsync(IEdmAction action) + { + return this.ProcessOperationAsync(action, this.schemaWriter.WriteActionElementHeaderAsync); + } + protected override void ProcessFunction(IEdmFunction function) { this.ProcessOperation(function, this.schemaWriter.WriteFunctionElementHeader); } + /// + /// Asynchronously processes the function. + /// + /// The Edm function. + protected override Task ProcessFunctionAsync(IEdmFunction function) + { + return this.ProcessOperationAsync(function, this.schemaWriter.WriteFunctionElementHeaderAsync); + } + protected override void ProcessOperationParameter(IEdmOperationParameter element) { bool inlineType = IsInlineType(element.Type); @@ -287,6 +615,32 @@ protected override void ProcessOperationParameter(IEdmOperationParameter element this.schemaWriter.WriteOperationParameterEndElement(element); } + /// + /// Asynchronously processes the operation parameter. + /// + /// The Edm operation parameter. + protected override async Task ProcessOperationParameterAsync(IEdmOperationParameter element) + { + bool inlineType = IsInlineType(element.Type); + await this.BeginElementAsync( + element, + (IEdmOperationParameter t) => this.schemaWriter.WriteOperationParameterElementHeaderAsync(t, inlineType), + e => this.ProcessFacetsAsync(e.Type, inlineType) + ).ConfigureAwait(false); + if (!inlineType) + { + VisitTypeReference(element.Type); + } + + await this.VisitPrimitiveElementAnnotationsAsync(this.Model.DirectValueAnnotations(element)).ConfigureAwait(false); + if (element is IEdmVocabularyAnnotatable vocabularyAnnotatableElement) + { + await this.VisitElementVocabularyAnnotationsAsync(this.Model.FindDeclaredVocabularyAnnotations(vocabularyAnnotatableElement).Where(a => a.IsInline(this.Model))).ConfigureAwait(false); + } + + await this.schemaWriter.WriteOperationParameterEndElementAsync(element).ConfigureAwait(false); + } + protected override void ProcessOperationReturn(IEdmOperationReturn operationReturn) { if (operationReturn == null) @@ -313,6 +667,36 @@ protected override void ProcessOperationReturn(IEdmOperationReturn operationRetu this.EndElement(operationReturn); } + /// + /// Asynchronously processes the operation return. + /// + /// The Edm operation return. + protected override async Task ProcessOperationReturnAsync(IEdmOperationReturn operationReturn) + { + if (operationReturn == null) + { + return; + } + + bool inlineType = IsInlineType(operationReturn.Type); + await this.BeginElementAsync( + operationReturn.Type, + type => this.schemaWriter.WriteReturnTypeElementHeaderAsync(operationReturn), + async type => + { + if (inlineType) + { + await this.schemaWriter.WriteTypeAttributeAsync(type).ConfigureAwait(false); + await this.ProcessFacetsAsync(type, true /*inlineType*/).ConfigureAwait(false); + } + else + { + this.VisitTypeReference(type); + } + }).ConfigureAwait(false); + await this.EndElementAsync(operationReturn).ConfigureAwait(false); + } + protected override void ProcessCollectionType(IEdmCollectionType element) { bool inlineType = IsInlineType(element.ElementType); @@ -328,18 +712,58 @@ protected override void ProcessCollectionType(IEdmCollectionType element) this.EndElement(element); } + /// + /// Asynchronously process collection type. + /// + /// The Edm collection type. + protected override async Task ProcessCollectionTypeAsync(IEdmCollectionType element) + { + bool inlineType = IsInlineType(element.ElementType); + await this.BeginElementAsync( + element, + (IEdmCollectionType t) => this.schemaWriter.WriteCollectionTypeElementHeaderAsync(t, inlineType), + e => this.ProcessFacetsAsync(e.ElementType, inlineType) + ).ConfigureAwait(false); + if (!inlineType) + { + VisitTypeReference(element.ElementType); + } + + await this.EndElementAsync(element).ConfigureAwait(false); + } + protected override void ProcessActionImport(IEdmActionImport actionImport) { this.BeginElement(actionImport, this.schemaWriter.WriteActionImportElementHeader); this.EndElement(actionImport); } + /// + /// Asynchronously processes the action import. + /// + /// The Edm action import. + protected override async Task ProcessActionImportAsync(IEdmActionImport actionImport) + { + await this.BeginElementAsync(actionImport, this.schemaWriter.WriteActionImportElementHeaderAsync).ConfigureAwait(false); + await this.EndElementAsync(actionImport).ConfigureAwait(false); + } + protected override void ProcessFunctionImport(IEdmFunctionImport functionImport) { this.BeginElement(functionImport, this.schemaWriter.WriteFunctionImportElementHeader); this.EndElement(functionImport); } + /// + /// Asynchronously processes the function import. + /// + /// The Edm function import. + protected override async Task ProcessFunctionImportAsync(IEdmFunctionImport functionImport) + { + await this.BeginElementAsync(functionImport, this.schemaWriter.WriteFunctionImportElementHeaderAsync).ConfigureAwait(false); + await this.EndElementAsync(functionImport).ConfigureAwait(false); + } + #region Vocabulary Annotations protected override void ProcessAnnotation(IEdmVocabularyAnnotation annotation) @@ -354,6 +778,22 @@ protected override void ProcessAnnotation(IEdmVocabularyAnnotation annotation) this.EndElement(annotation, t => this.schemaWriter.WriteVocabularyAnnotationElementEnd(annotation, isInline)); } + /// + /// Asynchronously processes the annotation. + /// + /// The Edm vocabulary annotation. + protected override async Task ProcessAnnotationAsync(IEdmVocabularyAnnotation annotation) + { + bool isInline = IsInlineExpression(annotation.Value); + await this.BeginElementAsync(annotation, t => this.schemaWriter.WriteVocabularyAnnotationElementHeaderAsync(t, isInline)).ConfigureAwait(false); + if (!isInline) + { + await base.ProcessAnnotationAsync(annotation).ConfigureAwait(false); + } + + await this.EndElementAsync(annotation, t => this.schemaWriter.WriteVocabularyAnnotationElementEndAsync(annotation, isInline)).ConfigureAwait(false); + } + #endregion #region Expressions @@ -363,11 +803,29 @@ protected override void ProcessStringConstantExpression(IEdmStringConstantExpres this.schemaWriter.WriteStringConstantExpressionElement(expression); } + /// + /// Asynchronously processes the string constant expression. + /// + /// The Edm string constant expression. + protected override Task ProcessStringConstantExpressionAsync(IEdmStringConstantExpression expression) + { + return this.schemaWriter.WriteStringConstantExpressionElementAsync(expression); + } + protected override void ProcessBinaryConstantExpression(IEdmBinaryConstantExpression expression) { this.schemaWriter.WriteBinaryConstantExpressionElement(expression); } + /// + /// Asynchronously processes the binary constant expression. + /// + /// The Edm binary constant expression. + protected override Task ProcessBinaryConstantExpressionAsync(IEdmBinaryConstantExpression expression) + { + return this.schemaWriter.WriteBinaryConstantExpressionElementAsync(expression); + } + protected override void ProcessRecordExpression(IEdmRecordExpression expression) { this.BeginElement(expression, this.schemaWriter.WriteRecordExpressionElementHeader); @@ -375,6 +833,17 @@ protected override void ProcessRecordExpression(IEdmRecordExpression expression) this.EndElement(expression); } + /// + /// Asynchronously processes the record expression. + /// + /// The Edm record expression. + protected override async Task ProcessRecordExpressionAsync(IEdmRecordExpression expression) + { + await this.BeginElementAsync(expression, this.schemaWriter.WriteRecordExpressionElementHeaderAsync).ConfigureAwait(false); + this.VisitPropertyConstructors(expression.Properties); + await this.EndElementAsync(expression).ConfigureAwait(false); + } + protected override void ProcessLabeledExpression(IEdmLabeledExpression element) { if (element.Name == null) @@ -389,11 +858,38 @@ protected override void ProcessLabeledExpression(IEdmLabeledExpression element) } } + /// + /// Asynchronously processes the labeled expression. + /// + /// The Edm labeled expression. + protected override async Task ProcessLabeledExpressionAsync(IEdmLabeledExpression element) + { + if (element.Name == null) + { + await base.ProcessLabeledExpressionAsync(element).ConfigureAwait(false); + } + else + { + await this.BeginElementAsync(element, this.schemaWriter.WriteLabeledElementHeaderAsync).ConfigureAwait(false); + await base.ProcessLabeledExpressionAsync(element).ConfigureAwait(false); + await this.EndElementAsync(element).ConfigureAwait(false); + } + } + protected override void ProcessLabeledExpressionReferenceExpression(IEdmLabeledExpressionReferenceExpression element) { this.schemaWriter.WriteLabeledExpressionReferenceExpression(element); } + /// + /// Asynchronously processes the labeled expression reference expression. + /// + /// The Edm labeled expression reference. + protected override Task ProcessLabeledExpressionReferenceExpressionAsync(IEdmLabeledExpressionReferenceExpression element) + { + return this.schemaWriter.WriteLabeledExpressionReferenceExpressionAsync(element); + } + protected override void ProcessPropertyConstructor(IEdmPropertyConstructor constructor) { bool isInline = IsInlineExpression(constructor.Value); @@ -406,26 +902,78 @@ protected override void ProcessPropertyConstructor(IEdmPropertyConstructor const this.EndElement(constructor, this.schemaWriter.WritePropertyConstructorElementEnd); } + /// + /// Asynchronously processes the property constructor. + /// + /// The Edm property constructor. + protected override async Task ProcessPropertyConstructorAsync(IEdmPropertyConstructor constructor) + { + bool isInline = IsInlineExpression(constructor.Value); + await this.BeginElementAsync(constructor, t => this.schemaWriter.WritePropertyConstructorElementHeaderAsync(t, isInline)).ConfigureAwait(false); + if (!isInline) + { + await base.ProcessPropertyConstructorAsync(constructor).ConfigureAwait(false); + } + + await this.EndElementAsync(constructor, this.schemaWriter.WritePropertyConstructorElementEndAsync).ConfigureAwait(false); + } + protected override void ProcessPathExpression(IEdmPathExpression expression) { this.schemaWriter.WritePathExpressionElement(expression); } + /// + /// Asynchronously processes the path expression. + /// + /// The Edm path expression. + protected override Task ProcessPathExpressionAsync(IEdmPathExpression expression) + { + return this.schemaWriter.WritePathExpressionElementAsync(expression); + } + protected override void ProcessPropertyPathExpression(IEdmPathExpression expression) { this.schemaWriter.WritePropertyPathExpressionElement(expression); } + /// + /// Asynchronously processes the property path expression. + /// + /// The Edm path expression. + protected override Task ProcessPropertyPathExpressionAsync(IEdmPathExpression expression) + { + return this.schemaWriter.WritePropertyPathExpressionElementAsync(expression); + } + protected override void ProcessNavigationPropertyPathExpression(IEdmPathExpression expression) { this.schemaWriter.WriteNavigationPropertyPathExpressionElement(expression); } + /// + /// Asynchronously processes the navigation property path expression. + /// + /// The Edm path expression. + protected override Task ProcessNavigationPropertyPathExpressionAsync(IEdmPathExpression expression) + { + return this.schemaWriter.WriteNavigationPropertyPathExpressionElementAsync(expression); + } + protected override void ProcessAnnotationPathExpression(IEdmPathExpression expression) { this.schemaWriter.WriteAnnotationPathExpressionElement(expression); } + /// + /// Asynchronously processes the annotation path expression. + /// + /// The Edm path expression. + protected override Task ProcessAnnotationPathExpressionAsync(IEdmPathExpression expression) + { + return this.schemaWriter.WriteAnnotationPathExpressionElementAsync(expression); + } + protected override void ProcessCollectionExpression(IEdmCollectionExpression expression) { this.BeginElement(expression, this.schemaWriter.WriteCollectionExpressionElementHeader); @@ -433,6 +981,17 @@ protected override void ProcessCollectionExpression(IEdmCollectionExpression exp this.EndElement(expression, this.schemaWriter.WriteCollectionExpressionElementEnd); } + /// + /// Asynchronously processes the collection expression. + /// + /// The Edm collection expression. + protected override async Task ProcessCollectionExpressionAsync(IEdmCollectionExpression expression) + { + await this.BeginElementAsync(expression, this.schemaWriter.WriteCollectionExpressionElementHeaderAsync).ConfigureAwait(false); + this.VisitExpressions(expression.Elements); + await this.EndElementAsync(expression, this.schemaWriter.WriteCollectionExpressionElementEndAsync).ConfigureAwait(false); + } + protected override void ProcessIsTypeExpression(IEdmIsTypeExpression expression) { bool inlineType = IsInlineType(expression.Type); @@ -458,11 +1017,50 @@ protected override void ProcessIsTypeExpression(IEdmIsTypeExpression expression) } } + /// + /// Asynchronously processes the is type expression. + /// + /// The Edm IsType expression. + protected override async Task ProcessIsTypeExpressionAsync(IEdmIsTypeExpression expression) + { + bool inlineType = IsInlineType(expression.Type); + + if (this.isXml) + { + await this.BeginElementAsync(expression, (IEdmIsTypeExpression t) => this.schemaWriter.WriteIsTypeExpressionElementHeaderAsync(t, inlineType), e => this.ProcessFacetsAsync(e.Type, inlineType)).ConfigureAwait(false); + + if (!inlineType) + { + VisitTypeReference(expression.Type); + } + + this.VisitExpression(expression.Operand); + await this.EndElementAsync(expression).ConfigureAwait(false); + } + else + { + await this.BeginElementAsync(expression, (IEdmIsTypeExpression t) => this.schemaWriter.WriteIsTypeExpressionElementHeaderAsync(t, inlineType)).ConfigureAwait(false); + this.VisitExpression(expression.Operand); + await this.schemaWriter.WriteIsOfExpressionTypeAsync(expression, inlineType).ConfigureAwait(false); + await this.ProcessFacetsAsync(expression.Type, inlineType).ConfigureAwait(false); + await this.EndElementAsync(expression).ConfigureAwait(false); + } + } + protected override void ProcessIntegerConstantExpression(IEdmIntegerConstantExpression expression) { this.schemaWriter.WriteIntegerConstantExpressionElement(expression); } + /// + /// Asynchronously processes the integer constant expression. + /// + /// The Edm integer constant expression. + protected override Task ProcessIntegerConstantExpressionAsync(IEdmIntegerConstantExpression expression) + { + return this.schemaWriter.WriteIntegerConstantExpressionElementAsync(expression); + } + protected override void ProcessIfExpression(IEdmIfExpression expression) { this.BeginElement(expression, this.schemaWriter.WriteIfExpressionElementHeader); @@ -470,6 +1068,17 @@ protected override void ProcessIfExpression(IEdmIfExpression expression) this.EndElement(expression, this.schemaWriter.WriteIfExpressionElementEnd); } + /// + /// Asynchronously processes the if expression. + /// + /// The Edm If expression. + protected override async Task ProcessIfExpressionAsync(IEdmIfExpression expression) + { + await this.BeginElementAsync(expression, this.schemaWriter.WriteIfExpressionElementHeaderAsync).ConfigureAwait(false); + await base.ProcessIfExpressionAsync(expression).ConfigureAwait(false); + await this.EndElementAsync(expression, this.schemaWriter.WriteIfExpressionElementEndAsync).ConfigureAwait(false); + } + protected override void ProcessFunctionApplicationExpression(IEdmApplyExpression expression) { this.BeginElement(expression, e => this.schemaWriter.WriteFunctionApplicationElementHeader(e)); @@ -477,56 +1086,157 @@ protected override void ProcessFunctionApplicationExpression(IEdmApplyExpression this.EndElement(expression, e => this.schemaWriter.WriteFunctionApplicationElementEnd(e)); } + /// + /// Asynchronously processes the function application expression. + /// + /// The Edm Apply expression. + protected override async Task ProcessFunctionApplicationExpressionAsync(IEdmApplyExpression expression) + { + await this.BeginElementAsync(expression, this.schemaWriter.WriteFunctionApplicationElementHeaderAsync).ConfigureAwait(false); + this.VisitExpressions(expression.Arguments); + await this.EndElementAsync(expression, this.schemaWriter.WriteFunctionApplicationElementEndAsync).ConfigureAwait(false); + } + protected override void ProcessFloatingConstantExpression(IEdmFloatingConstantExpression expression) { this.schemaWriter.WriteFloatingConstantExpressionElement(expression); } + /// + /// Asynchronously processes the floating constant expression. + /// + /// The Edm floating constant expression. + protected override Task ProcessFloatingConstantExpressionAsync(IEdmFloatingConstantExpression expression) + { + return this.schemaWriter.WriteFloatingConstantExpressionElementAsync(expression); + } + protected override void ProcessGuidConstantExpression(IEdmGuidConstantExpression expression) { this.schemaWriter.WriteGuidConstantExpressionElement(expression); } + /// + /// Asynchronously processes the guid constant expression. + /// + /// The Edm Guid constant expression. + protected override Task ProcessGuidConstantExpressionAsync(IEdmGuidConstantExpression expression) + { + return this.schemaWriter.WriteGuidConstantExpressionElementAsync(expression); + } + protected override void ProcessEnumMemberExpression(IEdmEnumMemberExpression expression) { this.schemaWriter.WriteEnumMemberExpressionElement(expression); } + /// + /// Asynchronously processes the enum member expression. + /// + /// The Edm enumeration member. + protected override Task ProcessEnumMemberExpressionAsync(IEdmEnumMemberExpression expression) + { + return this.schemaWriter.WriteEnumMemberExpressionElementAsync(expression); + } + protected override void ProcessDecimalConstantExpression(IEdmDecimalConstantExpression expression) { this.schemaWriter.WriteDecimalConstantExpressionElement(expression); } + /// + /// Asynchronously processes the decimal constant expression. + /// + /// The Edm decimal constant expression. + protected override Task ProcessDecimalConstantExpressionAsync(IEdmDecimalConstantExpression expression) + { + return this.schemaWriter.WriteDecimalConstantExpressionElementAsync(expression); + } + protected override void ProcessDateConstantExpression(IEdmDateConstantExpression expression) { this.schemaWriter.WriteDateConstantExpressionElement(expression); } + /// + /// Asynchronously processes the date constant expression. + /// + /// The Edm date constant expression. + protected override Task ProcessDateConstantExpressionAsync(IEdmDateConstantExpression expression) + { + return this.schemaWriter.WriteDateConstantExpressionElementAsync(expression); + } + protected override void ProcessDateTimeOffsetConstantExpression(IEdmDateTimeOffsetConstantExpression expression) { this.schemaWriter.WriteDateTimeOffsetConstantExpressionElement(expression); } + /// + /// Asynchronously processes the date time offset constant expression. + /// + /// The Edm DateTimeOffset constant expression. + protected override Task ProcessDateTimeOffsetConstantExpressionAsync(IEdmDateTimeOffsetConstantExpression expression) + { + return this.schemaWriter.WriteDateTimeOffsetConstantExpressionElementAsync(expression); + } + protected override void ProcessDurationConstantExpression(IEdmDurationConstantExpression expression) { this.schemaWriter.WriteDurationConstantExpressionElement(expression); } + /// + /// Asynchronously processes the duration constant expression. + /// + /// The Edm duration constant expression. + protected override Task ProcessDurationConstantExpressionAsync(IEdmDurationConstantExpression expression) + { + return this.schemaWriter.WriteDurationConstantExpressionElementAsync(expression); + } + protected override void ProcessTimeOfDayConstantExpression(IEdmTimeOfDayConstantExpression expression) { this.schemaWriter.WriteTimeOfDayConstantExpressionElement(expression); } + /// + /// Asynchronously processes the time of day constant expression. + /// + /// The Edm TimeOfDay constant expression. + protected override Task ProcessTimeOfDayConstantExpressionAsync(IEdmTimeOfDayConstantExpression expression) + { + return this.schemaWriter.WriteTimeOfDayConstantExpressionElementAsync(expression); + } + protected override void ProcessBooleanConstantExpression(IEdmBooleanConstantExpression expression) { this.schemaWriter.WriteBooleanConstantExpressionElement(expression); } + /// + /// Asynchronously processes the boolean constant expression. + /// + /// The Edm boolean constant expression. + protected override Task ProcessBooleanConstantExpressionAsync(IEdmBooleanConstantExpression expression) + { + return this.schemaWriter.WriteBooleanConstantExpressionElementAsync(expression); + } + protected override void ProcessNullConstantExpression(IEdmNullExpression expression) { this.schemaWriter.WriteNullConstantExpressionElement(expression); } + /// + /// Asynchronously processes the null constant expression. + /// + /// The Edm Null expression. + protected override Task ProcessNullConstantExpressionAsync(IEdmNullExpression expression) + { + return this.schemaWriter.WriteNullConstantExpressionElementAsync(expression); + } + protected override void ProcessCastExpression(IEdmCastExpression expression) { bool inlineType = IsInlineType(expression.Type); @@ -555,6 +1265,39 @@ protected override void ProcessCastExpression(IEdmCastExpression expression) } } + /// + /// Asynchronously processes the cast expression. + /// + /// The Edm cast expression. + protected override async Task ProcessCastExpressionAsync(IEdmCastExpression expression) + { + bool inlineType = IsInlineType(expression.Type); + + if (this.isXml) + { + await this.BeginElementAsync(expression, (IEdmCastExpression t) => this.schemaWriter.WriteCastExpressionElementHeaderAsync(t, inlineType), e => this.ProcessFacetsAsync(e.Type, inlineType)).ConfigureAwait(false); + + if (!inlineType) + { + VisitTypeReference(expression.Type); + } + + this.VisitExpression(expression.Operand); + await this.EndElementAsync(expression).ConfigureAwait(false); + } + else + { + await this.BeginElementAsync(expression, (IEdmCastExpression t) => this.schemaWriter.WriteCastExpressionElementHeaderAsync(t, inlineType)).ConfigureAwait(false); + + this.VisitExpression(expression.Operand); + + await this.schemaWriter.WriteCastExpressionTypeAsync(expression, inlineType).ConfigureAwait(false); + await this.ProcessFacetsAsync(expression.Type, inlineType).ConfigureAwait(false); + + await this.EndElementAsync(expression, t => this.schemaWriter.WriteCastExpressionElementEndAsync(t, inlineType)).ConfigureAwait(false); + } + } + #endregion private void ProcessNavigationPropertyBindings(IEdmNavigationSource navigationSource) @@ -572,6 +1315,25 @@ private void ProcessNavigationPropertyBindings(IEdmNavigationSource navigationSo } } + /// + /// Asynchronously processes the navigation property bindings. + /// + /// The Edm navigation source. + private async Task ProcessNavigationPropertyBindingsAsync(IEdmNavigationSource navigationSource) + { + if (navigationSource != null && navigationSource.NavigationPropertyBindings.Any()) + { + await this.schemaWriter.WriteNavigationPropertyBindingsBeginAsync(navigationSource.NavigationPropertyBindings).ConfigureAwait(false); + + foreach (IEdmNavigationPropertyBinding binding in navigationSource.NavigationPropertyBindings) + { + await this.schemaWriter.WriteNavigationPropertyBindingAsync(binding).ConfigureAwait(false); + } + + await this.schemaWriter.WriteNavigationPropertyBindingsEndAsync(navigationSource.NavigationPropertyBindings).ConfigureAwait(false); + } + } + private static bool IsInlineType(IEdmTypeReference reference) { if (reference.Definition is IEdmSchemaElement || reference.IsEntityReference()) @@ -639,6 +1401,26 @@ private void ProcessOperation(TOperation operation, Action + /// Asynchronously processes the operation. + /// + /// + /// Operation. + /// Write Element action. + private async Task ProcessOperationAsync(TOperation operation, Func writeElementAction) where TOperation : IEdmOperation + { + await this.BeginElementAsync(operation, writeElementAction).ConfigureAwait(false); + + await this.schemaWriter.WriteOperationParametersBeginAsync(operation.Parameters).ConfigureAwait(false); + this.VisitOperationParameters(operation.Parameters); + await this.schemaWriter.WriteOperationParametersEndAsync(operation.Parameters).ConfigureAwait(false); + + IEdmOperationReturn operationReturn = operation.GetReturn(); + await this.ProcessOperationReturnAsync(operationReturn).ConfigureAwait(false); + + await this.EndElementAsync(operation).ConfigureAwait(false); + } + private void ProcessReferentialConstraint(IEdmReferentialConstraint element) { if (element != null) @@ -653,6 +1435,24 @@ private void ProcessReferentialConstraint(IEdmReferentialConstraint element) } } + /// + /// Asynchronously processes the referential constraint. + /// + /// The Edm referential constraint. + private async Task ProcessReferentialConstraintAsync(IEdmReferentialConstraint element) + { + if (element != null) + { + await this.schemaWriter.WriteReferentialConstraintBeginAsync(element).ConfigureAwait(false); + foreach (var pair in element.PropertyPairs) + { + await this.schemaWriter.WriteReferentialConstraintPairAsync(pair).ConfigureAwait(false); + } + + await this.schemaWriter.WriteReferentialConstraintEndAsync(element).ConfigureAwait(false); + } + } + private void ProcessFacets(IEdmTypeReference element, bool inlineType) { if (element != null) @@ -680,6 +1480,38 @@ private void ProcessFacets(IEdmTypeReference element, bool inlineType) } } + /// + /// Asynchronously processes the facets. + /// + /// The Edm type reference. + /// Is inline type or not. + private async Task ProcessFacetsAsync(IEdmTypeReference element, bool inlineType) + { + if (element != null) + { + if (element.IsEntityReference()) + { + // No facets get serialized for an entity reference. + return; + } + + if (inlineType) + { + if (element.TypeKind() == EdmTypeKind.Collection) + { + IEdmCollectionTypeReference collectionElement = element.AsCollection(); + await this.schemaWriter.WriteNullableAttributeAsync(collectionElement.CollectionDefinition().ElementType).ConfigureAwait(false); + VisitTypeReference(collectionElement.CollectionDefinition().ElementType); + } + else + { + await this.schemaWriter.WriteNullableAttributeAsync(element).ConfigureAwait(false); + VisitTypeReference(element); + } + } + } + } + private void VisitEntityTypeDeclaredKey(IEnumerable keyProperties) { this.schemaWriter.WriteDeclaredKeyPropertiesElementHeader(); @@ -687,6 +1519,17 @@ private void VisitEntityTypeDeclaredKey(IEnumerable keyP this.schemaWriter.WriteArrayEndElement(); } + /// + /// Asynchronously visits the entity type declared key. + /// + /// Collection of Edm structural properties. + private async Task VisitEntityTypeDeclaredKeyAsync(IEnumerable keyProperties) + { + await this.schemaWriter.WriteDeclaredKeyPropertiesElementHeaderAsync().ConfigureAwait(false); + await this.VisitPropertyRefsAsync(keyProperties).ConfigureAwait(false); + await this.schemaWriter.WriteArrayEndElementAsync().ConfigureAwait(false); + } + private void VisitPropertyRefs(IEnumerable properties) { foreach (IEdmStructuralProperty property in properties) @@ -695,6 +1538,18 @@ private void VisitPropertyRefs(IEnumerable properties) } } + /// + /// Asynchronously visits the property references. + /// + /// Collection of Edm structural properties. + private async Task VisitPropertyRefsAsync(IEnumerable properties) + { + foreach (IEdmStructuralProperty property in properties) + { + await this.schemaWriter.WritePropertyRefElementAsync(property).ConfigureAwait(false); + } + } + private void VisitAttributeAnnotations(IEnumerable annotations) { foreach (IEdmDirectValueAnnotation annotation in annotations) @@ -716,6 +1571,24 @@ private void VisitAttributeAnnotations(IEnumerable an } } + /// + /// Asynchronously visits the attribute annotations. + /// + /// Collection of Edm direct value annotations. + private async Task VisitAttributeAnnotationsAsync(IEnumerable annotations) + { + foreach (IEdmDirectValueAnnotation annotation in annotations) + { + if (annotation.NamespaceUri != EdmConstants.InternalUri && annotation.Value is IEdmValue edmValue && !edmValue.IsSerializedAsElement(this.Model)) + { + if (edmValue.Type.TypeKind() == EdmTypeKind.Primitive) + { + await this.ProcessAttributeAnnotationAsync(annotation).ConfigureAwait(false); + } + } + } + } + private void VisitPrimitiveElementAnnotations(IEnumerable annotations) { foreach (IEdmDirectValueAnnotation annotation in annotations) @@ -737,16 +1610,52 @@ private void VisitPrimitiveElementAnnotations(IEnumerable + /// Asynchronously visits the primitive element annotations. + /// + /// Collection of Edm direct value annotations. + private async Task VisitPrimitiveElementAnnotationsAsync(IEnumerable annotations) + { + foreach (IEdmDirectValueAnnotation annotation in annotations) + { + if (annotation.NamespaceUri != EdmConstants.InternalUri && annotation.Value is IEdmValue edmValue && edmValue.IsSerializedAsElement(this.Model)) + { + if (edmValue.Type.TypeKind() == EdmTypeKind.Primitive) + { + await this.ProcessElementAnnotationAsync(annotation).ConfigureAwait(false); + } + } + } + } + private void ProcessAttributeAnnotation(IEdmDirectValueAnnotation annotation) { this.schemaWriter.WriteAnnotationStringAttribute(annotation); } + /// + /// Asynchronously processes the attribute annotation. + /// + /// The Edm direct value annotation. + private Task ProcessAttributeAnnotationAsync(IEdmDirectValueAnnotation annotation) + { + return this.schemaWriter.WriteAnnotationStringAttributeAsync(annotation); + } + private void ProcessElementAnnotation(IEdmDirectValueAnnotation annotation) { this.schemaWriter.WriteAnnotationStringElement(annotation); } + /// + /// Asynchronously processes the element annotation. + /// + /// The Edm Direct value annotation. + private Task ProcessElementAnnotationAsync(IEdmDirectValueAnnotation annotation) + { + return this.schemaWriter.WriteAnnotationStringElementAsync(annotation); + } + private void VisitElementVocabularyAnnotations(IEnumerable annotations) { foreach (IEdmVocabularyAnnotation annotation in annotations) @@ -755,6 +1664,18 @@ private void VisitElementVocabularyAnnotations(IEnumerable + /// Asynchronously visits the element vocabulary annotations. + /// + /// Collection of Edm vocabulary annotation. + private async Task VisitElementVocabularyAnnotationsAsync(IEnumerable annotations) + { + foreach (IEdmVocabularyAnnotation annotation in annotations) + { + await this.ProcessAnnotationAsync(annotation).ConfigureAwait(false); + } + } + private void BeginElement(TElement element, Action elementHeaderWriter, params Action[] additionalAttributeWriters) where TElement : IEdmElement { @@ -770,6 +1691,28 @@ private void BeginElement(TElement element, Action elementHe this.VisitAttributeAnnotations(this.Model.DirectValueAnnotations(element)); } + /// + /// Asynchronously begins the element. + /// + /// + /// The Edm element. + /// The Edm element Header writer. + /// Collection of additional attribute writers. + private async Task BeginElementAsync(TElement element, Func elementHeaderWriter, params Func[] additionalAttributeWriters) + where TElement : IEdmElement + { + await elementHeaderWriter(element).ConfigureAwait(false); + if (additionalAttributeWriters != null && additionalAttributeWriters.Length > 0) + { + foreach (Func action in additionalAttributeWriters) + { + await action(element).ConfigureAwait(false); + } + } + + await this.VisitAttributeAnnotationsAsync(this.Model.DirectValueAnnotations(element)).ConfigureAwait(false); + } + private void EndElement(TElement element, Action elementEndWriter) where TElement : IEdmElement { this.VisitPrimitiveElementAnnotations(this.Model.DirectValueAnnotations(element)); @@ -783,6 +1726,24 @@ private void EndElement(TElement element, Action elementEndW elementEndWriter(element); } + /// + /// Asynchronously ends the element. + /// + /// + /// The Edm element. + /// The Edm element end writer. + private async Task EndElementAsync(TElement element, Func elementEndWriter) where TElement : IEdmElement + { + await this.VisitPrimitiveElementAnnotationsAsync(this.Model.DirectValueAnnotations(element)).ConfigureAwait(false); + + if (element is IEdmVocabularyAnnotatable vocabularyAnnotatableElement) + { + await this.VisitElementVocabularyAnnotationsAsync(this.Model.FindDeclaredVocabularyAnnotations(vocabularyAnnotatableElement).Where(a => a.IsInline(this.Model))).ConfigureAwait(false); + } + + await elementEndWriter(element).ConfigureAwait(false); + } + private void EndElement(IEdmElement element) { this.VisitPrimitiveElementAnnotations(this.Model.DirectValueAnnotations(element)); @@ -794,5 +1755,20 @@ private void EndElement(IEdmElement element) this.schemaWriter.WriteEndElement(); } + + /// + /// Asynchronously ends the element. + /// + /// The Edm element. + private async Task EndElementAsync(IEdmElement element) + { + await this.VisitPrimitiveElementAnnotationsAsync(this.Model.DirectValueAnnotations(element)).ConfigureAwait(false); + if (element is IEdmVocabularyAnnotatable vocabularyAnnotatableElement) + { + await this.VisitElementVocabularyAnnotationsAsync(this.Model.FindDeclaredVocabularyAnnotations(vocabularyAnnotatableElement).Where(a => a.IsInline(this.Model))).ConfigureAwait(false); + } + + await this.schemaWriter.WriteEndElementAsync().ConfigureAwait(false); + } } } diff --git a/src/Microsoft.OData.Edm/Csdl/Serialization/EdmModelReferenceElementsJsonVisitor.cs b/src/Microsoft.OData.Edm/Csdl/Serialization/EdmModelReferenceElementsJsonVisitor.cs index 5784622525..585b01cf19 100644 --- a/src/Microsoft.OData.Edm/Csdl/Serialization/EdmModelReferenceElementsJsonVisitor.cs +++ b/src/Microsoft.OData.Edm/Csdl/Serialization/EdmModelReferenceElementsJsonVisitor.cs @@ -10,6 +10,7 @@ using System.Collections.Generic; using System.Linq; using System.Text.Json; +using System.Threading.Tasks; namespace Microsoft.OData.Edm.Csdl.Serialization { @@ -27,6 +28,10 @@ internal EdmModelReferenceElementsJsonVisitor(IEdmModel model, Utf8JsonWriter wr this.schemaWriter = new EdmModelCsdlSchemaJsonWriter(model, writer, edmVersion); } + /// + /// Visits Edm references and Write includes and annotations. + /// + /// The Edm Model. internal void VisitEdmReferences(IEdmModel model) { IEnumerable references = model.GetEdmReferences(); @@ -54,6 +59,43 @@ internal void VisitEdmReferences(IEdmModel model) } } + /// + /// Asynchronously Visits Edm references and Write includes and annotations. + /// + /// The Edm Model. + /// Task represents an asynchronous operation. + internal async Task VisitEdmReferencesAsync(IEdmModel model) + { + IEnumerable references = model.GetEdmReferences(); + if (model != null && references != null && references.Any()) + { + // The value of $Reference is an object that contains one member per referenced CSDL document. + this.jsonWriter.WritePropertyName("$Reference"); + this.jsonWriter.WriteStartObject(); + + foreach (IEdmReference refence in references) + { + await this.schemaWriter.WriteReferenceElementHeaderAsync(refence).ConfigureAwait(false); + + await WriteIncludesAsync(model, refence.Includes).ConfigureAwait(false); + + await WriteIncludeAnnotationsAsync(refence.IncludeAnnotations).ConfigureAwait(false); + + await WriteAnnotationsAsync(model, refence).ConfigureAwait(false); + + await this.schemaWriter.WriteReferenceElementEndAsync(refence).ConfigureAwait(false); + } + + // End of $Reference + this.jsonWriter.WriteEndObject(); + } + } + + /// + /// Writes Includes + /// + /// The Edm model. + /// Collection of Edm includes. private void WriteIncludes(IEdmModel model, IEnumerable includes) { if (includes == null || !includes.Any()) @@ -81,7 +123,39 @@ private void WriteIncludes(IEdmModel model, IEnumerable includes) } /// - /// $IncludeAnnotations Array. + /// Asynchronously Writes Includes + /// + /// The Edm model. + /// Collection of Edm includes. + /// Task represents an asynchronous operation. + private async Task WriteIncludesAsync(IEdmModel model, IEnumerable includes) + { + if (includes == null || !includes.Any()) + { + return; + } + + // The reference object MAY contain the members $Include + this.jsonWriter.WritePropertyName("$Include"); + + // The value of $Include is an array. + this.jsonWriter.WriteStartArray(); + + foreach (IEdmInclude include in includes) + { + // Array items are objects. + await this.schemaWriter.WritIncludeElementHeaderAsync(include).ConfigureAwait(false); + + await WriteAnnotationsAsync(model, include).ConfigureAwait(false); + + await this.schemaWriter.WriteIncludeElementEndAsync(include).ConfigureAwait(false); + } + + this.jsonWriter.WriteEndArray(); + } + + /// + /// Wrties $IncludeAnnotations Array. /// private void WriteIncludeAnnotations(IEnumerable includeAnnotations) { @@ -116,6 +190,51 @@ private void WriteIncludeAnnotations(IEnumerable include this.jsonWriter.WriteEndArray(); } + /// + /// Asynchronously Writes $IncludeAnnotations Array + /// + /// Collection of Edm include annotations. + /// Task represents an asynchronous operation that may or may not return a result. + private Task WriteIncludeAnnotationsAsync(IEnumerable includeAnnotations) + { + if (includeAnnotations == null || !includeAnnotations.Any()) + { + return Task.CompletedTask; + } + + // The reference object MAY contain the members $IncludeAnnotations + this.jsonWriter.WritePropertyName("$IncludeAnnotations"); + + // The value of $IncludeAnnotations is an array. + this.jsonWriter.WriteStartArray(); + + foreach (IEdmIncludeAnnotations includeAnnotation in includeAnnotations) + { + // Array items are objects + this.jsonWriter.WriteStartObject(); + + // MUST contain the member $TermNamespace + this.jsonWriter.WriteRequiredProperty("$TermNamespace", includeAnnotation.TermNamespace); + + // MAY contain the members $Qualifier + this.jsonWriter.WriteOptionalProperty("$Qualifier", includeAnnotation.Qualifier); + + // MAY contain the members $TargetNamespace + this.jsonWriter.WriteOptionalProperty("$TargetNamespace", includeAnnotation.TargetNamespace); + + this.jsonWriter.WriteEndObject(); + } + + this.jsonWriter.WriteEndArray(); + + return Task.CompletedTask; + } + + /// + /// Writes Annotations + /// + /// The Edm model. + /// The Edm vocabulary annotations. private void WriteAnnotations(IEdmModel model, IEdmVocabularyAnnotatable target) { var annotations = model.FindDeclaredVocabularyAnnotations(target); @@ -125,6 +244,22 @@ private void WriteAnnotations(IEdmModel model, IEdmVocabularyAnnotatable target) this.schemaWriter.WriteVocabularyAnnotationElementEnd(annotation, true); } } + + /// + /// Asynchronously Writes Annotations + /// + /// The Edm model. + /// The Edm vocabulary annotations. + /// Task represents an asynchronous operation that may or may not return a result. + private async Task WriteAnnotationsAsync(IEdmModel model, IEdmVocabularyAnnotatable target) + { + var annotations = model.FindDeclaredVocabularyAnnotations(target); + foreach (IEdmVocabularyAnnotation annotation in annotations) + { + await this.schemaWriter.WriteVocabularyAnnotationElementHeaderAsync(annotation, true).ConfigureAwait(false); + await this.schemaWriter.WriteVocabularyAnnotationElementEndAsync(annotation, true).ConfigureAwait(false); + } + } } } #endif \ No newline at end of file diff --git a/src/Microsoft.OData.Edm/Csdl/Serialization/EdmModelReferenceElementsXmlVisitor.cs b/src/Microsoft.OData.Edm/Csdl/Serialization/EdmModelReferenceElementsXmlVisitor.cs index 9869447e6e..577f8b769c 100644 --- a/src/Microsoft.OData.Edm/Csdl/Serialization/EdmModelReferenceElementsXmlVisitor.cs +++ b/src/Microsoft.OData.Edm/Csdl/Serialization/EdmModelReferenceElementsXmlVisitor.cs @@ -6,6 +6,7 @@ using System; using System.Collections.Generic; +using System.Threading.Tasks; using System.Xml; using Microsoft.OData.Edm.Vocabularies; @@ -53,6 +54,41 @@ internal void VisitEdmReferences(IEdmModel model, IEdmReference reference) } + /// + /// Asynchronously visit Edm references and Write includes and annotations. + /// + /// The Edm model. + /// The Edm reference. + /// Task represents an asynchronous operation. + internal async Task VisitEdmReferencesAsync(IEdmModel model, IEdmReference reference) + { + await this.schemaWriter.WriteReferenceElementHeaderAsync(reference).ConfigureAwait(false); + + if (reference.Includes != null) + { + foreach (IEdmInclude include in reference.Includes) + { + await this.schemaWriter.WritIncludeElementHeaderAsync(include).ConfigureAwait(false); + + await WriteAnnotationsAsync(model, include).ConfigureAwait(false); + + await this.schemaWriter.WriteIncludeElementEndAsync(include).ConfigureAwait(false); + } + } + + if (reference.IncludeAnnotations != null) + { + foreach (IEdmIncludeAnnotations includeAnnotations in reference.IncludeAnnotations) + { + await this.schemaWriter.WriteIncludeAnnotationsElementAsync(includeAnnotations).ConfigureAwait(false); + } + } + + await WriteAnnotationsAsync(model, reference).ConfigureAwait(false); + await this.schemaWriter.WriteReferenceElementEndAsync(reference).ConfigureAwait(false); + + } + private void WriteAnnotations(IEdmModel model, IEdmVocabularyAnnotatable target) { var annotations = model.FindDeclaredVocabularyAnnotations(target); @@ -63,6 +99,22 @@ private void WriteAnnotations(IEdmModel model, IEdmVocabularyAnnotatable target) } } + /// + /// Asynchronously write annotations. + /// + /// The Edm model. + /// The Edm vocabulary annotations. + /// Task represents an asynchronous operation. + private async Task WriteAnnotationsAsync(IEdmModel model, IEdmVocabularyAnnotatable target) + { + var annotations = model.FindDeclaredVocabularyAnnotations(target); + foreach (IEdmVocabularyAnnotation annotation in annotations) + { + await this.schemaWriter.WriteVocabularyAnnotationElementHeaderAsync(annotation, true).ConfigureAwait(false); + await this.schemaWriter.WriteVocabularyAnnotationElementEndAsync(annotation, true).ConfigureAwait(false); + } + } + #endregion } } \ No newline at end of file diff --git a/src/Microsoft.OData.Edm/EdmModelVisitor.cs b/src/Microsoft.OData.Edm/EdmModelVisitor.cs index 2477986ff1..786a0bc2fe 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.Threading.Tasks; using Microsoft.OData.Edm.Vocabularies; namespace Microsoft.OData.Edm @@ -228,6 +229,33 @@ public virtual void VisitEntityContainerElements(IEnumerable elements) + { + foreach (IEdmEntityContainerElement element in elements) + { + switch (element.ContainerElementKind) + { + case EdmContainerElementKind.EntitySet: + await this.ProcessEntitySetAsync((IEdmEntitySet)element); + break; + case EdmContainerElementKind.Singleton: + await this.ProcessSingletonAsync((IEdmSingleton)element); + break; + case EdmContainerElementKind.ActionImport: + await this.ProcessActionImportAsync((IEdmActionImport)element); + break; + case EdmContainerElementKind.FunctionImport: + await this.ProcessFunctionImportAsync((IEdmFunctionImport)element); + break; + case EdmContainerElementKind.None: + await this.ProcessEntityContainerElementAsync(element); + break; + default: + throw new InvalidOperationException(Edm.Strings.UnknownEnumVal_ContainerElementKind(element.ContainerElementKind.ToString())); + } + } + } + #endregion #region Type References @@ -427,21 +455,45 @@ protected virtual void ProcessElement(IEdmElement element) this.VisitAnnotations(this.Model.DirectValueAnnotations(element)); } + protected virtual Task ProcessElementAsync(IEdmElement element) + { + // TODO: DirectValueAnnotationsInMainSchema (not including those in referenced schemas) + this.VisitAnnotations(this.Model.DirectValueAnnotations(element)); + + return Task.FromResult(0); + } + protected virtual void ProcessNamedElement(IEdmNamedElement element) { this.ProcessElement(element); } + protected virtual async Task ProcessNamedElementAsync(IEdmNamedElement element) + { + await this.ProcessElementAsync(element); + } + protected virtual void ProcessSchemaElement(IEdmSchemaElement element) { this.ProcessVocabularyAnnotatable(element); this.ProcessNamedElement(element); } + protected virtual async Task ProcessSchemaElementAsync(IEdmSchemaElement element) + { + await this.ProcessVocabularyAnnotatableAsync(element); + await this.ProcessNamedElementAsync(element); + } + protected virtual void ProcessVocabularyAnnotatable(IEdmVocabularyAnnotatable annotatable) { } + protected virtual Task ProcessVocabularyAnnotatableAsync(IEdmVocabularyAnnotatable annotatable) + { + return Task.FromResult(0); + } + #endregion #region Type References @@ -451,83 +503,165 @@ protected virtual void ProcessComplexTypeReference(IEdmComplexTypeReference refe this.ProcessStructuredTypeReference(reference); } + protected virtual async Task ProcessComplexTypeReferenceAsync(IEdmComplexTypeReference reference) + { + await this.ProcessStructuredTypeReferenceAsync(reference); + } + protected virtual void ProcessEntityTypeReference(IEdmEntityTypeReference reference) { this.ProcessStructuredTypeReference(reference); } + protected virtual async Task ProcessEntityTypeReferenceAsync(IEdmEntityTypeReference reference) + { + await this.ProcessStructuredTypeReferenceAsync(reference); + } + protected virtual void ProcessEntityReferenceTypeReference(IEdmEntityReferenceTypeReference reference) { this.ProcessTypeReference(reference); this.ProcessEntityReferenceType(reference.EntityReferenceDefinition()); } + protected virtual async Task ProcessEntityReferenceTypeReferenceAsync(IEdmEntityReferenceTypeReference reference) + { + await this.ProcessTypeReferenceAsync(reference); + await this.ProcessEntityReferenceTypeAsync(reference.EntityReferenceDefinition()); + } + protected virtual void ProcessCollectionTypeReference(IEdmCollectionTypeReference reference) { this.ProcessTypeReference(reference); this.ProcessCollectionType(reference.CollectionDefinition()); } + protected virtual async Task ProcessCollectionTypeReferenceAsync(IEdmCollectionTypeReference reference) + { + await this.ProcessTypeReferenceAsync(reference); + await this.ProcessCollectionTypeAsync(reference.CollectionDefinition()); + } + protected virtual void ProcessEnumTypeReference(IEdmEnumTypeReference reference) { this.ProcessTypeReference(reference); } + protected virtual async Task ProcessEnumTypeReferenceAsync(IEdmEnumTypeReference reference) + { + await this.ProcessTypeReferenceAsync(reference); + } + protected virtual void ProcessTypeDefinitionReference(IEdmTypeDefinitionReference reference) { this.ProcessTypeReference(reference); } + protected virtual async Task ProcessTypeDefinitionReferenceAsync(IEdmTypeDefinitionReference reference) + { + await this.ProcessTypeReferenceAsync(reference); + } + protected virtual void ProcessBinaryTypeReference(IEdmBinaryTypeReference reference) { this.ProcessPrimitiveTypeReference(reference); } + protected virtual async Task ProcessBinaryTypeReferenceAsync(IEdmBinaryTypeReference reference) + { + await this.ProcessPrimitiveTypeReferenceAsync(reference); + } + protected virtual void ProcessDecimalTypeReference(IEdmDecimalTypeReference reference) { this.ProcessPrimitiveTypeReference(reference); } + protected virtual async Task ProcessDecimalTypeReferenceAsync(IEdmDecimalTypeReference reference) + { + await this.ProcessPrimitiveTypeReferenceAsync(reference); + } + protected virtual void ProcessSpatialTypeReference(IEdmSpatialTypeReference reference) { this.ProcessPrimitiveTypeReference(reference); } + protected virtual async Task ProcessSpatialTypeReferenceAsync(IEdmSpatialTypeReference reference) + { + await this.ProcessPrimitiveTypeReferenceAsync(reference); + } + protected virtual void ProcessStringTypeReference(IEdmStringTypeReference reference) { this.ProcessPrimitiveTypeReference(reference); } + protected virtual async Task ProcessStringTypeReferenceAsync(IEdmStringTypeReference reference) + { + await this.ProcessPrimitiveTypeReferenceAsync(reference); + } + protected virtual void ProcessTemporalTypeReference(IEdmTemporalTypeReference reference) { this.ProcessPrimitiveTypeReference(reference); } + protected virtual async Task ProcessTemporalTypeReferenceAsync(IEdmTemporalTypeReference reference) + { + await this.ProcessPrimitiveTypeReferenceAsync(reference); + } + protected virtual void ProcessPrimitiveTypeReference(IEdmPrimitiveTypeReference reference) { this.ProcessTypeReference(reference); } + protected virtual async Task ProcessPrimitiveTypeReferenceAsync(IEdmPrimitiveTypeReference reference) + { + await this.ProcessTypeReferenceAsync(reference); + } + protected virtual void ProcessStructuredTypeReference(IEdmStructuredTypeReference reference) { this.ProcessTypeReference(reference); } + protected virtual async Task ProcessStructuredTypeReferenceAsync(IEdmStructuredTypeReference reference) + { + await this.ProcessTypeReferenceAsync(reference); + } + protected virtual void ProcessTypeReference(IEdmTypeReference element) { this.ProcessElement(element); } + protected virtual async Task ProcessTypeReferenceAsync(IEdmTypeReference element) + { + await this.ProcessElementAsync(element); + } + protected virtual void ProcessPathTypeReference(IEdmPathTypeReference reference) { this.ProcessTypeReference(reference); } + protected virtual async Task ProcessPathTypeReferenceAsync(IEdmPathTypeReference reference) + { + await this.ProcessTypeReferenceAsync(reference); + } + protected virtual void ProcessUntypedTypeReference(IEdmUntypedTypeReference reference) { this.ProcessTypeReference(reference); } + protected virtual async Task ProcessUntypedTypeReferenceAsync(IEdmUntypedTypeReference reference) + { + await this.ProcessTypeReferenceAsync(reference); + } + #endregion #region Terms @@ -538,6 +672,12 @@ protected virtual void ProcessTerm(IEdmTerm term) this.VisitTypeReference(term.Type); } + protected virtual async Task ProcessTermAsync(IEdmTerm term) + { + await this.ProcessSchemaElementAsync(term); + this.VisitTypeReference(term.Type); + } + #endregion #region Type Definitions @@ -549,6 +689,13 @@ protected virtual void ProcessComplexType(IEdmComplexType definition) this.ProcessSchemaType(definition); } + protected virtual async Task ProcessComplexTypeAsync(IEdmComplexType definition) + { + await this.ProcessSchemaElementAsync(definition); + await this.ProcessStructuredTypeAsync(definition); + await this.ProcessSchemaTypeAsync(definition); + } + protected virtual void ProcessEntityType(IEdmEntityType definition) { this.ProcessSchemaElement(definition); @@ -556,6 +703,13 @@ protected virtual void ProcessEntityType(IEdmEntityType definition) this.ProcessSchemaType(definition); } + protected virtual async Task ProcessEntityTypeAsync(IEdmEntityType definition) + { + await this.ProcessSchemaElementAsync(definition); + await this.ProcessStructuredTypeAsync(definition); + await this.ProcessSchemaTypeAsync(definition); + } + protected virtual void ProcessCollectionType(IEdmCollectionType definition) { this.ProcessElement(definition); @@ -563,6 +717,13 @@ protected virtual void ProcessCollectionType(IEdmCollectionType definition) this.VisitTypeReference(definition.ElementType); } + protected virtual async Task ProcessCollectionTypeAsync(IEdmCollectionType definition) + { + await this.ProcessElementAsync(definition); + await this.ProcessTypeAsync(definition); + this.VisitTypeReference(definition.ElementType); + } + protected virtual void ProcessEnumType(IEdmEnumType definition) { this.ProcessSchemaElement(definition); @@ -571,6 +732,14 @@ protected virtual void ProcessEnumType(IEdmEnumType definition) this.VisitEnumMembers(definition.Members); } + protected virtual async Task ProcessEnumTypeAsync(IEdmEnumType definition) + { + await this.ProcessSchemaElementAsync(definition); + await this.ProcessTypeAsync(definition); + await this.ProcessSchemaTypeAsync(definition); + this.VisitEnumMembers(definition.Members); + } + protected virtual void ProcessTypeDefinition(IEdmTypeDefinition definition) { this.ProcessSchemaElement(definition); @@ -578,27 +747,57 @@ protected virtual void ProcessTypeDefinition(IEdmTypeDefinition definition) this.ProcessSchemaType(definition); } + protected virtual async Task ProcessTypeDefinitionAsync(IEdmTypeDefinition definition) + { + await this.ProcessSchemaElementAsync(definition); + await this.ProcessTypeAsync(definition); + await this.ProcessSchemaTypeAsync(definition); + } + protected virtual void ProcessEntityReferenceType(IEdmEntityReferenceType definition) { this.ProcessElement(definition); this.ProcessType(definition); } + protected virtual async Task ProcessEntityReferenceTypeAsync(IEdmEntityReferenceType definition) + { + await this.ProcessElementAsync(definition); + await this.ProcessTypeAsync(definition); + } + protected virtual void ProcessStructuredType(IEdmStructuredType definition) { this.ProcessType(definition); this.VisitProperties(definition.DeclaredProperties); } + protected virtual async Task ProcessStructuredTypeAsync(IEdmStructuredType definition) + { + await this.ProcessTypeAsync(definition); + this.VisitProperties(definition.DeclaredProperties); + } + protected virtual void ProcessSchemaType(IEdmSchemaType type) { // Do not visit type or schema element, because all types will do that on their own. } + protected virtual Task ProcessSchemaTypeAsync(IEdmSchemaType type) + { + // Do not visit type or schema element, because all types will do that on their own. + return Task.FromResult(0); + } + protected virtual void ProcessType(IEdmType definition) { } + protected virtual Task ProcessTypeAsync(IEdmType definition) + { + return Task.FromResult(0); + } + #endregion #region Definition Components @@ -608,11 +807,21 @@ protected virtual void ProcessNavigationProperty(IEdmNavigationProperty property this.ProcessProperty(property); } + protected virtual async Task ProcessNavigationPropertyAsync(IEdmNavigationProperty property) + { + await this.ProcessPropertyAsync(property); + } + protected virtual void ProcessStructuralProperty(IEdmStructuralProperty property) { this.ProcessProperty(property); } + protected virtual async Task ProcessStructuralPropertyAsync(IEdmStructuralProperty property) + { + await this.ProcessPropertyAsync(property); + } + protected virtual void ProcessProperty(IEdmProperty property) { this.ProcessVocabularyAnnotatable(property); @@ -620,11 +829,23 @@ protected virtual void ProcessProperty(IEdmProperty property) this.VisitTypeReference(property.Type); } + protected virtual async Task ProcessPropertyAsync(IEdmProperty property) + { + await this.ProcessVocabularyAnnotatableAsync(property); + await this.ProcessNamedElementAsync(property); + this.VisitTypeReference(property.Type); + } + protected virtual void ProcessEnumMember(IEdmEnumMember enumMember) { this.ProcessNamedElement(enumMember); } + protected virtual async Task ProcessEnumMemberAsync(IEdmEnumMember enumMember) + { + await this.ProcessNamedElementAsync(enumMember); + } + #endregion #region Annotations @@ -634,17 +855,33 @@ protected virtual void ProcessVocabularyAnnotation(IEdmVocabularyAnnotation anno this.ProcessElement(annotation); } + protected virtual async Task ProcessVocabularyAnnotationAsync(IEdmVocabularyAnnotation annotation) + { + await this.ProcessElementAsync(annotation); + } + protected virtual void ProcessImmediateValueAnnotation(IEdmDirectValueAnnotation annotation) { this.ProcessNamedElement(annotation); } + protected virtual async Task ProcessImmediateValueAnnotationAsync(IEdmDirectValueAnnotation annotation) + { + await this.ProcessNamedElementAsync(annotation); + } + protected virtual void ProcessAnnotation(IEdmVocabularyAnnotation annotation) { this.ProcessVocabularyAnnotation(annotation); this.VisitExpression(annotation.Value); } + protected virtual async Task ProcessAnnotationAsync(IEdmVocabularyAnnotation annotation) + { + await this.ProcessVocabularyAnnotationAsync(annotation); + this.VisitExpression(annotation.Value); + } + protected virtual void ProcessPropertyValueBinding(IEdmPropertyValueBinding binding) { this.VisitExpression(binding.Value); @@ -658,16 +895,31 @@ protected virtual void ProcessExpression(IEdmExpression expression) { } + protected virtual Task ProcessExpressionAsync(IEdmExpression expression) + { + return Task.FromResult(0); + } + protected virtual void ProcessStringConstantExpression(IEdmStringConstantExpression expression) { this.ProcessExpression(expression); } + protected virtual async Task ProcessStringConstantExpressionAsync(IEdmStringConstantExpression expression) + { + await this.ProcessExpressionAsync(expression); + } + protected virtual void ProcessBinaryConstantExpression(IEdmBinaryConstantExpression expression) { this.ProcessExpression(expression); } + protected virtual async Task ProcessBinaryConstantExpressionAsync(IEdmBinaryConstantExpression expression) + { + await this.ProcessExpressionAsync(expression); + } + protected virtual void ProcessRecordExpression(IEdmRecordExpression expression) { this.ProcessExpression(expression); @@ -679,37 +931,79 @@ protected virtual void ProcessRecordExpression(IEdmRecordExpression expression) this.VisitPropertyConstructors(expression.Properties); } + protected virtual async Task ProcessRecordExpressionAsync(IEdmRecordExpression expression) + { + await this.ProcessExpressionAsync(expression); + if (expression.DeclaredType != null) + { + this.VisitTypeReference(expression.DeclaredType); + } + + this.VisitPropertyConstructors(expression.Properties); + } + protected virtual void ProcessPathExpression(IEdmPathExpression expression) { this.ProcessExpression(expression); } + protected virtual async Task ProcessPathExpressionAsync(IEdmPathExpression expression) + { + await this.ProcessExpressionAsync(expression); + } + protected virtual void ProcessPropertyPathExpression(IEdmPathExpression expression) { this.ProcessExpression(expression); } + protected virtual async Task ProcessPropertyPathExpressionAsync(IEdmPathExpression expression) + { + await this.ProcessExpressionAsync(expression); + } + protected virtual void ProcessNavigationPropertyPathExpression(IEdmPathExpression expression) { this.ProcessExpression(expression); } + protected virtual async Task ProcessNavigationPropertyPathExpressionAsync(IEdmPathExpression expression) + { + await this.ProcessExpressionAsync(expression); + } + protected virtual void ProcessAnnotationPathExpression(IEdmPathExpression expression) { this.ProcessExpression(expression); } + protected virtual async Task ProcessAnnotationPathExpressionAsync(IEdmPathExpression expression) + { + await this.ProcessExpressionAsync(expression); + } + protected virtual void ProcessCollectionExpression(IEdmCollectionExpression expression) { this.ProcessExpression(expression); this.VisitExpressions(expression.Elements); } + protected virtual async Task ProcessCollectionExpressionAsync(IEdmCollectionExpression expression) + { + await this.ProcessExpressionAsync(expression); + this.VisitExpressions(expression.Elements); + } + protected virtual void ProcessLabeledExpressionReferenceExpression(IEdmLabeledExpressionReferenceExpression expression) { this.ProcessExpression(expression); } + protected virtual async Task ProcessLabeledExpressionReferenceExpressionAsync(IEdmLabeledExpressionReferenceExpression expression) + { + await this.ProcessExpressionAsync(expression); + } + protected virtual void ProcessIsTypeExpression(IEdmIsTypeExpression expression) { this.ProcessExpression(expression); @@ -717,11 +1011,23 @@ protected virtual void ProcessIsTypeExpression(IEdmIsTypeExpression expression) this.VisitExpression(expression.Operand); } + protected virtual async Task ProcessIsTypeExpressionAsync(IEdmIsTypeExpression expression) + { + await this.ProcessExpressionAsync(expression); + this.VisitTypeReference(expression.Type); + this.VisitExpression(expression.Operand); + } + protected virtual void ProcessIntegerConstantExpression(IEdmIntegerConstantExpression expression) { this.ProcessExpression(expression); } + protected virtual async Task ProcessIntegerConstantExpressionAsync(IEdmIntegerConstantExpression expression) + { + await this.ProcessExpressionAsync(expression); + } + protected virtual void ProcessIfExpression(IEdmIfExpression expression) { this.ProcessExpression(expression); @@ -730,57 +1036,116 @@ protected virtual void ProcessIfExpression(IEdmIfExpression expression) this.VisitExpression(expression.FalseExpression); } + protected virtual async Task ProcessIfExpressionAsync(IEdmIfExpression expression) + { + await this.ProcessExpressionAsync(expression); + this.VisitExpression(expression.TestExpression); + this.VisitExpression(expression.TrueExpression); + this.VisitExpression(expression.FalseExpression); + } + protected virtual void ProcessFunctionApplicationExpression(IEdmApplyExpression expression) { this.ProcessExpression(expression); this.VisitExpressions(expression.Arguments); } + protected virtual async Task ProcessFunctionApplicationExpressionAsync(IEdmApplyExpression expression) + { + await this.ProcessExpressionAsync(expression); + this.VisitExpressions(expression.Arguments); + } + protected virtual void ProcessFloatingConstantExpression(IEdmFloatingConstantExpression expression) { this.ProcessExpression(expression); } + protected virtual async Task ProcessFloatingConstantExpressionAsync(IEdmFloatingConstantExpression expression) + { + await this.ProcessExpressionAsync(expression); + } + protected virtual void ProcessGuidConstantExpression(IEdmGuidConstantExpression expression) { this.ProcessExpression(expression); } + protected virtual async Task ProcessGuidConstantExpressionAsync(IEdmGuidConstantExpression expression) + { + await this.ProcessExpressionAsync(expression); + } + protected virtual void ProcessEnumMemberExpression(IEdmEnumMemberExpression expression) { this.ProcessExpression(expression); } + protected virtual async Task ProcessEnumMemberExpressionAsync(IEdmEnumMemberExpression expression) + { + await this.ProcessExpressionAsync(expression); + } + protected virtual void ProcessDecimalConstantExpression(IEdmDecimalConstantExpression expression) { this.ProcessExpression(expression); } + protected virtual async Task ProcessDecimalConstantExpressionAsync(IEdmDecimalConstantExpression expression) + { + await this.ProcessExpressionAsync(expression); + } + protected virtual void ProcessDateConstantExpression(IEdmDateConstantExpression expression) { this.ProcessExpression(expression); } + protected virtual async Task ProcessDateConstantExpressionAsync(IEdmDateConstantExpression expression) + { + await this.ProcessExpressionAsync(expression); + } + protected virtual void ProcessTimeOfDayConstantExpression(IEdmTimeOfDayConstantExpression expression) { this.ProcessExpression(expression); } + protected virtual async Task ProcessTimeOfDayConstantExpressionAsync(IEdmTimeOfDayConstantExpression expression) + { + await this.ProcessExpressionAsync(expression); + } + protected virtual void ProcessDateTimeOffsetConstantExpression(IEdmDateTimeOffsetConstantExpression expression) { this.ProcessExpression(expression); } + protected virtual async Task ProcessDateTimeOffsetConstantExpressionAsync(IEdmDateTimeOffsetConstantExpression expression) + { + await this.ProcessExpressionAsync(expression); + } + protected virtual void ProcessDurationConstantExpression(IEdmDurationConstantExpression expression) { this.ProcessExpression(expression); } + protected virtual async Task ProcessDurationConstantExpressionAsync(IEdmDurationConstantExpression expression) + { + await this.ProcessExpressionAsync(expression); + } + protected virtual void ProcessBooleanConstantExpression(IEdmBooleanConstantExpression expression) { this.ProcessExpression(expression); } + protected virtual async Task ProcessBooleanConstantExpressionAsync(IEdmBooleanConstantExpression expression) + { + await this.ProcessExpressionAsync(expression); + } + protected virtual void ProcessCastExpression(IEdmCastExpression expression) { this.ProcessExpression(expression); @@ -788,21 +1153,48 @@ protected virtual void ProcessCastExpression(IEdmCastExpression expression) this.VisitExpression(expression.Operand); } + protected virtual async Task ProcessCastExpressionAsync(IEdmCastExpression expression) + { + await this.ProcessExpressionAsync(expression); + this.VisitTypeReference(expression.Type); + this.VisitExpression(expression.Operand); + } + + protected virtual void ProcessLabeledExpression(IEdmLabeledExpression element) { this.VisitExpression(element.Expression); } + protected virtual Task ProcessLabeledExpressionAsync(IEdmLabeledExpression element) + { + this.VisitExpression(element.Expression); + + return Task.FromResult(0); + } + protected virtual void ProcessPropertyConstructor(IEdmPropertyConstructor constructor) { this.VisitExpression(constructor.Value); } + protected virtual Task ProcessPropertyConstructorAsync(IEdmPropertyConstructor constructor) + { + this.VisitExpression(constructor.Value); + + return Task.FromResult(0); + } + protected virtual void ProcessNullConstantExpression(IEdmNullExpression expression) { this.ProcessExpression(expression); } + protected virtual async Task ProcessNullConstantExpressionAsync(IEdmNullExpression expression) + { + await this.ProcessExpressionAsync(expression); + } + #endregion #region Data Model @@ -814,21 +1206,43 @@ protected virtual void ProcessEntityContainer(IEdmEntityContainer container) this.VisitEntityContainerElements(container.Elements); } + protected virtual async Task ProcessEntityContainerAsync(IEdmEntityContainer container) + { + await this.ProcessVocabularyAnnotatableAsync(container); + await this.ProcessNamedElementAsync(container); + await this.VisitEntityContainerElementsAsync(container.Elements); + } + protected virtual void ProcessEntityContainerElement(IEdmEntityContainerElement element) { this.ProcessNamedElement(element); } + protected virtual async Task ProcessEntityContainerElementAsync(IEdmEntityContainerElement element) + { + await this.ProcessNamedElementAsync(element); + } + protected virtual void ProcessEntitySet(IEdmEntitySet set) { this.ProcessEntityContainerElement(set); } + protected virtual async Task ProcessEntitySetAsync(IEdmEntitySet set) + { + await this.ProcessEntityContainerElementAsync(set); + } + protected virtual void ProcessSingleton(IEdmSingleton singleton) { this.ProcessEntityContainerElement(singleton); } + protected virtual async Task ProcessSingletonAsync(IEdmSingleton singleton) + { + await this.ProcessEntityContainerElementAsync(singleton); + } + #endregion #region Operation Related @@ -839,22 +1253,44 @@ protected virtual void ProcessAction(IEdmAction action) this.ProcessOperation(action); } + protected virtual async Task ProcessActionAsync(IEdmAction action) + { + await this.ProcessSchemaElementAsync(action); + await this.ProcessOperationAsync(action); + } + protected virtual void ProcessFunction(IEdmFunction function) { this.ProcessSchemaElement(function); this.ProcessOperation(function); } + protected virtual async Task ProcessFunctionAsync(IEdmFunction function) + { + await this.ProcessSchemaElementAsync(function); + await this.ProcessOperationAsync(function); + } + protected virtual void ProcessActionImport(IEdmActionImport actionImport) { this.ProcessEntityContainerElement(actionImport); } + protected virtual async Task ProcessActionImportAsync(IEdmActionImport actionImport) + { + await this.ProcessEntityContainerElementAsync(actionImport); + } + protected virtual void ProcessFunctionImport(IEdmFunctionImport functionImport) { this.ProcessEntityContainerElement(functionImport); } + protected virtual async Task ProcessFunctionImportAsync(IEdmFunctionImport functionImport) + { + await this.ProcessEntityContainerElementAsync(functionImport); + } + protected virtual void ProcessOperation(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. @@ -864,6 +1300,15 @@ protected virtual void ProcessOperation(IEdmOperation operation) this.ProcessOperationReturn(operationReturn); } + 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); + + IEdmOperationReturn operationReturn = operation.GetReturn(); + await this.ProcessOperationReturnAsync(operationReturn); + } + protected virtual void ProcessOperationParameter(IEdmOperationParameter parameter) { this.ProcessVocabularyAnnotatable(parameter); @@ -871,6 +1316,13 @@ protected virtual void ProcessOperationParameter(IEdmOperationParameter paramete this.VisitTypeReference(parameter.Type); } + protected virtual async Task ProcessOperationParameterAsync(IEdmOperationParameter parameter) + { + await this.ProcessVocabularyAnnotatableAsync(parameter); + await this.ProcessNamedElementAsync(parameter); + this.VisitTypeReference(parameter.Type); + } + protected virtual void ProcessOperationReturn(IEdmOperationReturn operationReturn) { if (operationReturn == null) @@ -881,6 +1333,17 @@ protected virtual void ProcessOperationReturn(IEdmOperationReturn operationRetur this.ProcessVocabularyAnnotatable(operationReturn); this.VisitTypeReference(operationReturn.Type); } + + protected virtual async Task ProcessOperationReturnAsync(IEdmOperationReturn operationReturn) + { + if (operationReturn == null) + { + return; + } + + await this.ProcessVocabularyAnnotatableAsync(operationReturn); + this.VisitTypeReference(operationReturn.Type); + } #endregion #endregion diff --git a/src/Microsoft.OData.Edm/PublicAPI/net45/PublicAPI.Unshipped.txt b/src/Microsoft.OData.Edm/PublicAPI/net45/PublicAPI.Unshipped.txt index e69de29bb2..6cd358ac8a 100644 --- a/src/Microsoft.OData.Edm/PublicAPI/net45/PublicAPI.Unshipped.txt +++ b/src/Microsoft.OData.Edm/PublicAPI/net45/PublicAPI.Unshipped.txt @@ -0,0 +1,4 @@ +static Microsoft.OData.Edm.Csdl.CsdlWriter.TryWriteCsdlAsync(Microsoft.OData.Edm.IEdmModel model, System.Xml.XmlWriter writer, Microsoft.OData.Edm.Csdl.CsdlTarget target) -> System.Threading.Tasks.Task>> +static Microsoft.OData.Edm.Csdl.SchemaWriter.TryWriteSchemaAsync(this Microsoft.OData.Edm.IEdmModel model, System.Func writerProvider) -> System.Threading.Tasks.Task>> +static Microsoft.OData.Edm.Csdl.SchemaWriter.TryWriteSchemaAsync(this Microsoft.OData.Edm.IEdmModel model, System.Xml.XmlWriter writer) -> System.Threading.Tasks.Task>> +virtual Microsoft.OData.Edm.Csdl.CsdlWriter.WriteCsdlAsync() -> System.Threading.Tasks.Task \ No newline at end of file diff --git a/src/Microsoft.OData.Edm/PublicAPI/netstandard1.1/PublicAPI.Unshipped.txt b/src/Microsoft.OData.Edm/PublicAPI/netstandard1.1/PublicAPI.Unshipped.txt index e69de29bb2..6cd358ac8a 100644 --- a/src/Microsoft.OData.Edm/PublicAPI/netstandard1.1/PublicAPI.Unshipped.txt +++ b/src/Microsoft.OData.Edm/PublicAPI/netstandard1.1/PublicAPI.Unshipped.txt @@ -0,0 +1,4 @@ +static Microsoft.OData.Edm.Csdl.CsdlWriter.TryWriteCsdlAsync(Microsoft.OData.Edm.IEdmModel model, System.Xml.XmlWriter writer, Microsoft.OData.Edm.Csdl.CsdlTarget target) -> System.Threading.Tasks.Task>> +static Microsoft.OData.Edm.Csdl.SchemaWriter.TryWriteSchemaAsync(this Microsoft.OData.Edm.IEdmModel model, System.Func writerProvider) -> System.Threading.Tasks.Task>> +static Microsoft.OData.Edm.Csdl.SchemaWriter.TryWriteSchemaAsync(this Microsoft.OData.Edm.IEdmModel model, System.Xml.XmlWriter writer) -> System.Threading.Tasks.Task>> +virtual Microsoft.OData.Edm.Csdl.CsdlWriter.WriteCsdlAsync() -> System.Threading.Tasks.Task \ No newline at end of file diff --git a/src/Microsoft.OData.Edm/PublicAPI/netstandard2.0/PublicAPI.Unshipped.txt b/src/Microsoft.OData.Edm/PublicAPI/netstandard2.0/PublicAPI.Unshipped.txt index e69de29bb2..2bf48f29ae 100644 --- a/src/Microsoft.OData.Edm/PublicAPI/netstandard2.0/PublicAPI.Unshipped.txt +++ b/src/Microsoft.OData.Edm/PublicAPI/netstandard2.0/PublicAPI.Unshipped.txt @@ -0,0 +1,6 @@ +static Microsoft.OData.Edm.Csdl.CsdlWriter.TryWriteCsdlAsync(Microsoft.OData.Edm.IEdmModel model, System.Text.Json.Utf8JsonWriter writer) -> System.Threading.Tasks.Task<(bool, System.Collections.Generic.IEnumerable)> +static Microsoft.OData.Edm.Csdl.CsdlWriter.TryWriteCsdlAsync(Microsoft.OData.Edm.IEdmModel model, System.Text.Json.Utf8JsonWriter writer, Microsoft.OData.Edm.Csdl.CsdlJsonWriterSettings settings) -> System.Threading.Tasks.Task<(bool, System.Collections.Generic.IEnumerable)> +static Microsoft.OData.Edm.Csdl.CsdlWriter.TryWriteCsdlAsync(Microsoft.OData.Edm.IEdmModel model, System.Xml.XmlWriter writer, Microsoft.OData.Edm.Csdl.CsdlTarget target) -> System.Threading.Tasks.Task>> +static Microsoft.OData.Edm.Csdl.SchemaWriter.TryWriteSchemaAsync(this Microsoft.OData.Edm.IEdmModel model, System.Func writerProvider) -> System.Threading.Tasks.Task>> +static Microsoft.OData.Edm.Csdl.SchemaWriter.TryWriteSchemaAsync(this Microsoft.OData.Edm.IEdmModel model, System.Xml.XmlWriter writer) -> System.Threading.Tasks.Task>> +virtual Microsoft.OData.Edm.Csdl.CsdlWriter.WriteCsdlAsync() -> System.Threading.Tasks.Task \ 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 381e841e85..643a2f9edf 100644 --- a/test/FunctionalTests/Microsoft.OData.Core.Tests/ODataMessageWriterTests.cs +++ b/test/FunctionalTests/Microsoft.OData.Core.Tests/ODataMessageWriterTests.cs @@ -947,6 +947,69 @@ public async Task WriteMetadataDocumentAsync_WorksForJsonCsdl() } }", payload); } + + [Fact] + public async Task WriteMetadataDocumentAsync_WorksForJsonCsdl_WithNoSynchronousIOSupport() + { + // Arrange + IEdmModel edmModel = GetEdmModel(); + + // Act - JSON + string payload = await this.WriteAndGetPayloadAsync(edmModel, "application/json", async omWriter => + { + await omWriter.WriteMetadataDocumentAsync(); + }); + + // Assert + Assert.Equal(@"{ + ""$Version"": ""4.0"", + ""$EntityContainer"": ""NS.Container"", + ""NS"": { + ""Customer"": { + ""$Kind"": ""EntityType"", + ""$Key"": [ + ""Id"" + ], + ""Id"": { + ""$Type"": ""Edm.Int32"" + }, + ""Name"": {} + }, + ""Container"": { + ""$Kind"": ""EntityContainer"", + ""Customers"": { + ""$Collection"": true, + ""$Type"": ""NS.Customer"" + } + } + } +}", payload); + } + + [Fact] + public async Task WriteMetadataDocumentPayload_MustEqual_WriteMetadataDocumentAsyncPayload_ForJsonCsdl() + { + // Arrange + IEdmModel edmModel = GetEdmModel(); + + // Act + var contentType = "application/json"; + + // Json CSDL generated synchronously + string syncPayload = this.WriteAndGetPayload(edmModel, contentType, omWriter => + { + omWriter.WriteMetadataDocument(); + }); + + // Json CSDL generated asynchronously + string asyncPayload = await this.WriteAndGetPayloadAsync(edmModel, contentType, async omWriter => + { + await omWriter.WriteMetadataDocumentAsync(); + }); + + // Assert + Assert.Equal(syncPayload, asyncPayload); + } #endif [Fact] @@ -992,6 +1055,63 @@ public async Task WriteMetadataDocumentAsync_WorksForXmlCsdl() "", payload); } + [Fact] + public async Task WriteMetadataDocumentAsync_WorksForXmlCsdl_WithNoSynchronousIOSupport() + { + // Arrange + IEdmModel edmModel = GetEdmModel(); + + // Act - XML + string payload = await this.WriteAndGetPayloadAsync(edmModel, "application/xml", async omWriter => + { + await omWriter.WriteMetadataDocumentAsync(); + }); + + // Assert - XML + Assert.Equal("" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "", payload); + } + + [Fact] + public async Task WriteMetadataDocumentPayload_MustEqual_WriteMetadataDocumentAsyncPayload_ForXmlCsdl() + { + // Arrange + IEdmModel edmModel = GetEdmModel(); + + // Act + var contentType = "application/xml"; + + // XML CSDL generated synchronously + string syncPayload = this.WriteAndGetPayload(edmModel, contentType, omWriter => + { + omWriter.WriteMetadataDocument(); + }); + + // XML CSDL generated asynchronously + string asyncPayload = await this.WriteAndGetPayloadAsync(edmModel, contentType, async omWriter => + { + await omWriter.WriteMetadataDocumentAsync(); + }); + + // Assert + Assert.Equal(asyncPayload, syncPayload); + } + #region "DisposeAsync" #if NETCOREAPP3_1_OR_GREATER @@ -1081,6 +1201,32 @@ public async Task DisposeAsync_Should_Dispose_Stream_Asynchronously_AfterWriting Assert.True(stream.Disposed); } + [Fact] + public async Task DisposeAsync_Should_Dispose_Stream_Asynchronously_AfterWritingJsonMetadata_NoSyncSupport() + { + AsyncStream stream = new AsyncStream(new MemoryStream()); + InMemoryMessage message = new InMemoryMessage() + { + Stream = stream + }; + + IEdmModel edmModel = GetEdmModel(); + + ODataMessageWriterSettings writerSettings = new ODataMessageWriterSettings(); + writerSettings.EnableMessageStreamDisposal = true; + writerSettings.BaseUri = new Uri("http://www.example.com/"); + writerSettings.SetServiceDocumentUri(new Uri("http://www.example.com/")); + message.SetHeader("Content-Type", "application/json"); + + var msgWriter = new ODataMessageWriter((IODataResponseMessageAsync)message, writerSettings, edmModel); + + await msgWriter.WriteMetadataDocumentAsync(); + + await msgWriter.DisposeAsync(); + + Assert.True(stream.Disposed); + } + [Fact] public async Task DisposeAsync_Should_Dispose_Stream_Asynchronously_AfterWritingXmlMetadata() { @@ -1118,6 +1264,32 @@ public async Task DisposeAsync_Should_Dispose_Stream_Asynchronously_AfterWriting Assert.True(stream.Disposed); } + [Fact] + public async Task DisposeAsync_Should_Dispose_Stream_Asynchronously_AfterWritingXmlMetadata_NoSyncSupport() + { + AsyncStream stream = new AsyncStream(new MemoryStream()); + InMemoryMessage message = new InMemoryMessage() + { + Stream = stream + }; + + IEdmModel edmModel = GetEdmModel(); + + ODataMessageWriterSettings writerSettings = new ODataMessageWriterSettings(); + writerSettings.EnableMessageStreamDisposal = true; + writerSettings.BaseUri = new Uri("http://www.example.com/"); + writerSettings.SetServiceDocumentUri(new Uri("http://www.example.com/")); + message.SetHeader("Content-Type", "application/xml"); + + var msgWriter = new ODataMessageWriter((IODataResponseMessageAsync)message, writerSettings, edmModel); + + await msgWriter.WriteMetadataDocumentAsync(); + + await msgWriter.DisposeAsync(); + + Assert.True(stream.Disposed); + } + [Fact] public async void DisposeAsync_Should_Dispose_Stream_Asynchronously_AfterWritingRawValue() { diff --git a/test/FunctionalTests/Microsoft.OData.Core.Tests/ScenarioTests/Roundtrip/ContextUrlWriterReaderTests.Async.cs b/test/FunctionalTests/Microsoft.OData.Core.Tests/ScenarioTests/Roundtrip/ContextUrlWriterReaderTests.Async.cs new file mode 100644 index 0000000000..8b7b785502 --- /dev/null +++ b/test/FunctionalTests/Microsoft.OData.Core.Tests/ScenarioTests/Roundtrip/ContextUrlWriterReaderTests.Async.cs @@ -0,0 +1,335 @@ +//--------------------------------------------------------------------- +// +// Copyright (C) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information. +// +//--------------------------------------------------------------------- + +using Microsoft.OData.Edm; +using Microsoft.OData.Edm.Csdl; +using Microsoft.OData.Tests; +using Microsoft.OData.UriParser; +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Threading.Tasks; +using System.Xml; +using Xunit; + +namespace Microsoft.OData.Core.Tests.ScenarioTests.Roundtrip +{ + public partial class ContextUrlWriterReaderTests + { + private static readonly ODataVersion[] Versions = new ODataVersion[] { ODataVersion.V4, ODataVersion.V401 }; + + private const string TestNameSpace = "Microsoft.OData.Tests.ScenarioTests.Roundtrip"; + private const string TestContainerName = "InMemoryEntities"; + private const string TestBaseUri = "http://www.example.com/"; + + private EdmModel model; + + private EdmEntityType personType; + private EdmEntityType employeeType; + private EdmEntityType companyType; + private EdmEntityType workingGroupType; + private EdmEntityType peopleWorkingGroupsType; + private EdmEntityType productType; + private EdmEntityType productBookType; + private EdmEntityType productCdType; + private EdmEntityType locationType; + private EdmComplexType addressType; + private EdmEnumType accessLevelType; + + private EdmEntitySet employeeSet; + private EdmEntitySet companySet; + private EdmEntitySet peopleSet; + private EdmEntitySet workingGroupsSet; + private EdmSingleton peopleWorkingGroupsSingleton; + + private Uri testServiceRootUri; + + protected readonly ODataFormat[] mimeTypes = new ODataFormat[] + { + ODataFormat.Json + }; + + public ContextUrlWriterReaderTests() + { + this.testServiceRootUri = new Uri(TestBaseUri); + + this.model = new EdmModel(); + + var defaultContainer = new EdmEntityContainer(TestNameSpace, TestContainerName); + this.model.AddElement(defaultContainer); + + productType = new EdmEntityType(TestNameSpace, "Product"); + var productIdProperty = new EdmStructuralProperty(productType, "PersonId", EdmCoreModel.Instance.GetInt32(false)); + productType.AddProperty(productIdProperty); + productType.AddKeys(new IEdmStructuralProperty[] { productIdProperty }); + productType.AddProperty(new EdmStructuralProperty(productType, "Name", EdmCoreModel.Instance.GetString(false))); + this.model.AddElement(productType); + + productBookType = new EdmEntityType(TestNameSpace, "ProductBook", productType); + productBookType.AddProperty(new EdmStructuralProperty(productBookType, "Author", EdmCoreModel.Instance.GetString(false))); + this.model.AddElement(productBookType); + + productCdType = new EdmEntityType(TestNameSpace, "ProductCd", productType); + productCdType.AddProperty(new EdmStructuralProperty(productCdType, "Artist", EdmCoreModel.Instance.GetString(false))); + this.model.AddElement(productCdType); + + addressType = new EdmComplexType(TestNameSpace, "Address"); + addressType.AddProperty(new EdmStructuralProperty(addressType, "Street", EdmCoreModel.Instance.GetString(false))); + addressType.AddProperty(new EdmStructuralProperty(addressType, "City", EdmCoreModel.Instance.GetString(false))); + this.model.AddElement(addressType); + + var factoryAddressType = new EdmComplexType(TestNameSpace, "FactoryAddress", addressType, false); + factoryAddressType.AddProperty(new EdmStructuralProperty(factoryAddressType, "FactoryType", EdmCoreModel.Instance.GetString(false))); + factoryAddressType.AddProperty(new EdmStructuralProperty(factoryAddressType, "FactoryPhoneNumbers", new EdmCollectionTypeReference(new EdmCollectionType(EdmCoreModel.Instance.GetString(false))))); + this.model.AddElement(factoryAddressType); + + var manufactoryType = new EdmComplexType(TestNameSpace, "Manufactory"); + manufactoryType.AddProperty(new EdmStructuralProperty(manufactoryType, "ManufactoryAddress", new EdmComplexTypeReference(addressType, true))); + manufactoryType.AddProperty(new EdmStructuralProperty(manufactoryType, "Name", EdmCoreModel.Instance.GetString(false))); + manufactoryType.AddProperty(new EdmStructuralProperty(manufactoryType, "Numbers", new EdmCollectionTypeReference(new EdmCollectionType(EdmCoreModel.Instance.GetString(false))))); + this.model.AddElement(manufactoryType); + + var companyDivisionType = new EdmComplexType(TestNameSpace, "Division"); + companyDivisionType.AddProperty(new EdmStructuralProperty(companyDivisionType, "Name", EdmCoreModel.Instance.GetString(false))); + companyDivisionType.AddProperty(new EdmStructuralProperty(companyDivisionType, "Manufactory", new EdmComplexTypeReference(manufactoryType, true))); + this.model.AddElement(companyDivisionType); + + accessLevelType = new EdmEnumType(TestNameSpace, "AccessLevel", isFlags: true); + accessLevelType.AddMember("None", new EdmEnumMemberValue(0)); + accessLevelType.AddMember("Read", new EdmEnumMemberValue(1)); + accessLevelType.AddMember("Write", new EdmEnumMemberValue(2)); + accessLevelType.AddMember("Execute", new EdmEnumMemberValue(4)); + accessLevelType.AddMember("ReadWrite", new EdmEnumMemberValue(3)); + this.model.AddElement(accessLevelType); + + personType = new EdmEntityType(TestNameSpace, "Person", null, false, /*IsOpen*/ true); + var personIdProperty = new EdmStructuralProperty(personType, "PersonId", EdmCoreModel.Instance.GetInt32(false)); + personType.AddProperty(personIdProperty); + personType.AddKeys(new IEdmStructuralProperty[] { personIdProperty }); + personType.AddProperty(new EdmStructuralProperty(personType, "Name", EdmCoreModel.Instance.GetString(false))); + personType.AddProperty(new EdmStructuralProperty(personType, "Age", EdmCoreModel.Instance.GetInt32(false))); + personType.AddProperty(new EdmStructuralProperty(personType, "HomeAddress", new EdmComplexTypeReference(addressType, true))); + personType.AddProperty(new EdmStructuralProperty(personType, "PhoneNumbers", new EdmCollectionTypeReference(new EdmCollectionType(EdmCoreModel.Instance.GetString(false))))); + personType.AddProperty(new EdmStructuralProperty(personType, "Addresses", new EdmCollectionTypeReference(new EdmCollectionType(new EdmComplexTypeReference(addressType, true))))); + personType.AddProperty(new EdmStructuralProperty(personType, "UserAccess", new EdmEnumTypeReference(accessLevelType, true))); + personType.AddProperty(new EdmStructuralProperty(personType, "UserAccesses", new EdmCollectionTypeReference(new EdmCollectionType(new EdmEnumTypeReference(accessLevelType, true))))); + + this.model.AddElement(personType); + + employeeType = new EdmEntityType(TestNameSpace, "Employee", personType, false, true); + employeeType.AddProperty(new EdmStructuralProperty(employeeType, "DateHired", EdmCoreModel.Instance.GetDateTimeOffset(true))); + employeeType.AddProperty(new EdmStructuralProperty(employeeType, "WorkNumbers", new EdmCollectionTypeReference(new EdmCollectionType(EdmCoreModel.Instance.GetString(false))))); + this.model.AddElement(employeeType); + + companyType = new EdmEntityType(TestNameSpace, "Company"); + var companyIdProperty = new EdmStructuralProperty(companyType, "CompanyId", EdmCoreModel.Instance.GetInt32(false)); + companyType.AddProperty(companyIdProperty); + companyType.AddKeys(new IEdmStructuralProperty[] { companyIdProperty }); + companyType.AddProperty(new EdmStructuralProperty(companyType, "Name", EdmCoreModel.Instance.GetString(false))); + companyType.AddProperty(new EdmStructuralProperty(companyType, "Division", new EdmComplexTypeReference(companyDivisionType, true))); + this.model.AddElement(companyType); + + locationType = new EdmEntityType(TestNameSpace, "Location"); + var locationId = new EdmStructuralProperty(locationType, "LocationId", EdmCoreModel.Instance.GetInt32(false)); + locationType.AddProperty(locationId); + locationType.AddKeys(new IEdmStructuralProperty[] { locationId }); + locationType.AddProperty(new EdmStructuralProperty(locationType, "Name", EdmCoreModel.Instance.GetString(false))); + this.model.AddElement(locationType); + + workingGroupType = new EdmEntityType(TestNameSpace, "WorkingGroup"); + var workingGroupIdProperty = new EdmStructuralProperty(workingGroupType, "WorkingGroupId", EdmCoreModel.Instance.GetInt32(false)); + workingGroupType.AddProperty(workingGroupIdProperty); + workingGroupType.AddKeys(new IEdmStructuralProperty[] { workingGroupIdProperty }); + workingGroupType.AddProperty(new EdmStructuralProperty(workingGroupType, "Name", EdmCoreModel.Instance.GetString(false))); + this.model.AddElement(workingGroupType); + + peopleWorkingGroupsType = new EdmEntityType(TestNameSpace, "PeopleWorkingGroupsRoot"); + this.model.AddElement(peopleWorkingGroupsType); + + this.peopleSet = new EdmEntitySet(defaultContainer, "People", personType); + this.companySet = new EdmEntitySet(defaultContainer, "Companys", companyType); + this.employeeSet = new EdmEntitySet(defaultContainer, "Employees", employeeType); + this.workingGroupsSet = new EdmEntitySet(defaultContainer, "WorkingGroups", workingGroupType); + defaultContainer.AddElement(this.employeeSet); + defaultContainer.AddElement(this.peopleSet); + defaultContainer.AddElement(this.companySet); + defaultContainer.AddElement(this.workingGroupsSet); + + this.peopleWorkingGroupsSingleton = new EdmSingleton(defaultContainer, "PeopleWorkingGroups", peopleWorkingGroupsType); + defaultContainer.AddElement(this.peopleWorkingGroupsSingleton); + + var associatedCompanyNavigation = employeeType.AddUnidirectionalNavigation(new EdmNavigationPropertyInfo + { + Name = "AssociatedCompany", + Target = companyType, + TargetMultiplicity = EdmMultiplicity.One + }); + + employeeSet.AddNavigationTarget(associatedCompanyNavigation, companySet); + + var peopleNavigation = peopleWorkingGroupsType.AddUnidirectionalNavigation(new EdmNavigationPropertyInfo + { + Name = "People", + Target = personType, + TargetMultiplicity = EdmMultiplicity.Many, + ContainsTarget = true, + }); + + var workingGroupNavigation = peopleWorkingGroupsType.AddUnidirectionalNavigation(new EdmNavigationPropertyInfo + { + Name = "WorkingGroups", + Target = workingGroupType, + TargetMultiplicity = EdmMultiplicity.Many, + ContainsTarget = true, + }); + + var associatedWorkingGroupsNavigation = personType.AddUnidirectionalNavigation(new EdmNavigationPropertyInfo + { + Name = "AssociatedWorkingGroups", + Target = workingGroupType, + TargetMultiplicity = EdmMultiplicity.Many + }); + + var locationNavigation = companyType.AddUnidirectionalNavigation(new EdmNavigationPropertyInfo + { + Name = "Locations", + Target = locationType, + TargetMultiplicity = EdmMultiplicity.Many, + ContainsTarget = true, + }); + + var propertyManagerNavigation = locationType.AddUnidirectionalNavigation(new EdmNavigationPropertyInfo + { + Name = "PropertyManager", + Target = personType, + TargetMultiplicity = EdmMultiplicity.One, + }); + + var employeesNavigation = locationType.AddUnidirectionalNavigation(new EdmNavigationPropertyInfo + { + Name = "Employees", + Target = personType, + TargetMultiplicity = EdmMultiplicity.Many, + }); + + peopleWorkingGroupsSingleton.AddNavigationTarget(associatedWorkingGroupsNavigation, peopleWorkingGroupsSingleton); + + var locationNavSource = companySet.FindNavigationTarget(locationNavigation) as EdmNavigationSource; + locationNavSource.AddNavigationTarget(propertyManagerNavigation, peopleSet); + locationNavSource.AddNavigationTarget(employeesNavigation, peopleSet); + } + + [Fact] + public async Task ExpandedEntity_Async() + { + ODataResource entry = new ODataResource() + { + TypeName = TestNameSpace + ".Employee", + Properties = new[] + { + new ODataProperty {Name = "PersonId", Value = 1}, + new ODataProperty {Name = "Name", Value = "Test1"}, + new ODataProperty {Name = "Age", Value = 20}, + new ODataProperty {Name = "HomeAddress", Value = null}, + new ODataProperty {Name = "DateHired", Value = null}, + }, + }; + + StringBuilder stringBuilder = new StringBuilder(); + StringWriter stringWriter = new StringWriter(stringBuilder); + + using (var writer = XmlWriter.Create(stringWriter, new XmlWriterSettings() { Async = true })) + { + Tuple> result = await CsdlWriter.TryWriteCsdlAsync(this.model, writer, CsdlTarget.OData).ConfigureAwait(false); + var success = result.Item1; + if (!success) + { + Assert.True(false, "Serialization was unsuccessful"); + } + } + + string modelAsString = stringBuilder.ToString(); + + foreach (ODataFormat mimeType in mimeTypes) + { + string payload, contentType; + this.WriteAndValidateContextUri(mimeType, model, omWriter => + { + var selectExpandClause = new ODataQueryOptionParser(this.model, this.employeeType, this.employeeSet, new Dictionary { { "$expand", "AssociatedCompany" }, { "$select", "AssociatedCompany" } }).ParseSelectAndExpand(); + + omWriter.Settings.ODataUri = new ODataUri() + { + ServiceRoot = this.testServiceRootUri, + SelectAndExpand = selectExpandClause + }; + var writer = omWriter.CreateODataResourceWriter(this.employeeSet, this.employeeType); + writer.WriteStart(entry); + writer.WriteEnd(); + }, + string.Format("\"{0}$metadata#Employees(AssociatedCompany,AssociatedCompany())/$entity\"", TestBaseUri), + out payload, out contentType); + + this.ReadPayload(payload, contentType, model, omReader => + { + var reader = omReader.CreateODataResourceReader(this.employeeSet, this.employeeType); + while (reader.Read()) { } + Assert.Equal(ODataReaderState.Completed, reader.State); + }); + } + } + + private void WriteAndValidateContextUri(ODataFormat odataFormat, EdmModel edmModel, Action test, string expectedUri, out string payload, out string contentType) + { + var writerSettings = new ODataMessageWriterSettings(); + WriteAndValidateContextUri(odataFormat, model, writerSettings, test, expectedUri, out payload, out contentType); + } + + private void WriteAndValidateContextUri(ODataFormat odataFormat, EdmModel edmModel, ODataMessageWriterSettings writerSettings, Action test, string expectedUri, out string payload, out string contentType) + { + var message = new InMemoryMessage() { Stream = new MemoryStream() }; + + writerSettings.EnableMessageStreamDisposal = false; + writerSettings.BaseUri = new Uri(TestBaseUri); + writerSettings.SetServiceDocumentUri(new Uri(TestBaseUri)); + writerSettings.SetContentType(odataFormat); + + using (var msgWriter = new ODataMessageWriter((IODataResponseMessage)message, writerSettings, edmModel)) + { + test(msgWriter); + } + + message.Stream.Seek(0, SeekOrigin.Begin); + using (StreamReader reader = new StreamReader(message.Stream)) + { + contentType = message.GetHeader("Content-Type"); + payload = reader.ReadToEnd(); + Assert.Contains(expectedUri, payload); + } + } + + private void ReadPayload(string payload, string contentType, EdmModel edmModel, Action test, bool readingResponse = true, ODataVersion version = ODataVersion.V4) + { + var message = new InMemoryMessage() { Stream = new MemoryStream(Encoding.UTF8.GetBytes(payload)) }; + message.SetHeader("Content-Type", contentType); + + ODataMessageReaderSettings readerSettings = new ODataMessageReaderSettings() + { + BaseUri = new Uri(TestBaseUri + "$metadata"), + EnableMessageStreamDisposal = true, + Version = version, + }; + + using (var msgReader = + readingResponse + ? new ODataMessageReader((IODataResponseMessage)message, readerSettings, edmModel) + : new ODataMessageReader((IODataRequestMessage)message, readerSettings, edmModel)) + { + test(msgReader); + } + } + } +} diff --git a/test/FunctionalTests/Microsoft.OData.Core.Tests/ScenarioTests/Roundtrip/ContextUrlWriterReaderTests.cs b/test/FunctionalTests/Microsoft.OData.Core.Tests/ScenarioTests/Roundtrip/ContextUrlWriterReaderTests.cs index f6106002a3..476dd7115a 100644 --- a/test/FunctionalTests/Microsoft.OData.Core.Tests/ScenarioTests/Roundtrip/ContextUrlWriterReaderTests.cs +++ b/test/FunctionalTests/Microsoft.OData.Core.Tests/ScenarioTests/Roundtrip/ContextUrlWriterReaderTests.cs @@ -58,7 +58,7 @@ public enum AccessLevel ReadWrite = 3 } - public class ContextUrlWriterReaderTests + public partial class ContextUrlWriterReaderTests { private static readonly ODataVersion[] Versions = new ODataVersion[] { ODataVersion.V4, ODataVersion.V401 }; diff --git a/test/FunctionalTests/Microsoft.OData.Edm.Tests/Csdl/CsdlReaderTests.Async.cs b/test/FunctionalTests/Microsoft.OData.Edm.Tests/Csdl/CsdlReaderTests.Async.cs new file mode 100644 index 0000000000..c5b33f5373 --- /dev/null +++ b/test/FunctionalTests/Microsoft.OData.Edm.Tests/Csdl/CsdlReaderTests.Async.cs @@ -0,0 +1,325 @@ +//--------------------------------------------------------------------- +// +// Copyright (C) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information. +// +//--------------------------------------------------------------------- + +using Microsoft.OData.Edm.Csdl; +using Microsoft.OData.Edm.Validation; +using Microsoft.OData.Edm.Vocabularies; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using System.Xml; +using System.Xml.Linq; +using Xunit; + +namespace Microsoft.OData.Edm.Tests.Csdl +{ + public partial class CsdlReaderTests + { + [InlineData("4.0")] + [InlineData("4.01")] + [Theory] + public async Task ValidateNavigationPropertyBindingPathEndingInTypeCast_Async(string odataVersion) + { + var entitySetWithNavProperties = + "" + + "" + + "" + + ""; + var entitySetWithoutNavProperties = + ""; + var csdl = + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "{1}" + + "" + + "" + + "" + + "" + + "" + + ""; + + var model = CsdlReader.Parse(XElement.Parse(String.Format(csdl, odataVersion, entitySetWithNavProperties)).CreateReader()); + IEnumerable errors; + Assert.True(model.Validate(out errors)); + Assert.Empty(errors); + + var setA = model.FindDeclaredNavigationSource("SetA"); + Assert.NotNull(setA); + + var navProp = setA.EntityType().FindProperty("Nav") as IEdmNavigationProperty; + Assert.NotNull(navProp); + Assert.Equal(2, setA.NavigationPropertyBindings.Count()); + + // NavPropBinding for for EntityX1 should be SetX1 + var X1NavPropBinding = setA.NavigationPropertyBindings.FirstOrDefault(b => b.Path.PathSegments.Last() == "NS.EntityX1"); + Assert.NotNull(X1NavPropBinding); + var setX1 = model.FindDeclaredNavigationSource("SetX1"); + Assert.Same(setX1, X1NavPropBinding.Target); + Assert.Same(setX1, setA.FindNavigationTarget(navProp, new EdmPathExpression("Nav/NS.EntityX1"))); + + // NavPropBinding for EntityX2 should be SetX2 + var X2NavPropBinding = setA.NavigationPropertyBindings.FirstOrDefault(b => b.Path.PathSegments.Last() == "NS.EntityX2"); + Assert.NotNull(X2NavPropBinding); + var setX2 = model.FindDeclaredNavigationSource("SetX2"); + Assert.Same(setX2, X2NavPropBinding.Target); + Assert.Same(setX2, setA.FindNavigationTarget(navProp, new EdmPathExpression("Nav/NS.EntityX2"))); + + // Navigation Property without path should return UnknownEntitySet + Assert.True(setA.FindNavigationTarget(navProp) is EdmUnknownEntitySet); + + // Verify writing model + model.SetEdmVersion(Version.Parse(odataVersion)); + await VerifyXmlModelAsync(model, odataVersion == "4.0" ? entitySetWithoutNavProperties : entitySetWithNavProperties).ConfigureAwait(false); + } + + [Fact] + public async Task ReadAnnotationWithoutSpecifiedValueAndUseDefault_Async() + { + var csdl + = "" + + "" + + "" + + "" + + "" + + "" + + "" + // does not specify value + "" + + "" + + "" + + "" + + "" + + ""; + // Parse into CSDL + + IEdmModel model; + using (XmlReader xr = XElement.Parse(csdl).CreateReader()) + { + model = CsdlReader.Parse(xr); + } + IEdmTerm defaultTerm = model.FindTerm("NS.MyDefaultTerm"); + Assert.Equal("This is a test", defaultTerm.DefaultValue); + IEdmVocabularyAnnotation[] annotations = model.VocabularyAnnotations.ToArray(); + Assert.Equal(2, annotations.Length); + + IEdmVocabularyAnnotation annotationWithSpecifiedValue = Assert.IsAssignableFrom(annotations[0]); + Assert.NotNull(annotationWithSpecifiedValue.Value); + Assert.IsAssignableFrom(annotationWithSpecifiedValue.Value); + + IEdmVocabularyAnnotation annotationWithDefaultValue = Assert.IsAssignableFrom(annotations[1]); + Assert.NotNull(annotationWithDefaultValue.Value); + IEdmStringConstantExpression stringConstantExp = Assert.IsAssignableFrom(annotationWithDefaultValue.Value); + Assert.Equal("This is a test", stringConstantExp.Value); + + // Validate model + IEnumerable errors; + bool validated = model.Validate(out errors); + Assert.True(validated); + + // Act & Assert for Reserialized XML + await CsdlWriterTests.WriteAndVerifyXmlAsync(model, "" + + "" + + "" + + "" + + "" + + "" + + "" + // no longer has value + "" + + "" + + "" + + "" + + "" + + "").ConfigureAwait(false); + } + + [Fact] + public async Task ReadWriteAnnotationUsingDefaultValueWithContinuity_Async() + { + var csdl + = "" + + "" + + "" + + "" + + "" + + "" + // does not specify value + "" + // specifies default + "" + // specifies non-default + "" + + "" + + "" + + "" + + "" + + "" + + ""; + // Parse into CSDL + IEdmModel model; + using (XmlReader xr = XElement.Parse(csdl).CreateReader()) + { + model = CsdlReader.Parse(xr); + } + IEdmTerm defaultTerm = model.FindTerm("NS.MyDefaultBoolTerm1"); + Assert.True(bool.Parse(defaultTerm.DefaultValue)); + IEdmVocabularyAnnotation[] annotations = model.VocabularyAnnotations.ToArray(); + Assert.Equal(3, annotations.Length); + IEdmVocabularyAnnotation annotation1 = Assert.IsAssignableFrom(annotations[0]); + IEdmBooleanConstantExpression annotationValue1 = Assert.IsAssignableFrom(annotation1.Value); + Assert.True(annotationValue1.Value); + + IEdmVocabularyAnnotation annotation2 = Assert.IsAssignableFrom(annotations[1]); + IEdmBooleanConstantExpression annotationValue2 = Assert.IsAssignableFrom(annotation2.Value); + Assert.True(annotationValue2.Value); + + IEdmVocabularyAnnotation annotation3 = Assert.IsAssignableFrom(annotations[2]); + IEdmBooleanConstantExpression annotationValue3 = Assert.IsAssignableFrom(annotation3.Value); + Assert.False(annotationValue3.Value); + + // Validate model + IEnumerable errors; + bool validated = model.Validate(out errors); + Assert.True(validated); + + // Act & Assert for Reserialized XML + await CsdlWriterTests.WriteAndVerifyXmlAsync(model, "" + + "" + + "" + + "" + + "" + + "" + // does not specify value + "" + // for back compatiable, write the default value again. + "" + // specifies non-default + "" + + "" + + "" + + "" + + "" + + "" + + "").ConfigureAwait(false); + } + + [Fact] + public async Task ReadAnnotationWithoutSpecifiedValueAndUsePrimitiveDefaultValues_Async() + { + var csdl = "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + ""; + // Parse into CSDL + IEdmModel model; + using (XmlReader xr = XElement.Parse(csdl).CreateReader()) + { + model = CsdlReader.Parse(xr); + } + IEdmVocabularyAnnotation[] annotations = model.VocabularyAnnotations.ToArray(); + Assert.Equal(13, annotations.Length); + for (int i = 0; i < annotations.Length; i++) + { + IEdmVocabularyAnnotation annotationWithDefaultValue = Assert.IsAssignableFrom(annotations[i]); + Assert.NotNull(annotationWithDefaultValue.Value); + } + + // Validate model + IEnumerable errors; + bool validated = model.Validate(out errors); + Assert.True(validated); + + // Act & Assert for Reserialized XML + await CsdlWriterTests.WriteAndVerifyXmlAsync(model, "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "").ConfigureAwait(false); + } + + static async Task VerifyXmlModelAsync(IEdmModel model, string csdl) + { + var stream = new MemoryStream(); + XmlWriter xmlWriter = XmlWriter.Create(stream, new XmlWriterSettings() { Async = true }); + Tuple> result = await CsdlWriter.TryWriteCsdlAsync(model, xmlWriter, CsdlTarget.OData).ConfigureAwait(false); + bool success = result.Item1; + IEnumerable errors = result.Item2; + + Assert.True(success); + Assert.Empty(errors); + stream.Position = 0; + Assert.Contains(csdl, new StreamReader(stream).ReadToEnd()); + } + } +} diff --git a/test/FunctionalTests/Microsoft.OData.Edm.Tests/Csdl/CsdlWriterTests.Async.cs b/test/FunctionalTests/Microsoft.OData.Edm.Tests/Csdl/CsdlWriterTests.Async.cs new file mode 100644 index 0000000000..91ae70db46 --- /dev/null +++ b/test/FunctionalTests/Microsoft.OData.Edm.Tests/Csdl/CsdlWriterTests.Async.cs @@ -0,0 +1,3014 @@ +//--------------------------------------------------------------------- +// +// Copyright (C) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information. +// +//--------------------------------------------------------------------- + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +#if NETCOREAPP3_1 +using System.Text.Encodings.Web; +using System.Text.Json; +#endif +using System.Xml; +using System.Xml.Linq; +using Microsoft.OData.Edm.Csdl; +using Microsoft.OData.Edm.Validation; +using Microsoft.OData.Edm.Vocabularies; +using Microsoft.OData.Edm.Vocabularies.V1; +using Xunit; + +namespace Microsoft.OData.Edm.Tests.Csdl +{ + public partial class CsdlWriterTests + { + #region Reference + + [Fact] + public async Task ShouldWriteEdmReference_Async() + { + // Arrange + EdmModel model = new EdmModel(); + EdmReference reference1 = new EdmReference(new Uri("https://example.com/Org.OData.Authorization.V1.xml")); + EdmInclude authInclude = new EdmInclude("Auth", "Org.OData.Authorization.V1"); + reference1.AddInclude(authInclude); + reference1.AddIncludeAnnotations(new EdmIncludeAnnotations("org.example.validation", null, null)); + reference1.AddIncludeAnnotations(new EdmIncludeAnnotations("org.example.display", "Tablet", null)); + + EdmReference reference2 = new EdmReference(new Uri("https://example.com/Org.OData.Core.V1.xml")); + reference2.AddInclude(new EdmInclude("Core", "Org.OData.Core.V1")); + reference2.AddIncludeAnnotations(new EdmIncludeAnnotations("org.example.hcm", null, "com.example.Sales")); + reference2.AddIncludeAnnotations(new EdmIncludeAnnotations("org.example.hcm", "Tablet", "com.example.Person")); + model.SetEdmReferences(new[] { reference1, reference2 }); + + EdmVocabularyAnnotation annotation = new EdmVocabularyAnnotation(authInclude, + CoreVocabularyModel.LongDescriptionTerm, new EdmStringConstant("Include Description.")); + annotation.SetSerializationLocation(model, EdmVocabularyAnnotationSerializationLocation.Inline); + model.SetVocabularyAnnotation(annotation); + + annotation = new EdmVocabularyAnnotation(reference2, + CoreVocabularyModel.LongDescriptionTerm, new EdmStringConstant("EdmReference Description.")); + annotation.SetSerializationLocation(model, EdmVocabularyAnnotationSerializationLocation.Inline); + model.SetVocabularyAnnotation(annotation); + + // Act & Assert for XML + await WriteAndVerifyXmlAsync(model, "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "").ConfigureAwait(false); + + // Act & Assert for JSON + await WriteAndVerifyJsonAsync(model, @"{ + ""$Version"": ""4.0"", + ""$Reference"": { + ""https://example.com/Org.OData.Authorization.V1.xml"": { + ""$Include"": [ + { + ""$Namespace"": ""Org.OData.Authorization.V1"", + ""$Alias"": ""Auth"", + ""@Core.LongDescription"": ""Include Description."" + } + ], + ""$IncludeAnnotations"": [ + { + ""$TermNamespace"": ""org.example.validation"" + }, + { + ""$TermNamespace"": ""org.example.display"", + ""$Qualifier"": ""Tablet"" + } + ] + }, + ""https://example.com/Org.OData.Core.V1.xml"": { + ""$Include"": [ + { + ""$Namespace"": ""Org.OData.Core.V1"", + ""$Alias"": ""Core"" + } + ], + ""$IncludeAnnotations"": [ + { + ""$TermNamespace"": ""org.example.hcm"", + ""$TargetNamespace"": ""com.example.Sales"" + }, + { + ""$TermNamespace"": ""org.example.hcm"", + ""$Qualifier"": ""Tablet"", + ""$TargetNamespace"": ""com.example.Person"" + } + ], + ""@Core.LongDescription"": ""EdmReference Description."" + } + } +}").ConfigureAwait(false); + } + #endregion + + #region Annotation - Computed, OptimisticConcurrency + + [Fact] + public async Task VerifyAnnotationComputedConcurrency_Async() + { + // Arrange + var model = new EdmModel(); + var entity = new EdmEntityType("NS1", "Product"); + var entityId = entity.AddStructuralProperty("Id", EdmCoreModel.Instance.GetInt32(false)); + entity.AddKeys(entityId); + EdmStructuralProperty name1 = entity.AddStructuralProperty("Name", EdmCoreModel.Instance.GetString(false)); + EdmStructuralProperty timeVer = entity.AddStructuralProperty("UpdatedTime", EdmCoreModel.Instance.GetDate(false)); + model.AddElement(entity); + + SetComputedAnnotation(model, entityId); // semantic meaning is V3's 'Identity' for Key property + SetComputedAnnotation(model, timeVer); // semantic meaning is V3's 'Computed' for non-key property + + var entityContainer = new EdmEntityContainer("NS1", "Container"); + model.AddElement(entityContainer); + EdmEntitySet set1 = new EdmEntitySet(entityContainer, "Products", entity); + model.SetOptimisticConcurrencyAnnotation(set1, new IEdmStructuralProperty[] { entityId, timeVer }); + entityContainer.AddElement(set1); + + // Act & Assert for XML + await WriteAndVerifyXmlAsync(model, "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "Id" + + "UpdatedTime" + + "" + + "" + + "" + + "" + + "" + + "" + + "").ConfigureAwait(false); + + // Act & Assert for JSON + await WriteAndVerifyJsonAsync(model, @"{ + ""$Version"": ""4.0"", + ""$EntityContainer"": ""NS1.Container"", + ""NS1"": { + ""Product"": { + ""$Kind"": ""EntityType"", + ""$Key"": [ + ""Id"" + ], + ""Id"": { + ""$Type"": ""Edm.Int32"", + ""@Org.OData.Core.V1.Computed"": true + }, + ""Name"": {}, + ""UpdatedTime"": { + ""$Type"": ""Edm.Date"", + ""@Org.OData.Core.V1.Computed"": true + } + }, + ""Container"": { + ""$Kind"": ""EntityContainer"", + ""Products"": { + ""$Collection"": true, + ""$Type"": ""NS1.Product"", + ""@Org.OData.Core.V1.OptimisticConcurrency"": [ + ""Id"", + ""UpdatedTime"" + ] + } + } + } +}").ConfigureAwait(false); + } + + [Fact] + public async Task WriteNavigationPropertyInComplexType_Async() + { + // Arrange + var model = new EdmModel(); + + var person = new EdmEntityType("DefaultNs", "Person"); + var entityId = person.AddStructuralProperty("UserName", EdmCoreModel.Instance.GetString(false)); + person.AddKeys(entityId); + + var city = new EdmEntityType("DefaultNs", "City"); + var cityId = city.AddStructuralProperty("Name", EdmCoreModel.Instance.GetString(false)); + city.AddKeys(cityId); + + var countryOrRegion = new EdmEntityType("DefaultNs", "CountryOrRegion"); + var countryId = countryOrRegion.AddStructuralProperty("Name", EdmCoreModel.Instance.GetString(false)); + countryOrRegion.AddKeys(countryId); + + var complex = new EdmComplexType("DefaultNs", "Address"); + complex.AddStructuralProperty("Id", EdmCoreModel.Instance.GetInt32(false)); + var navP = complex.AddUnidirectionalNavigation( + new EdmNavigationPropertyInfo() + { + Name = "City", + Target = city, + TargetMultiplicity = EdmMultiplicity.One, + }); + + var derivedComplex = new EdmComplexType("DefaultNs", "WorkAddress", complex); + var navP2 = derivedComplex.AddUnidirectionalNavigation( + new EdmNavigationPropertyInfo() + { + Name = "CountryOrRegion", + Target = countryOrRegion, + TargetMultiplicity = EdmMultiplicity.One, + }); + + person.AddStructuralProperty("HomeAddress", new EdmComplexTypeReference(complex, false)); + person.AddStructuralProperty("WorkAddress", new EdmComplexTypeReference(complex, false)); + person.AddStructuralProperty("Addresses", new EdmCollectionTypeReference(new EdmCollectionType(new EdmComplexTypeReference(complex, false)))); + + model.AddElement(person); + model.AddElement(city); + model.AddElement(countryOrRegion); + model.AddElement(complex); + model.AddElement(derivedComplex); + + var entityContainer = new EdmEntityContainer("DefaultNs", "Container"); + model.AddElement(entityContainer); + EdmEntitySet people = new EdmEntitySet(entityContainer, "People", person); + EdmEntitySet cities = new EdmEntitySet(entityContainer, "City", city); + EdmEntitySet countriesOrRegions = new EdmEntitySet(entityContainer, "CountryOrRegion", countryOrRegion); + people.AddNavigationTarget(navP, cities, new EdmPathExpression("HomeAddress/City")); + people.AddNavigationTarget(navP, cities, new EdmPathExpression("Addresses/City")); + people.AddNavigationTarget(navP2, countriesOrRegions, new EdmPathExpression("WorkAddress/DefaultNs.WorkAddress/CountryOrRegion")); + entityContainer.AddElement(people); + entityContainer.AddElement(cities); + entityContainer.AddElement(countriesOrRegions); + + IEnumerable actualErrors = null; + model.Validate(out actualErrors); + Assert.Empty(actualErrors); + + // Act & Assert for XML + await WriteAndVerifyXmlAsync(model, "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "<" + + "/ComplexType>" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "").ConfigureAwait(false); + + // Act & Assert for JSON + await WriteAndVerifyJsonAsync(model, @"{ + ""$Version"": ""4.0"", + ""$EntityContainer"": ""DefaultNs.Container"", + ""DefaultNs"": { + ""Person"": { + ""$Kind"": ""EntityType"", + ""$Key"": [ + ""UserName"" + ], + ""UserName"": {}, + ""HomeAddress"": { + ""$Type"": ""DefaultNs.Address"" + }, + ""WorkAddress"": { + ""$Type"": ""DefaultNs.Address"" + }, + ""Addresses"": { + ""$Collection"": true, + ""$Type"": ""DefaultNs.Address"" + } + }, + ""City"": { + ""$Kind"": ""EntityType"", + ""$Key"": [ + ""Name"" + ], + ""Name"": {} + }, + ""CountryOrRegion"": { + ""$Kind"": ""EntityType"", + ""$Key"": [ + ""Name"" + ], + ""Name"": {} + }, + ""Address"": { + ""$Kind"": ""ComplexType"", + ""Id"": { + ""$Type"": ""Edm.Int32"" + }, + ""City"": { + ""$Kind"": ""NavigationProperty"", + ""$Type"": ""DefaultNs.City"" + } + }, + ""WorkAddress"": { + ""$Kind"": ""ComplexType"", + ""$BaseType"": ""DefaultNs.Address"", + ""CountryOrRegion"": { + ""$Kind"": ""NavigationProperty"", + ""$Type"": ""DefaultNs.CountryOrRegion"" + } + }, + ""Container"": { + ""$Kind"": ""EntityContainer"", + ""People"": { + ""$Collection"": true, + ""$Type"": ""DefaultNs.Person"", + ""$NavigationPropertyBinding"": { + ""Addresses/City"": ""City"", + ""HomeAddress/City"": ""City"", + ""WorkAddress/DefaultNs.WorkAddress/CountryOrRegion"": ""CountryOrRegion"" + } + }, + ""City"": { + ""$Collection"": true, + ""$Type"": ""DefaultNs.City"" + }, + ""CountryOrRegion"": { + ""$Collection"": true, + ""$Type"": ""DefaultNs.CountryOrRegion"" + } + } + } +}").ConfigureAwait(false); + } + + [Fact] + public async Task WriteCollectionOfNavigationOnComplex_Async() + { + // Arrange + var model = new EdmModel(); + + var entity = new EdmEntityType("DefaultNs", "EntityType"); + var entityId = entity.AddStructuralProperty("ID", EdmCoreModel.Instance.GetString(false)); + entity.AddKeys(entityId); + + var navEntity = new EdmEntityType("DefaultNs", "NavEntityType"); + var navEntityId = navEntity.AddStructuralProperty("ID", EdmCoreModel.Instance.GetString(false)); + navEntity.AddKeys(navEntityId); + + var complex = new EdmComplexType("DefaultNs", "ComplexType"); + complex.AddStructuralProperty("Prop1", EdmCoreModel.Instance.GetInt32(false)); + + var navP = complex.AddUnidirectionalNavigation( + new EdmNavigationPropertyInfo() + { + Name = "CollectionOfNav", + Target = navEntity, + TargetMultiplicity = EdmMultiplicity.Many, + }); + + entity.AddStructuralProperty("Complex", new EdmComplexTypeReference(complex, false)); + + model.AddElement(entity); + model.AddElement(navEntity); + model.AddElement(complex); + + var entityContainer = new EdmEntityContainer("DefaultNs", "Container"); + model.AddElement(entityContainer); + EdmEntitySet entites = new EdmEntitySet(entityContainer, "Entities", entity); + EdmEntitySet navEntities = new EdmEntitySet(entityContainer, "NavEntities", navEntity); + entites.AddNavigationTarget(navP, navEntities, new EdmPathExpression("Complex/CollectionOfNav")); + entityContainer.AddElement(entites); + entityContainer.AddElement(navEntities); + + // Act & Assert for XML + await WriteAndVerifyXmlAsync(model, "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "").ConfigureAwait(false); + + // Act & Assert for JSON + await WriteAndVerifyJsonAsync(model, @"{ + ""$Version"": ""4.0"", + ""$EntityContainer"": ""DefaultNs.Container"", + ""DefaultNs"": { + ""EntityType"": { + ""$Kind"": ""EntityType"", + ""$Key"": [ + ""ID"" + ], + ""ID"": {}, + ""Complex"": { + ""$Type"": ""DefaultNs.ComplexType"" + } + }, + ""NavEntityType"": { + ""$Kind"": ""EntityType"", + ""$Key"": [ + ""ID"" + ], + ""ID"": {} + }, + ""ComplexType"": { + ""$Kind"": ""ComplexType"", + ""Prop1"": { + ""$Type"": ""Edm.Int32"" + }, + ""CollectionOfNav"": { + ""$Kind"": ""NavigationProperty"", + ""$Collection"": true, + ""$Type"": ""DefaultNs.NavEntityType"" + } + }, + ""Container"": { + ""$Kind"": ""EntityContainer"", + ""Entities"": { + ""$Collection"": true, + ""$Type"": ""DefaultNs.EntityType"", + ""$NavigationPropertyBinding"": { + ""Complex/CollectionOfNav"": ""NavEntities"" + } + }, + ""NavEntities"": { + ""$Collection"": true, + ""$Type"": ""DefaultNs.NavEntityType"" + } + } + } +}").ConfigureAwait(false); + } + + [Fact] + public async Task ContainedUnderComplexTest_Async() + { + // Arrange + var model = new EdmModel(); + + var entity = new EdmEntityType("NS", "EntityType"); + var entityId = entity.AddStructuralProperty("ID", EdmCoreModel.Instance.GetString(false)); + entity.AddKeys(entityId); + + var containedEntity = new EdmEntityType("NS", "ContainedEntityType"); + var containedEntityId = containedEntity.AddStructuralProperty("ID", EdmCoreModel.Instance.GetString(false)); + containedEntity.AddKeys(containedEntityId); + + var complex = new EdmComplexType("NS", "ComplexType"); + complex.AddStructuralProperty("Prop1", EdmCoreModel.Instance.GetInt32(false)); + + var containedUnderComplex = complex.AddUnidirectionalNavigation( + new EdmNavigationPropertyInfo() + { + Name = "ContainedUnderComplex", + Target = containedEntity, + TargetMultiplicity = EdmMultiplicity.Many, + ContainsTarget = true + }); + + var navUnderContained = containedEntity.AddUnidirectionalNavigation( + new EdmNavigationPropertyInfo() + { + Name = "NavUnderContained", + Target = entity, + TargetMultiplicity = EdmMultiplicity.Many + }); + + entity.AddStructuralProperty("Complex", new EdmComplexTypeReference(complex, false)); + + model.AddElement(entity); + model.AddElement(containedEntity); + model.AddElement(complex); + + var entityContainer = new EdmEntityContainer("NS", "Container"); + model.AddElement(entityContainer); + EdmEntitySet entites1 = new EdmEntitySet(entityContainer, "Entities1", entity); + EdmEntitySet entites2 = new EdmEntitySet(entityContainer, "Entities2", entity); + entites1.AddNavigationTarget(navUnderContained, entites2, + new EdmPathExpression("Complex/ContainedUnderComplex/NavUnderContained")); + entityContainer.AddElement(entites1); + entityContainer.AddElement(entites2); + + var entitySet1 = model.EntityContainer.FindEntitySet("Entities1"); + var entitySet2 = model.EntityContainer.FindEntitySet("Entities2"); + var containedEntitySet = entitySet1.FindNavigationTarget(containedUnderComplex, + new EdmPathExpression("Complex/ContainedUnderComplex")); + Assert.Equal("ContainedUnderComplex", containedEntitySet.Name); + var entitySetUnderContained = containedEntitySet.FindNavigationTarget(navUnderContained); + Assert.Equal(entitySetUnderContained, entitySet2); + + // Act & Assert for XML + await WriteAndVerifyXmlAsync(model, "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "").ConfigureAwait(false); + + // Act & Assert for JSON + await WriteAndVerifyJsonAsync(model, @"{ + ""$Version"": ""4.0"", + ""$EntityContainer"": ""NS.Container"", + ""NS"": { + ""EntityType"": { + ""$Kind"": ""EntityType"", + ""$Key"": [ + ""ID"" + ], + ""ID"": {}, + ""Complex"": { + ""$Type"": ""NS.ComplexType"" + } + }, + ""ContainedEntityType"": { + ""$Kind"": ""EntityType"", + ""$Key"": [ + ""ID"" + ], + ""ID"": {}, + ""NavUnderContained"": { + ""$Kind"": ""NavigationProperty"", + ""$Collection"": true, + ""$Type"": ""NS.EntityType"" + } + }, + ""ComplexType"": { + ""$Kind"": ""ComplexType"", + ""Prop1"": { + ""$Type"": ""Edm.Int32"" + }, + ""ContainedUnderComplex"": { + ""$Kind"": ""NavigationProperty"", + ""$Collection"": true, + ""$Type"": ""NS.ContainedEntityType"", + ""$ContainsTarget"": true + } + }, + ""Container"": { + ""$Kind"": ""EntityContainer"", + ""Entities1"": { + ""$Collection"": true, + ""$Type"": ""NS.EntityType"", + ""$NavigationPropertyBinding"": { + ""Complex/ContainedUnderComplex/NavUnderContained"": ""Entities2"" + } + }, + ""Entities2"": { + ""$Collection"": true, + ""$Type"": ""NS.EntityType"" + } + } + } +}").ConfigureAwait(false); + } + + [Fact] + public async Task SetNavigationPropertyPartnerTest_Async() + { + // build model + var model = new EdmModel(); + var entityType1 = new EdmEntityType("NS", "EntityType1"); + var entityType2 = new EdmEntityType("NS", "EntityType2"); + var entityType3 = new EdmEntityType("NS", "EntityType3", entityType2); + var complexType1 = new EdmComplexType("NS", "ComplexType1"); + var complexType2 = new EdmComplexType("NS", "ComplexType2"); + model.AddElements(new IEdmSchemaElement[] { entityType1, entityType2, entityType3, complexType1, complexType2 }); + entityType1.AddKeys(entityType1.AddStructuralProperty("ID", EdmPrimitiveTypeKind.Int32)); + entityType2.AddKeys(entityType2.AddStructuralProperty("ID", EdmPrimitiveTypeKind.Int32)); + var outerNav1A = entityType1.AddUnidirectionalNavigation(new EdmNavigationPropertyInfo + { + Name = "OuterNavA", + Target = entityType2, + TargetMultiplicity = EdmMultiplicity.One + }); + var outerNav2A = entityType2.AddUnidirectionalNavigation(new EdmNavigationPropertyInfo + { + Name = "OuterNavA", + Target = entityType1, + TargetMultiplicity = EdmMultiplicity.One + }); + entityType1.SetNavigationPropertyPartner( + outerNav1A, new EdmPathExpression("OuterNavA"), outerNav2A, new EdmPathExpression("OuterNavA")); + entityType1.AddStructuralProperty( + "ComplexProp", + new EdmCollectionTypeReference( + new EdmCollectionType( + new EdmComplexTypeReference(complexType1, false)))); + entityType2.AddStructuralProperty("ComplexProp", new EdmComplexTypeReference(complexType2, false)); + var innerNav1 = complexType1.AddUnidirectionalNavigation(new EdmNavigationPropertyInfo + { + Name = "InnerNav", + Target = entityType2, + TargetMultiplicity = EdmMultiplicity.One + }); + var innerNav2 = complexType2.AddUnidirectionalNavigation(new EdmNavigationPropertyInfo + { + Name = "InnerNav", + Target = entityType1, + TargetMultiplicity = EdmMultiplicity.One + }); + var outerNav2B = entityType2.AddUnidirectionalNavigation(new EdmNavigationPropertyInfo + { + Name = "OuterNavB", + Target = entityType1, + TargetMultiplicity = EdmMultiplicity.One + }); + entityType2.SetNavigationPropertyPartner( + outerNav2B, new EdmPathExpression("OuterNavB"), innerNav1, new EdmPathExpression("ComplexProp/InnerNav")); + var outerNav2C = entityType3.AddUnidirectionalNavigation(new EdmNavigationPropertyInfo + { + Name = "OuterNavC", + Target = entityType1, + TargetMultiplicity = EdmMultiplicity.Many + }); + var outerNav1B = entityType1.AddUnidirectionalNavigation(new EdmNavigationPropertyInfo + { + Name = "OuterNavB", + Target = entityType2, + TargetMultiplicity = EdmMultiplicity.Many + }); + entityType1.SetNavigationPropertyPartner( + outerNav1B, new EdmPathExpression("OuterNavB"), outerNav2C, new EdmPathExpression("NS.EntityType3/OuterNavC")); + + // Act & Assert for XML + await WriteAndVerifyXmlAsync(model, "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "").ConfigureAwait(false); + + // Act & Assert for JSON + await WriteAndVerifyJsonAsync(model, @"{ + ""$Version"": ""4.0"", + ""NS"": { + ""EntityType1"": { + ""$Kind"": ""EntityType"", + ""$Key"": [ + ""ID"" + ], + ""ID"": { + ""$Type"": ""Edm.Int32"", + ""$Nullable"": true + }, + ""ComplexProp"": { + ""$Collection"": true, + ""$Type"": ""NS.ComplexType1"" + }, + ""OuterNavA"": { + ""$Kind"": ""NavigationProperty"", + ""$Type"": ""NS.EntityType2"", + ""$Partner"": ""OuterNavA"" + }, + ""OuterNavB"": { + ""$Kind"": ""NavigationProperty"", + ""$Collection"": true, + ""$Type"": ""NS.EntityType2"", + ""$Partner"": ""NS.EntityType3/OuterNavC"" + } + }, + ""EntityType2"": { + ""$Kind"": ""EntityType"", + ""$Key"": [ + ""ID"" + ], + ""ID"": { + ""$Type"": ""Edm.Int32"", + ""$Nullable"": true + }, + ""ComplexProp"": { + ""$Type"": ""NS.ComplexType2"" + }, + ""OuterNavA"": { + ""$Kind"": ""NavigationProperty"", + ""$Type"": ""NS.EntityType1"", + ""$Partner"": ""OuterNavA"" + }, + ""OuterNavB"": { + ""$Kind"": ""NavigationProperty"", + ""$Type"": ""NS.EntityType1"", + ""$Partner"": ""ComplexProp/InnerNav"" + } + }, + ""EntityType3"": { + ""$Kind"": ""EntityType"", + ""$BaseType"": ""NS.EntityType2"", + ""OuterNavC"": { + ""$Kind"": ""NavigationProperty"", + ""$Collection"": true, + ""$Type"": ""NS.EntityType1"", + ""$Partner"": ""OuterNavB"" + } + }, + ""ComplexType1"": { + ""$Kind"": ""ComplexType"", + ""InnerNav"": { + ""$Kind"": ""NavigationProperty"", + ""$Type"": ""NS.EntityType2"" + } + }, + ""ComplexType2"": { + ""$Kind"": ""ComplexType"", + ""InnerNav"": { + ""$Kind"": ""NavigationProperty"", + ""$Type"": ""NS.EntityType1"" + } + } + } +}").ConfigureAwait(false); + } + + [Fact] + public async Task SetNavigationPropertyPartnerTypeHierarchyTest_Async() + { + // Arrange + var model = new EdmModel(); + var entityTypeA1 = new EdmEntityType("NS", "EntityTypeA1"); + var entityTypeA2 = new EdmEntityType("NS", "EntityTypeA2", entityTypeA1); + var entityTypeA3 = new EdmEntityType("NS", "EntityTypeA3", entityTypeA2); + var entityTypeB = new EdmEntityType("NS", "EntityTypeB"); + model.AddElements(new IEdmSchemaElement[] { entityTypeA1, entityTypeA2, entityTypeA3, entityTypeB }); + entityTypeA1.AddKeys(entityTypeA1.AddStructuralProperty("ID", EdmPrimitiveTypeKind.Int32)); + var a1Nav = entityTypeA1.AddUnidirectionalNavigation(new EdmNavigationPropertyInfo + { + Name = "A1Nav", + Target = entityTypeB, + TargetMultiplicity = EdmMultiplicity.One + }); + var a3Nav = entityTypeA3.AddUnidirectionalNavigation(new EdmNavigationPropertyInfo + { + Name = "A3Nav", + Target = entityTypeB, + TargetMultiplicity = EdmMultiplicity.One + }); + entityTypeB.AddKeys(entityTypeB.AddStructuralProperty("ID", EdmPrimitiveTypeKind.Int32)); + var bNav1 = entityTypeB.AddUnidirectionalNavigation(new EdmNavigationPropertyInfo + { + Name = "BNav1", + Target = entityTypeA2, + TargetMultiplicity = EdmMultiplicity.One + }); + var bNav2 = entityTypeB.AddUnidirectionalNavigation(new EdmNavigationPropertyInfo + { + Name = "BNav2", + Target = entityTypeA3, + TargetMultiplicity = EdmMultiplicity.One + }); + entityTypeA2.SetNavigationPropertyPartner(a1Nav, new EdmPathExpression("A1Nav"), bNav1, new EdmPathExpression("BNav1")); + entityTypeA2.SetNavigationPropertyPartner(a3Nav, new EdmPathExpression("NS.EntityTypeA3/A3Nav"), bNav2, new EdmPathExpression("BNav2")); + + // Act & Assert for XML + await WriteAndVerifyXmlAsync(model, "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "").ConfigureAwait(false); + + // Act & Assert for JSON + await WriteAndVerifyJsonAsync(model, @"{ + ""$Version"": ""4.0"", + ""NS"": { + ""EntityTypeA1"": { + ""$Kind"": ""EntityType"", + ""$Key"": [ + ""ID"" + ], + ""ID"": { + ""$Type"": ""Edm.Int32"", + ""$Nullable"": true + }, + ""A1Nav"": { + ""$Kind"": ""NavigationProperty"", + ""$Type"": ""NS.EntityTypeB"", + ""$Partner"": ""BNav1"" + } + }, + ""EntityTypeA2"": { + ""$Kind"": ""EntityType"", + ""$BaseType"": ""NS.EntityTypeA1"" + }, + ""EntityTypeA3"": { + ""$Kind"": ""EntityType"", + ""$BaseType"": ""NS.EntityTypeA2"", + ""A3Nav"": { + ""$Kind"": ""NavigationProperty"", + ""$Type"": ""NS.EntityTypeB"", + ""$Partner"": ""BNav2"" + } + }, + ""EntityTypeB"": { + ""$Kind"": ""EntityType"", + ""$Key"": [ + ""ID"" + ], + ""ID"": { + ""$Type"": ""Edm.Int32"", + ""$Nullable"": true + }, + ""BNav1"": { + ""$Kind"": ""NavigationProperty"", + ""$Type"": ""NS.EntityTypeA2"", + ""$Partner"": ""A1Nav"" + }, + ""BNav2"": { + ""$Kind"": ""NavigationProperty"", + ""$Type"": ""NS.EntityTypeA3"", + ""$Partner"": ""NS.EntityTypeA3/A3Nav"" + } + } + } +}").ConfigureAwait(false); + } + #endregion + + #region Optional Parameters + + [Fact] + public async Task ShouldWriteInLineOptionalParameters_Async() + { + // Arrange + var stringTypeReference = new EdmStringTypeReference(EdmCoreModel.Instance.GetPrimitiveType(EdmPrimitiveTypeKind.String), false); + var model = new EdmModel(); + var function = new EdmFunction("test", "TestFunction", stringTypeReference); + var requiredParam = new EdmOperationParameter(function, "requiredParam", stringTypeReference); + var optionalParam = new EdmOptionalParameter(function, "optionalParam", stringTypeReference, null); + var optionalParamWithDefault = new EdmOptionalParameter(function, "optionalParamWithDefault", stringTypeReference, "Smith"); + function.AddParameter(requiredParam); + function.AddParameter(optionalParam); + function.AddParameter(optionalParamWithDefault); + model.AddElement(function); + model.AddEntityContainer("test", "Default").AddFunctionImport("TestFunction", function); + + // Act & Assert for XML + await WriteAndVerifyXmlAsync(model, "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "").ConfigureAwait(false); + + // Act & Assert for JSON + await WriteAndVerifyJsonAsync(model, @"{ + ""$Version"": ""4.0"", + ""$EntityContainer"": ""test.Default"", + ""test"": { + ""TestFunction"": [ + { + ""$Kind"": ""Function"", + ""$Parameter"": [ + { + ""$Name"": ""requiredParam"" + }, + { + ""$Name"": ""optionalParam"", + ""@Org.OData.Core.V1.OptionalParameter"": {} + }, + { + ""$Name"": ""optionalParamWithDefault"", + ""@Org.OData.Core.V1.OptionalParameter"": { + ""DefaultValue"": ""Smith"" + } + } + ], + ""$ReturnType"": {} + } + ], + ""Default"": { + ""$Kind"": ""EntityContainer"", + ""TestFunction"": { + ""$Kind"": ""FunctionImport"", + ""$Function"": ""test.TestFunction"" + } + } + } +}").ConfigureAwait(false); + } + + [Fact] + public async Task ShouldWriteOutofLineOptionalParameters_Async() + { + var stringTypeReference = new EdmStringTypeReference(EdmCoreModel.Instance.GetPrimitiveType(EdmPrimitiveTypeKind.String), false); + var model = new EdmModel(); + var function = new EdmFunction("NS", "TestFunction", stringTypeReference); + var requiredParam = new EdmOperationParameter(function, "requiredParam", stringTypeReference); + var optionalParam = new EdmOptionalParameter(function, "optionalParam", stringTypeReference, null); + var optionalParamWithDefault = new EdmOptionalParameter(function, "optionalParamWithDefault", stringTypeReference, "Smith"); + function.AddParameter(requiredParam); + function.AddParameter(optionalParam); + function.AddParameter(optionalParamWithDefault); + model.AddElement(function); + + // parameter without default value + EdmVocabularyAnnotation annotation = new EdmVocabularyAnnotation(optionalParam, CoreVocabularyModel.OptionalParameterTerm, new EdmRecordExpression()); + annotation.SetSerializationLocation(model, EdmVocabularyAnnotationSerializationLocation.OutOfLine); + model.SetVocabularyAnnotation(annotation); + + // parameter with default value + IEdmComplexType optionalParameterType = CoreVocabularyModel.Instance.FindDeclaredType("Org.OData.Core.V1.OptionalParameterType") as IEdmComplexType; + Assert.NotNull(optionalParameterType); + + IEdmRecordExpression optionalParameterRecord = new EdmRecordExpression( + new EdmComplexTypeReference(optionalParameterType, false), + new EdmPropertyConstructor("DefaultValue", new EdmStringConstant("Smith"))); + annotation = new EdmVocabularyAnnotation(optionalParamWithDefault, CoreVocabularyModel.OptionalParameterTerm, optionalParameterRecord); + annotation.SetSerializationLocation(model, EdmVocabularyAnnotationSerializationLocation.OutOfLine); + model.SetVocabularyAnnotation(annotation); + + // Act & Assert for XML + await WriteAndVerifyXmlAsync(model, "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "").ConfigureAwait(false); + + // Act & Assert for JSON + await WriteAndVerifyJsonAsync(model, @"{ + ""$Version"": ""4.0"", + ""NS"": { + ""TestFunction"": [ + { + ""$Kind"": ""Function"", + ""$Parameter"": [ + { + ""$Name"": ""requiredParam"" + }, + { + ""$Name"": ""optionalParam"" + }, + { + ""$Name"": ""optionalParamWithDefault"" + } + ], + ""$ReturnType"": {} + } + ], + ""$Annotations"": { + ""NS.TestFunction(Edm.String, Edm.String, Edm.String)/optionalParam"": { + ""@Org.OData.Core.V1.OptionalParameter"": {} + }, + ""NS.TestFunction(Edm.String, Edm.String, Edm.String)/optionalParamWithDefault"": { + ""@Org.OData.Core.V1.OptionalParameter"": { + ""@type"": ""Org.OData.Core.V1.OptionalParameterType"", + ""DefaultValue"": ""Smith"" + } + } + } + } +}").ConfigureAwait(false); + } + + [Fact] + public async Task ShouldWriteOutOfLineOptionalParametersOverwriteInLineOptionalParameter_Async() + { + // Arrange + var stringTypeReference = new EdmStringTypeReference(EdmCoreModel.Instance.GetPrimitiveType(EdmPrimitiveTypeKind.String), false); + var model = new EdmModel(); + var function = new EdmFunction("NS", "TestFunction", stringTypeReference); + var optionalParamWithDefault = new EdmOptionalParameter(function, "optionalParamWithDefault", stringTypeReference, "InlineDefaultValue"); + function.AddParameter(optionalParamWithDefault); + model.AddElement(function); + + // parameter with default value + IEdmComplexType optionalParameterType = CoreVocabularyModel.Instance.FindDeclaredType("Org.OData.Core.V1.OptionalParameterType") as IEdmComplexType; + Assert.NotNull(optionalParameterType); + + IEdmRecordExpression optionalParameterRecord = new EdmRecordExpression( + new EdmComplexTypeReference(optionalParameterType, false), + new EdmPropertyConstructor("DefaultValue", new EdmStringConstant("OutofLineValue"))); + EdmVocabularyAnnotation annotation = new EdmVocabularyAnnotation(optionalParamWithDefault, CoreVocabularyModel.OptionalParameterTerm, optionalParameterRecord); + annotation.SetSerializationLocation(model, EdmVocabularyAnnotationSerializationLocation.OutOfLine); + model.SetVocabularyAnnotation(annotation); + + // Act & Assert for XML + await WriteAndVerifyXmlAsync(model, "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "").ConfigureAwait(false); + + // Act & Assert for JSON + await WriteAndVerifyJsonAsync(model, @"{ + ""$Version"": ""4.0"", + ""NS"": { + ""TestFunction"": [ + { + ""$Kind"": ""Function"", + ""$Parameter"": [ + { + ""$Name"": ""optionalParamWithDefault"" + } + ], + ""$ReturnType"": {} + } + ], + ""$Annotations"": { + ""NS.TestFunction(Edm.String)/optionalParamWithDefault"": { + ""@Org.OData.Core.V1.OptionalParameter"": { + ""@type"": ""Org.OData.Core.V1.OptionalParameterType"", + ""DefaultValue"": ""OutofLineValue"" + } + } + } + } +}").ConfigureAwait(false); + } + + #endregion + + [Fact] + public async Task ShouldWriteInLineReturnTypeAnnotation_Async() + { + // Arrange + IEdmModel model = GetReturnTypeModel(EdmVocabularyAnnotationSerializationLocation.Inline); + + // Act & Assert for XML + await WriteAndVerifyXmlAsync(model, "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "Edm.Int32" + + "Edm.Boolean" + + "" + + "" + + "" + + "" + + "" + + "" + + "").ConfigureAwait(false); + + // Act & Assert for JSON + await WriteAndVerifyJsonAsync(model, @"{ + ""$Version"": ""4.0"", + ""NS"": { + ""TestFunction"": [ + { + ""$Kind"": ""Function"", + ""$ReturnType"": { + ""$Type"": ""Edm.PrimitiveType"", + ""@Org.OData.Validation.V1.DerivedTypeConstraint"": [ + ""Edm.Int32"", + ""Edm.Boolean"" + ] + } + } + ] + } +}").ConfigureAwait(false); + } + + [Fact] + public async Task ShouldWriteOutofLineReturnTypeAnnotation_Async() + { + // Arrange + IEdmModel model = GetReturnTypeModel(EdmVocabularyAnnotationSerializationLocation.OutOfLine); + + // Act & Assert for XML + await WriteAndVerifyXmlAsync(model, "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "Edm.Int32" + + "Edm.Boolean" + + "" + + "" + + "" + + "" + + "" + + "").ConfigureAwait(false); + + // Act & Assert for JSON + await WriteAndVerifyJsonAsync(model, @"{ + ""$Version"": ""4.0"", + ""NS"": { + ""TestFunction"": [ + { + ""$Kind"": ""Function"", + ""$ReturnType"": { + ""$Type"": ""Edm.PrimitiveType"" + } + } + ], + ""$Annotations"": { + ""NS.TestFunction()/$ReturnType"": { + ""@Org.OData.Validation.V1.DerivedTypeConstraint"": [ + ""Edm.Int32"", + ""Edm.Boolean"" + ] + } + } + } +}").ConfigureAwait(false); + } + + [Fact] + public async Task ShouldWriteEdmComplexTypeProperty_Async() + { + // Arrange + EdmModel model = new EdmModel(); + EdmEntityType customer = new EdmEntityType("NS", "Customer"); + customer.AddKeys(customer.AddStructuralProperty("Id", EdmCoreModel.Instance.GetInt32(false))); + customer.AddStructuralProperty("ComplexProperty", EdmCoreModel.Instance.GetComplexType(true)); + model.AddElement(customer); + + // Act & Assert for XML + await WriteAndVerifyXmlAsync(model, "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "").ConfigureAwait(false); + + // Act & Assert for JSON + await WriteAndVerifyJsonAsync(model, @"{ + ""$Version"": ""4.0"", + ""NS"": { + ""Customer"": { + ""$Kind"": ""EntityType"", + ""$Key"": [ + ""Id"" + ], + ""Id"": { + ""$Type"": ""Edm.Int32"" + }, + ""ComplexProperty"": { + ""$Type"": ""Edm.ComplexType"", + ""$Nullable"": true + } + } + } +}").ConfigureAwait(false); + } + + [Fact] + public async Task ShouldWriteEdmEntityTypeProperty_Async() + { + // Arrange + EdmModel model = new EdmModel(); + EdmEntityType customer = new EdmEntityType("NS", "Customer"); + customer.AddKeys(customer.AddStructuralProperty("Id", EdmCoreModel.Instance.GetInt32(false))); + customer.AddUnidirectionalNavigation(new EdmNavigationPropertyInfo + { + Target = EdmCoreModel.Instance.GetEntityType(true).EntityDefinition(), + ContainsTarget = false, + TargetMultiplicity = EdmMultiplicity.ZeroOrOne, + Name = "EntityNavigationProperty" + }); + model.AddElement(customer); + + // Act & Assert for XML + await WriteAndVerifyXmlAsync(model, "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "").ConfigureAwait(false); + + // Act & Assert for JSON + await WriteAndVerifyJsonAsync(model, @"{ + ""$Version"": ""4.0"", + ""NS"": { + ""Customer"": { + ""$Kind"": ""EntityType"", + ""$Key"": [ + ""Id"" + ], + ""Id"": { + ""$Type"": ""Edm.Int32"" + }, + ""EntityNavigationProperty"": { + ""$Kind"": ""NavigationProperty"", + ""$Type"": ""Edm.EntityType"" + } + } + } +}").ConfigureAwait(false); + } + + [Fact] + public async Task ShouldWriteEdmPathTypeProperty_Async() + { + // Arrange + EdmModel model = new EdmModel(); + EdmComplexType complexType = new EdmComplexType("NS", "SelectType"); + complexType.AddStructuralProperty("DefaultSelect", new EdmCollectionTypeReference(new EdmCollectionType(EdmCoreModel.Instance.GetPropertyPath(true)))); + complexType.AddStructuralProperty("DefaultHidden", new EdmCollectionTypeReference(new EdmCollectionType(EdmCoreModel.Instance.GetNavigationPropertyPath(false)))); + model.AddElement(complexType); + EdmTerm term = new EdmTerm("NS", "MyTerm", new EdmComplexTypeReference(complexType, true)); + model.AddElement(term); + + // Act & Assert for XML + await WriteAndVerifyXmlAsync(model, "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "").ConfigureAwait(false); + + // Act & Assert for JSON + await WriteAndVerifyJsonAsync(model, @"{ + ""$Version"": ""4.0"", + ""NS"": { + ""SelectType"": { + ""$Kind"": ""ComplexType"", + ""DefaultSelect"": { + ""$Collection"": true, + ""$Type"": ""Edm.PropertyPath"", + ""$Nullable"": true + }, + ""DefaultHidden"": { + ""$Collection"": true, + ""$Type"": ""Edm.NavigationPropertyPath"" + } + }, + ""MyTerm"": { + ""$Kind"": ""Term"", + ""$Type"": ""NS.SelectType"", + ""$Nullable"": true + } + } +}").ConfigureAwait(false); + } + + [Fact] + public async Task CanWriteEdmSingletonWithEdmEntityTypeButValidationFailed_Async() + { + // Arrange + EdmModel model = new EdmModel(); + EdmEntityContainer container = new EdmEntityContainer("NS", "Default"); + EdmSingleton singleton = new EdmSingleton(container, "VIP", EdmCoreModel.Instance.GetEntityType()); + container.AddElement(singleton); + model.AddElement(container); + IEnumerable errors; + Assert.False(model.Validate(out errors)); + Assert.Single(errors); + + // Act & Assert for XML + await WriteAndVerifyXmlAsync(model, "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "").ConfigureAwait(false); + + // Act & Assert for JSON + await WriteAndVerifyJsonAsync(model, @"{ + ""$Version"": ""4.0"", + ""$EntityContainer"": ""NS.Default"", + ""NS"": { + ""Default"": { + ""$Kind"": ""EntityContainer"", + ""VIP"": { + ""$Type"": ""Edm.EntityType"" + } + } + } +}").ConfigureAwait(false); + } + + [Fact] + public async Task CanWriteEdmEntitySetWithEdmEntityTypeButValidationFailed_Async() + { + // Arrange + EdmModel model = new EdmModel(); + EdmEntityContainer container = new EdmEntityContainer("NS", "Default"); + EdmEntitySet entitySet = new EdmEntitySet(container, "Customers", EdmCoreModel.Instance.GetEntityType()); + container.AddElement(entitySet); + model.AddElement(container); + IEnumerable errors; + Assert.False(model.Validate(out errors)); + Assert.Equal(2, errors.Count()); + + await WriteAndVerifyXmlAsync(model, "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "").ConfigureAwait(false); + + // Act & Assert for JSON + await WriteAndVerifyJsonAsync(model, @"{ + ""$Version"": ""4.0"", + ""$EntityContainer"": ""NS.Default"", + ""NS"": { + ""Default"": { + ""$Kind"": ""EntityContainer"", + ""Customers"": { + ""$Collection"": true, + ""$Type"": ""Edm.EntityType"" + } + } + } +}").ConfigureAwait(false); + } + + [Fact] + public async Task CanWriteEdmEntityTypeWithEdmPrimitiveTypeKeyButValidationFailed_Async() + { + // Arrange + EdmModel model = new EdmModel(); + EdmEntityType customer = new EdmEntityType("NS", "Customer"); + customer.AddKeys(customer.AddStructuralProperty("Id", EdmCoreModel.Instance.GetPrimitiveType(false))); + model.AddElement(customer); + IEnumerable errors; + Assert.False(model.Validate(out errors)); + Assert.Single(errors); + + // Act & Assert for XML + await WriteAndVerifyXmlAsync(model, "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "").ConfigureAwait(false); + + // Act & Assert for JSON + await WriteAndVerifyJsonAsync(model, @"{ + ""$Version"": ""4.0"", + ""NS"": { + ""Customer"": { + ""$Kind"": ""EntityType"", + ""$Key"": [ + ""Id"" + ], + ""Id"": { + ""$Type"": ""Edm.PrimitiveType"" + } + } + } +}").ConfigureAwait(false); + } + + [Fact] + public async Task CanWriteEdmEntityTypeWithCollectionAbstractTypeButValidationFailed_Async() + { + // Arrange + EdmModel model = new EdmModel(); + EdmEntityType customer = new EdmEntityType("NS", "Customer"); + customer.AddKeys(customer.AddStructuralProperty("Id", EdmCoreModel.Instance.GetInt32(false))); + customer.AddStructuralProperty("Primitive", + new EdmCollectionTypeReference(new EdmCollectionType(EdmCoreModel.Instance.GetPrimitiveType(true)))); + customer.AddStructuralProperty("Complex", + new EdmCollectionTypeReference(new EdmCollectionType(EdmCoreModel.Instance.GetComplexType(true)))); + model.AddElement(customer); + IEnumerable errors; + Assert.False(model.Validate(out errors)); + Assert.Equal(2, errors.Count()); + + // Act & Assert for XML + await WriteAndVerifyXmlAsync(model, "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "").ConfigureAwait(false); + + // Act & Assert for JSON + await WriteAndVerifyJsonAsync(model, @"{ + ""$Version"": ""4.0"", + ""NS"": { + ""Customer"": { + ""$Kind"": ""EntityType"", + ""$Key"": [ + ""Id"" + ], + ""Id"": { + ""$Type"": ""Edm.Int32"" + }, + ""Primitive"": { + ""$Collection"": true, + ""$Type"": ""Edm.PrimitiveType"", + ""$Nullable"": true + }, + ""Complex"": { + ""$Collection"": true, + ""$Type"": ""Edm.ComplexType"", + ""$Nullable"": true + } + } + } +}").ConfigureAwait(false); + } + + [Fact] + public async Task CanWriteEdmStructuredTypeWithAbstractBaseTypeButValidationFailed_Async() + { + // Arrange + EdmModel model = new EdmModel(); + EdmEntityType customer = new EdmEntityType("NS", "Customer", EdmCoreModel.Instance.GetEntityType()); + model.AddElement(customer); + EdmComplexType address = new EdmComplexType("NS", "Address", EdmCoreModel.Instance.GetComplexType()); + model.AddElement(address); + IEnumerable errors; + Assert.False(model.Validate(out errors)); + Assert.Equal(2, errors.Count()); + + // Act & Assert for XML + await WriteAndVerifyXmlAsync(model, "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "").ConfigureAwait(false); + + // Act & Assert for JSON + await WriteAndVerifyJsonAsync(model, @"{ + ""$Version"": ""4.0"", + ""NS"": { + ""Customer"": { + ""$Kind"": ""EntityType"", + ""$BaseType"": ""Edm.EntityType"" + }, + ""Address"": { + ""$Kind"": ""ComplexType"", + ""$BaseType"": ""Edm.ComplexType"" + } + } +}").ConfigureAwait(false); + } + + [Fact] + public async Task CanWriteEdmTypeDefinitionWithEdmPrimitiveTypeButValidationFailed_Async() + { + // Arrange + EdmModel model = new EdmModel(); + EdmTypeDefinition definition = new EdmTypeDefinition("NS", "MyType", EdmPrimitiveTypeKind.PrimitiveType); + model.AddElement(definition); + IEnumerable errors; + Assert.False(model.Validate(out errors)); + Assert.Single(errors); + + // Act & Assert for XML + await WriteAndVerifyXmlAsync(model, "" + + "" + + "" + + "" + + "" + + "" + + "" + + "").ConfigureAwait(false); + + // Act & Assert for JSON + await WriteAndVerifyJsonAsync(model, @"{ + ""$Version"": ""4.0"", + ""NS"": { + ""MyType"": { + ""$Kind"": ""TypeDefinition"", + ""$UnderlyingType"": ""Edm.PrimitiveType"" + } + } +}").ConfigureAwait(false); + } + + [Fact] + public async Task CanWriteEdmFunctioneWithCollectionAbstractTypeButValidationFailed_Async() + { + // Arrange + EdmModel model = new EdmModel(); + EdmFunction function = new EdmFunction("NS", "GetCustomer", new EdmCollectionTypeReference(new EdmCollectionType(EdmCoreModel.Instance.GetPrimitiveType(true)))); + model.AddElement(function); + function = new EdmFunction("NS", "GetSomething", new EdmCollectionTypeReference(new EdmCollectionType(EdmCoreModel.Instance.GetComplexType(false)))); + model.AddElement(function); + IEnumerable errors; + Assert.False(model.Validate(out errors)); + Assert.Equal(2, errors.Count()); + + // Act & Assert for XML + await WriteAndVerifyXmlAsync(model, "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "").ConfigureAwait(false); + + // Act & Assert for JSON + await WriteAndVerifyJsonAsync(model, @"{ + ""$Version"": ""4.0"", + ""NS"": { + ""GetCustomer"": [ + { + ""$Kind"": ""Function"", + ""$ReturnType"": { + ""$Collection"": true, + ""$Type"": ""Edm.PrimitiveType"", + ""$Nullable"": true + } + } + ], + ""GetSomething"": [ + { + ""$Kind"": ""Function"", + ""$ReturnType"": { + ""$Collection"": true, + ""$Type"": ""Edm.ComplexType"" + } + } + ] + } +}").ConfigureAwait(false); + } + + [Fact] + public async Task ShouldWriteAnnotationForEnumMember_Async() + { + // Arrange + EdmModel model = new EdmModel(); + EdmEnumType appliance = new EdmEnumType("NS", "Appliance", EdmPrimitiveTypeKind.Int64, isFlags: true); + model.AddElement(appliance); + + var stove = new EdmEnumMember(appliance, "Stove", new EdmEnumMemberValue(1)); + appliance.AddMember(stove); + + var washer = new EdmEnumMember(appliance, "Washer", new EdmEnumMemberValue(2)); + appliance.AddMember(washer); + + EdmVocabularyAnnotation annotation = new EdmVocabularyAnnotation(stove, CoreVocabularyModel.LongDescriptionTerm, new EdmStringConstant("Stove Inline LongDescription")); + annotation.SetSerializationLocation(model, EdmVocabularyAnnotationSerializationLocation.Inline); + model.SetVocabularyAnnotation(annotation); + + annotation = new EdmVocabularyAnnotation(washer, CoreVocabularyModel.LongDescriptionTerm, new EdmStringConstant("Washer OutOfLine LongDescription")); + annotation.SetSerializationLocation(model, EdmVocabularyAnnotationSerializationLocation.OutOfLine); + model.SetVocabularyAnnotation(annotation); + + EdmTerm term = new EdmTerm("NS", "MyTerm", EdmCoreModel.Instance.GetString(true)); + model.AddElement(term); + annotation = new EdmVocabularyAnnotation(stove, term, new EdmStringConstant("Stove OutOfLine MyTerm Value")); + annotation.SetSerializationLocation(model, EdmVocabularyAnnotationSerializationLocation.OutOfLine); + model.SetVocabularyAnnotation(annotation); + + annotation = new EdmVocabularyAnnotation(washer, term, new EdmStringConstant("Washer Inline MyTerm Value")); + annotation.SetSerializationLocation(model, EdmVocabularyAnnotationSerializationLocation.Inline); + model.SetVocabularyAnnotation(annotation); + + await WriteAndVerifyXmlAsync(model, "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "").ConfigureAwait(false); + + // Act & Assert for JSON + await WriteAndVerifyJsonAsync(model, @"{ + ""$Version"": ""4.0"", + ""NS"": { + ""Appliance"": { + ""$Kind"": ""EnumType"", + ""$UnderlyingType"": ""Edm.Int64"", + ""$IsFlags"": true, + ""Stove"": 1, + ""Stove@Org.OData.Core.V1.LongDescription"": ""Stove Inline LongDescription"", + ""Washer"": 2, + ""Washer@NS.MyTerm"": ""Washer Inline MyTerm Value"" + }, + ""MyTerm"": { + ""$Kind"": ""Term"", + ""$Nullable"": true + }, + ""$Annotations"": { + ""NS.Appliance/Stove"": { + ""@NS.MyTerm"": ""Stove OutOfLine MyTerm Value"" + }, + ""NS.Appliance/Washer"": { + ""@Org.OData.Core.V1.LongDescription"": ""Washer OutOfLine LongDescription"" + } + } + } +}").ConfigureAwait(false); + } + + [Fact] + public async Task CanWritePropertyWithCoreTypeDefinitionAndValidationPassed_Async() + { + // Arrange + EdmModel model = new EdmModel(); + var localDateTime = model.FindType("Org.OData.Core.V1.LocalDateTime") as IEdmTypeDefinition; + Assert.NotNull(localDateTime); + + var qualifiedTypeName = model.FindType("Org.OData.Core.V1.QualifiedTypeName") as IEdmTypeDefinition; + Assert.NotNull(qualifiedTypeName); + + EdmComplexType type = new EdmComplexType("NS", "Complex"); + type.AddStructuralProperty("ModifiedDate", new EdmTypeDefinitionReference(localDateTime, true)); + type.AddStructuralProperty("QualifiedName", new EdmTypeDefinitionReference(qualifiedTypeName, true)); + + model.AddElement(type); + IEnumerable errors; + Assert.True(model.Validate(out errors)); + await WriteAndVerifyXmlAsync(model, "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "").ConfigureAwait(false); + + // Act & Assert for JSON + await WriteAndVerifyJsonAsync(model, @"{ + ""$Version"": ""4.0"", + ""NS"": { + ""Complex"": { + ""$Kind"": ""ComplexType"", + ""ModifiedDate"": { + ""$Type"": ""Org.OData.Core.V1.LocalDateTime"", + ""$Nullable"": true + }, + ""QualifiedName"": { + ""$Type"": ""Org.OData.Core.V1.QualifiedTypeName"", + ""$Nullable"": true + } + } + } +}").ConfigureAwait(false); + } + + [Fact] + public async Task CanWriteNavigationPropertyBindingWithTargetPathOnContainmentOnSingleton_Async() + { + // Arrange + EdmModel model = new EdmModel(); + EdmEntityType customer = new EdmEntityType("NS", "Customer"); + customer.AddKeys(customer.AddStructuralProperty("Id", EdmPrimitiveTypeKind.Int32)); + EdmEntityType order = new EdmEntityType("NS", "Order"); + order.AddKeys(order.AddStructuralProperty("Id", EdmPrimitiveTypeKind.Int32)); + EdmEntityType orderLine = new EdmEntityType("NS", "OrderLine"); + orderLine.AddKeys(orderLine.AddStructuralProperty("Id", EdmPrimitiveTypeKind.Int32)); + + // Customer + // -> ContainedOrders (Contained) + // -> ContainedOrderLines (Contained) + customer.AddUnidirectionalNavigation(new EdmNavigationPropertyInfo + { + Name = "ContainedOrders", + TargetMultiplicity = EdmMultiplicity.Many, + Target = order, + ContainsTarget = true + }); + + var orderLinesContainedNav = customer.AddUnidirectionalNavigation(new EdmNavigationPropertyInfo + { + Name = "ContainedOrderLines", + TargetMultiplicity = EdmMultiplicity.Many, + Target = orderLine, + ContainsTarget = true + }); + + // Order + // -> OrderLines + var orderLinesNav = order.AddUnidirectionalNavigation(new EdmNavigationPropertyInfo + { + Name = "OrderLines", + TargetMultiplicity = EdmMultiplicity.Many, + Target = orderLine + }); + + model.AddElement(customer); + model.AddElement(order); + model.AddElement(orderLine); + + EdmEntityContainer container = new EdmEntityContainer("NS", "Default"); + EdmSingleton me = new EdmSingleton(container, "Me", customer); + container.AddElement(me); + EdmEntitySet customers = new EdmEntitySet(container, "Customers", customer); + container.AddElement(customers); + model.AddElement(container); + + // Navigation property binding to the containment of the singleton + EdmContainedEntitySet containedEntitySet = new EdmContainedEntitySet(me, orderLinesContainedNav); + customers.AddNavigationTarget(orderLinesNav, containedEntitySet, new EdmPathExpression("ContainedOrders/OrderLines")); + + IEnumerable errors; + Assert.False(model.Validate(out errors)); + + // Act & Assert for XML + await WriteAndVerifyXmlAsync(model, "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "").ConfigureAwait(false); + + // Act & Assert for JSON + await WriteAndVerifyJsonAsync(model, @"{ + ""$Version"": ""4.0"", + ""$EntityContainer"": ""NS.Default"", + ""NS"": { + ""Customer"": { + ""$Kind"": ""EntityType"", + ""$Key"": [ + ""Id"" + ], + ""Id"": { + ""$Type"": ""Edm.Int32"", + ""$Nullable"": true + }, + ""ContainedOrders"": { + ""$Kind"": ""NavigationProperty"", + ""$Collection"": true, + ""$Type"": ""NS.Order"", + ""$ContainsTarget"": true + }, + ""ContainedOrderLines"": { + ""$Kind"": ""NavigationProperty"", + ""$Collection"": true, + ""$Type"": ""NS.OrderLine"", + ""$ContainsTarget"": true + } + }, + ""Order"": { + ""$Kind"": ""EntityType"", + ""$Key"": [ + ""Id"" + ], + ""Id"": { + ""$Type"": ""Edm.Int32"", + ""$Nullable"": true + }, + ""OrderLines"": { + ""$Kind"": ""NavigationProperty"", + ""$Collection"": true, + ""$Type"": ""NS.OrderLine"" + } + }, + ""OrderLine"": { + ""$Kind"": ""EntityType"", + ""$Key"": [ + ""Id"" + ], + ""Id"": { + ""$Type"": ""Edm.Int32"", + ""$Nullable"": true + } + }, + ""Default"": { + ""$Kind"": ""EntityContainer"", + ""Me"": { + ""$Type"": ""NS.Customer"" + }, + ""Customers"": { + ""$Collection"": true, + ""$Type"": ""NS.Customer"", + ""$NavigationPropertyBinding"": { + ""ContainedOrders/OrderLines"": ""Me/ContainedOrderLines"" + } + } + } + } +}").ConfigureAwait(false); + } + + [Fact] + public async Task CanWriteUrlEscapeFunction_Async() + { + // Arrange + EdmModel model = new EdmModel(); + EdmEntityType entityType = new EdmEntityType("NS", "Entity"); + entityType.AddKeys(entityType.AddStructuralProperty("Id", EdmCoreModel.Instance.GetInt32(false))); + EdmFunction function = new EdmFunction("NS", "Function", EdmCoreModel.Instance.GetInt32(true), true, null, false); + function.AddParameter("entity", new EdmEntityTypeReference(entityType, true)); + function.AddParameter("path", EdmCoreModel.Instance.GetString(true)); + model.AddElement(entityType); + model.AddElement(function); + model.SetUrlEscapeFunction(function); + + IEnumerable errors; + Assert.True(model.Validate(out errors)); + + // Act & Assert for XML + await WriteAndVerifyXmlAsync(model, "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "").ConfigureAwait(false); + + // Act & Assert for JSON + await WriteAndVerifyJsonAsync(model, @"{ + ""$Version"": ""4.0"", + ""NS"": { + ""Entity"": { + ""$Kind"": ""EntityType"", + ""$Key"": [ + ""Id"" + ], + ""Id"": { + ""$Type"": ""Edm.Int32"" + } + }, + ""Function"": [ + { + ""$Kind"": ""Function"", + ""$IsBound"": true, + ""$Parameter"": [ + { + ""$Name"": ""entity"", + ""$Type"": ""NS.Entity"", + ""$Nullable"": true + }, + { + ""$Name"": ""path"", + ""$Nullable"": true + } + ], + ""$ReturnType"": { + ""$Type"": ""Edm.Int32"", + ""$Nullable"": true + }, + ""@Org.OData.Community.V1.UrlEscapeFunction"": true + } + ] + } +}").ConfigureAwait(false); + } + + [Fact] + public async Task CanWriteAnnotationPathExpression_Async() + { + // Arrange + EdmModel model = new EdmModel(); + EdmComplexType complex = new EdmComplexType("NS", "Complex"); + model.AddElement(complex); + EdmTerm term1 = new EdmTerm("NS", "MyAnnotationPathTerm", EdmCoreModel.Instance.GetAnnotationPath(false)); + EdmTerm term2 = new EdmTerm("NS", "MyNavigationPathTerm", EdmCoreModel.Instance.GetNavigationPropertyPath(false)); + model.AddElement(term1); + model.AddElement(term2); + + EdmVocabularyAnnotation annotation = new EdmVocabularyAnnotation(complex, term1, new EdmAnnotationPathExpression("abc/efg")); + annotation.SetSerializationLocation(model, EdmVocabularyAnnotationSerializationLocation.Inline); + model.SetVocabularyAnnotation(annotation); + + annotation = new EdmVocabularyAnnotation(complex, term2, new EdmNavigationPropertyPathExpression("123/456.t")); + annotation.SetSerializationLocation(model, EdmVocabularyAnnotationSerializationLocation.Inline); + model.SetVocabularyAnnotation(annotation); + + IEnumerable errors; + Assert.True(model.Validate(out errors)); + + // Act & Assert for XML + await WriteAndVerifyXmlAsync(model, "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "").ConfigureAwait(false); + + // Act & Assert for JSON + await WriteAndVerifyJsonAsync(model, @"{ + ""$Version"": ""4.0"", + ""NS"": { + ""Complex"": { + ""$Kind"": ""ComplexType"", + ""@NS.MyAnnotationPathTerm"": ""abc/efg"", + ""@NS.MyNavigationPathTerm"": ""123/456.t"" + }, + ""MyAnnotationPathTerm"": { + ""$Kind"": ""Term"", + ""$Type"": ""Edm.AnnotationPath"" + }, + ""MyNavigationPathTerm"": { + ""$Kind"": ""Term"", + ""$Type"": ""Edm.NavigationPropertyPath"" + } + } +}").ConfigureAwait(false); + } + + [Fact] + public async Task CanWriteAnnotationWithoutSpecifiedValue_Async() + { + // Arrange + EdmModel model = new EdmModel(); + EdmComplexType complex = new EdmComplexType("NS", "Complex"); + EdmTerm term1 = new EdmTerm("NS", "MyAnnotationPathTerm", EdmCoreModel.Instance.GetAnnotationPath(false)); + EdmTerm term2 = new EdmTerm("NS", "MyDefaultStringTerm", EdmCoreModel.Instance.GetString(false), "Property Term", "This is a test"); + EdmTerm term3 = new EdmTerm("NS", "MyDefaultBoolTerm", EdmCoreModel.Instance.GetBoolean(false), "Property Term", "true"); + model.AddElements(new IEdmSchemaElement[] { complex, term1, term2, term3 }); + + // annotation with value + IEdmVocabularyAnnotation annotation = new EdmVocabularyAnnotation(complex, term1, new EdmAnnotationPathExpression("abc/efg")); + annotation.SetSerializationLocation(model, EdmVocabularyAnnotationSerializationLocation.Inline); + model.SetVocabularyAnnotation(annotation); + + // annotation without value + annotation = term2.CreateVocabularyAnnotation(complex); + annotation.SetSerializationLocation(model, EdmVocabularyAnnotationSerializationLocation.Inline); + model.SetVocabularyAnnotation(annotation); + + annotation = term3.CreateVocabularyAnnotation(complex); + annotation.SetSerializationLocation(model, EdmVocabularyAnnotationSerializationLocation.Inline); + model.SetVocabularyAnnotation(annotation); + + // Validate model + IEnumerable errors; + Assert.True(model.Validate(out errors)); + + // Act & Assert for XML + await WriteAndVerifyXmlAsync(model, "" + + "" + + "" + + "" + + "" + + "" + + "" + // no longer has value + "" + // no longer has value + "" + + "" + + "" + + "" + + "" + + "" + + "").ConfigureAwait(false); + + // Act & Assert for JSON + await WriteAndVerifyJsonAsync(model, @"{ + ""$Version"": ""4.0"", + ""NS"": { + ""Complex"": { + ""$Kind"": ""ComplexType"", + ""@NS.MyAnnotationPathTerm"": ""abc/efg"", + ""@NS.MyDefaultStringTerm"": ""This is a test"", + ""@NS.MyDefaultBoolTerm"": true + }, + ""MyAnnotationPathTerm"": { + ""$Kind"": ""Term"", + ""$Type"": ""Edm.AnnotationPath"" + }, + ""MyDefaultStringTerm"": { + ""$Kind"": ""Term"", + ""$AppliesTo"": [ + ""Property Term"" + ], + ""$DefaultValue"": ""This is a test"" + }, + ""MyDefaultBoolTerm"": { + ""$Kind"": ""Term"", + ""$Type"": ""Edm.Boolean"", + ""$AppliesTo"": [ + ""Property Term"" + ], + ""$DefaultValue"": ""true"" + } + } +}").ConfigureAwait(false); + } + + [Fact] + public async Task CanWriteAnnotationWithVariousPrimitiveDefaultValues_Async() + { + // Arrange + EdmModel model = new EdmModel(); + EdmComplexType complex = new EdmComplexType("NS", "Complex"); + model.AddElement(complex); + + Action registerAction = (kind, value) => + { + string name = $"Default{kind}Term"; + EdmTerm term = new EdmTerm("NS", name, EdmCoreModel.Instance.GetPrimitive(kind, false), null, value); + model.AddElement(term); + + IEdmVocabularyAnnotation annotation = term.CreateVocabularyAnnotation(complex); + annotation.SetSerializationLocation(model, EdmVocabularyAnnotationSerializationLocation.Inline); + model.SetVocabularyAnnotation(annotation); + Assert.NotNull(annotation.Value); + }; + + registerAction(EdmPrimitiveTypeKind.Binary, "01"); + registerAction(EdmPrimitiveTypeKind.Decimal, "0.34"); + registerAction(EdmPrimitiveTypeKind.String, "This is a test"); + registerAction(EdmPrimitiveTypeKind.Duration, "P11DT23H59M59.999999999999S"); + registerAction(EdmPrimitiveTypeKind.TimeOfDay, "21:45:00"); + registerAction(EdmPrimitiveTypeKind.Boolean, "true"); + registerAction(EdmPrimitiveTypeKind.Single, "0.2"); + registerAction(EdmPrimitiveTypeKind.Double, "3.94"); + registerAction(EdmPrimitiveTypeKind.Guid, "21EC2020-3AEA-1069-A2DD-08002B30309D"); + registerAction(EdmPrimitiveTypeKind.Int16, "4"); + registerAction(EdmPrimitiveTypeKind.Int32, "4"); + registerAction(EdmPrimitiveTypeKind.Int64, "4"); + registerAction(EdmPrimitiveTypeKind.Date, "2000-12-10"); + + // Validate model + IEnumerable errors; + Assert.True(model.Validate(out errors)); + + // Act & Assert for XML + await WriteAndVerifyXmlAsync(model, "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "").ConfigureAwait(false); + + // Act & Assert for JSON + await WriteAndVerifyJsonAsync(model, @"{ + ""$Version"": ""4.0"", + ""NS"": { + ""Complex"": { + ""$Kind"": ""ComplexType"", + ""@NS.DefaultBinaryTerm"": ""AQ"", + ""@NS.DefaultDecimalTerm"": 0.34, + ""@NS.DefaultStringTerm"": ""This is a test"", + ""@NS.DefaultDurationTerm"": ""P11DT23H59M59.9999999S"", + ""@NS.DefaultTimeOfDayTerm"": ""21:45:00.0000000"", + ""@NS.DefaultBooleanTerm"": true, + ""@NS.DefaultSingleTerm"": 0.2, + ""@NS.DefaultDoubleTerm"": 3.94, + ""@NS.DefaultGuidTerm"": ""21ec2020-3aea-1069-a2dd-08002b30309d"", + ""@NS.DefaultInt16Term"": 4, + ""@NS.DefaultInt32Term"": 4, + ""@NS.DefaultInt64Term"": 4, + ""@NS.DefaultDateTerm"": ""2000-12-10"" + }, + ""DefaultBinaryTerm"": { + ""$Kind"": ""Term"", + ""$Type"": ""Edm.Binary"", + ""$DefaultValue"": ""01"" + }, + ""DefaultDecimalTerm"": { + ""$Kind"": ""Term"", + ""$Type"": ""Edm.Decimal"", + ""$DefaultValue"": ""0.34"" + }, + ""DefaultStringTerm"": { + ""$Kind"": ""Term"", + ""$DefaultValue"": ""This is a test"" + }, + ""DefaultDurationTerm"": { + ""$Kind"": ""Term"", + ""$Type"": ""Edm.Duration"", + ""$DefaultValue"": ""P11DT23H59M59.999999999999S"", + ""$Precision"": 0 + }, + ""DefaultTimeOfDayTerm"": { + ""$Kind"": ""Term"", + ""$Type"": ""Edm.TimeOfDay"", + ""$DefaultValue"": ""21:45:00"", + ""$Precision"": 0 + }, + ""DefaultBooleanTerm"": { + ""$Kind"": ""Term"", + ""$Type"": ""Edm.Boolean"", + ""$DefaultValue"": ""true"" + }, + ""DefaultSingleTerm"": { + ""$Kind"": ""Term"", + ""$Type"": ""Edm.Single"", + ""$DefaultValue"": ""0.2"" + }, + ""DefaultDoubleTerm"": { + ""$Kind"": ""Term"", + ""$Type"": ""Edm.Double"", + ""$DefaultValue"": ""3.94"" + }, + ""DefaultGuidTerm"": { + ""$Kind"": ""Term"", + ""$Type"": ""Edm.Guid"", + ""$DefaultValue"": ""21EC2020-3AEA-1069-A2DD-08002B30309D"" + }, + ""DefaultInt16Term"": { + ""$Kind"": ""Term"", + ""$Type"": ""Edm.Int16"", + ""$DefaultValue"": ""4"" + }, + ""DefaultInt32Term"": { + ""$Kind"": ""Term"", + ""$Type"": ""Edm.Int32"", + ""$DefaultValue"": ""4"" + }, + ""DefaultInt64Term"": { + ""$Kind"": ""Term"", + ""$Type"": ""Edm.Int64"", + ""$DefaultValue"": ""4"" + }, + ""DefaultDateTerm"": { + ""$Kind"": ""Term"", + ""$Type"": ""Edm.Date"", + ""$DefaultValue"": ""2000-12-10"" + } + } +}").ConfigureAwait(false); + } + + [Fact] + public async Task CanWriteScaleAndSridVariable_UsingLegacyVariable_Async() + { + string csdlTemplate = "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + ""; + + // Parse into CSDL + IEdmModel model; + using (XmlReader xr = XElement.Parse(csdlTemplate).CreateReader()) + { + model = CsdlReader.Parse(xr); + } + + // Validate model + IEnumerable errors; + bool validated = model.Validate(out errors); + Assert.True(validated); + + // Act & Assert for Reserialized XML + await WriteAndVerifyXmlAsync(model, csdlTemplate).ConfigureAwait(false); + } + + [Fact] + public async Task CanWriteNavigationPropertyBindingTargetOnContainmentNavigationProperty_Async() + { + EdmModel model = new EdmModel(); + + var areaEntityType = new EdmEntityType("NS", "Area"); + var key = areaEntityType.AddStructuralProperty("Id", EdmPrimitiveTypeKind.String); + areaEntityType.AddKeys(key); + + var plantEntityType = new EdmEntityType("NS", "Plant"); + key = plantEntityType.AddStructuralProperty("Id", EdmPrimitiveTypeKind.String); + plantEntityType.AddKeys(key); + + var siteEntityType = new EdmEntityType("NS", "Site"); + key = siteEntityType.AddStructuralProperty("Id", EdmPrimitiveTypeKind.String); + siteEntityType.AddKeys(key); + + // Contained entity sets + plantEntityType.AddUnidirectionalNavigation(new EdmNavigationPropertyInfo() + { + ContainsTarget = true, + Name = "Areas", + Target = areaEntityType, + TargetMultiplicity = EdmMultiplicity.Many + }); + + var sitePlantsNavigationProperty = siteEntityType.AddUnidirectionalNavigation(new EdmNavigationPropertyInfo() + { + ContainsTarget = true, + Name = "Plants", + Target = plantEntityType, + TargetMultiplicity = EdmMultiplicity.Many + }); + + // Non-contained navigation property back to plant + var areaPlantNavigationProperty = areaEntityType.AddUnidirectionalNavigation(new EdmNavigationPropertyInfo() + { + ContainsTarget = false, + Name = "Plant", + Target = plantEntityType, + TargetMultiplicity = EdmMultiplicity.One + }); + + model.AddElement(areaEntityType); + model.AddElement(plantEntityType); + model.AddElement(siteEntityType); + + var entityContainer = new EdmEntityContainer("NS", "MyApi"); + var sitesEntitySet = entityContainer.AddEntitySet("Sites", siteEntityType); + var areasEntitySet = entityContainer.AddEntitySet("Areas", areaEntityType); + model.AddElement(entityContainer); + var plantsContainedEntitySet = sitesEntitySet.FindNavigationTarget(sitePlantsNavigationProperty); + areasEntitySet.AddNavigationTarget(areaPlantNavigationProperty, plantsContainedEntitySet); + + // Act & Assert for XML + await WriteAndVerifyXmlAsync(model, "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "").ConfigureAwait(false); + + // Act & Assert for JSON + await WriteAndVerifyJsonAsync(model, @"{ + ""$Version"": ""4.0"", + ""$EntityContainer"": ""NS.MyApi"", + ""NS"": { + ""Area"": { + ""$Kind"": ""EntityType"", + ""$Key"": [ + ""Id"" + ], + ""Id"": { + ""$Nullable"": true + }, + ""Plant"": { + ""$Kind"": ""NavigationProperty"", + ""$Type"": ""NS.Plant"" + } + }, + ""Plant"": { + ""$Kind"": ""EntityType"", + ""$Key"": [ + ""Id"" + ], + ""Id"": { + ""$Nullable"": true + }, + ""Areas"": { + ""$Kind"": ""NavigationProperty"", + ""$Collection"": true, + ""$Type"": ""NS.Area"", + ""$ContainsTarget"": true + } + }, + ""Site"": { + ""$Kind"": ""EntityType"", + ""$Key"": [ + ""Id"" + ], + ""Id"": { + ""$Nullable"": true + }, + ""Plants"": { + ""$Kind"": ""NavigationProperty"", + ""$Collection"": true, + ""$Type"": ""NS.Plant"", + ""$ContainsTarget"": true + } + }, + ""MyApi"": { + ""$Kind"": ""EntityContainer"", + ""Sites"": { + ""$Collection"": true, + ""$Type"": ""NS.Site"" + }, + ""Areas"": { + ""$Collection"": true, + ""$Type"": ""NS.Area"", + ""$NavigationPropertyBinding"": { + ""Plant"": ""Sites/Plants"" + } + } + } + } +}").ConfigureAwait(false); + } + + [Fact] + public async Task CanWriteNavigationPropertyBindingTargetWithTypeCast_Async() + { + EdmModel model = new EdmModel(); + + var purchaseOrderEntityType = new EdmEntityType("NS", "PurchaseOrder"); + var key = purchaseOrderEntityType.AddStructuralProperty("Id", EdmPrimitiveTypeKind.String, /*isNullable*/ false); + purchaseOrderEntityType.AddKeys(key); + + var productEntityType = new EdmEntityType("NS", "Product"); + key = productEntityType.AddStructuralProperty("Id", EdmPrimitiveTypeKind.String, /*isNullable*/ false); + productEntityType.AddKeys(key); + + var widgetEntityType = new EdmEntityType("NS", "Widget", productEntityType); + + var dooHickeyEntityType = new EdmEntityType("NS", "DooHickey", productEntityType); + + // Navigation Property to Product + var productNavigationProperty = purchaseOrderEntityType.AddUnidirectionalNavigation(new EdmNavigationPropertyInfo() + { + ContainsTarget = false, + Name = "Product", + Target = productEntityType, + TargetMultiplicity = EdmMultiplicity.Many + }); + + model.AddElement(purchaseOrderEntityType); + model.AddElement(productEntityType); + model.AddElement(widgetEntityType); + model.AddElement(dooHickeyEntityType); + + var entityContainer = new EdmEntityContainer("NS", "MyApi"); + var purchaseOrdersEntitySet = entityContainer.AddEntitySet("PurchaseOrders", purchaseOrderEntityType); + var widgetEntitySet = entityContainer.AddEntitySet("Widgets", widgetEntityType); + var dooHickeyEntitySet = entityContainer.AddEntitySet("DooHickies", dooHickeyEntityType); + model.AddElement(entityContainer); + purchaseOrdersEntitySet.AddNavigationTarget(productNavigationProperty, widgetEntitySet, new EdmPathExpression("Product/NS.Widget")); + purchaseOrdersEntitySet.AddNavigationTarget(productNavigationProperty, dooHickeyEntitySet, new EdmPathExpression("Product/NS.DooHickey")); + + IEnumerable errors; + Assert.True(model.Validate(out errors)); + Assert.Empty(errors); + + var v40Json = + @"{ + ""$Version"": ""4.0"", + ""$EntityContainer"": ""NS.MyApi"", + ""NS"": { + ""PurchaseOrder"": { + ""$Kind"": ""EntityType"", + ""$Key"": [ + ""Id"" + ], + ""Id"": {}, + ""Product"": { + ""$Kind"": ""NavigationProperty"", + ""$Collection"": true, + ""$Type"": ""NS.Product"" + } + }, + ""Product"": { + ""$Kind"": ""EntityType"", + ""$Key"": [ + ""Id"" + ], + ""Id"": {} + }, + ""Widget"": { + ""$Kind"": ""EntityType"", + ""$BaseType"": ""NS.Product"" + }, + ""DooHickey"": { + ""$Kind"": ""EntityType"", + ""$BaseType"": ""NS.Product"" + }, + ""MyApi"": { + ""$Kind"": ""EntityContainer"", + ""PurchaseOrders"": { + ""$Collection"": true, + ""$Type"": ""NS.PurchaseOrder"", + ""$NavigationPropertyBinding"": { + ""Product/NS.DooHickey"": ""DooHickies"", + ""Product/NS.Widget"": ""Widgets"" + } + }, + ""Widgets"": { + ""$Collection"": true, + ""$Type"": ""NS.Widget"" + }, + ""DooHickies"": { + ""$Collection"": true, + ""$Type"": ""NS.DooHickey"" + } + } + } +}"; + + var xmlResult = + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "{1}" + + "" + + "" + + "" + + "" + + "" + + ""; + + var v40EntitySet = + ""; + + var v401EntitySet = + "" + + "" + + "" + + ""; + + // Act & Assert for XML 4.0 + await WriteAndVerifyXmlAsync(model, String.Format(xmlResult, "4.0", v40EntitySet)).ConfigureAwait(false); + + // Act & Assert for JSON 4.0 + await WriteAndVerifyJsonAsync(model, v40Json).ConfigureAwait(false); + + model.SetEdmVersion(Version.Parse("4.01")); + + // Act & Assert for XML 4.1 + await WriteAndVerifyXmlAsync(model, String.Format(xmlResult, "4.01", v401EntitySet)).ConfigureAwait(false); + + // Act & Assert for JSON 4.1 + await WriteAndVerifyJsonAsync(model, v40Json.Replace("4.0", "4.01")).ConfigureAwait(false); + } + + [Fact] + public async Task CanWriteEdmModelWithUntypedProperty_Async() + { + EdmModel model = new EdmModel(); + + var entityType = new EdmEntityType("NS", "Entity"); + var key = entityType.AddStructuralProperty("Id", EdmPrimitiveTypeKind.String); + entityType.AddKeys(key); + entityType.AddStructuralProperty("Value", EdmCoreModel.Instance.GetUntyped()); + entityType.AddStructuralProperty("Data", new EdmCollectionTypeReference(new EdmCollectionType(EdmCoreModel.Instance.GetUntyped()))); + model.AddElement(entityType); + + // Act & Assert for XML + await WriteAndVerifyXmlAsync(model, "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "").ConfigureAwait(false); + + // Act & Assert for JSON + await WriteAndVerifyJsonAsync(model, @"{ + ""$Version"": ""4.0"", + ""NS"": { + ""Entity"": { + ""$Kind"": ""EntityType"", + ""$Key"": [ + ""Id"" + ], + ""Id"": { + ""$Nullable"": true + }, + ""Value"": { + ""$Type"": ""Edm.Untyped"", + ""$Nullable"": true + }, + ""Data"": { + ""$Collection"": true, + ""$Type"": ""Edm.Untyped"", + ""$Nullable"": true + } + } + } +}").ConfigureAwait(false); + } + + [Theory] + [InlineData("4.0")] + [InlineData("4.01")] + public async Task ValidateEdmxVersions_Async(string odataVersion) + { + // Specify the model + EdmModel edmModel = new EdmModel(false); + edmModel.SetEdmVersion(odataVersion == "4.0" ? EdmConstants.EdmVersion4 : EdmConstants.EdmVersion401); + + // XML + await WriteAndVerifyXmlAsync(edmModel, "").ConfigureAwait(false); + + // JSON + await WriteAndVerifyJsonAsync(edmModel, "{\"$Version\":\"" + odataVersion + "\"}", false).ConfigureAwait(false); + } + + [Fact] + public async Task ShouldSubstituteFullyQualifiedNamespaceWithAliasIfAliasIsSet_Async() + { + // Arrange + var stringTypeReference = new EdmStringTypeReference(EdmCoreModel.Instance.GetPrimitiveType(EdmPrimitiveTypeKind.String), false); + var model = new EdmModel(); + model.SetNamespaceAlias("Org.OData.Core.V1", "Core"); + var function = new EdmFunction("test", "TestFunction", stringTypeReference); + var requiredParam = new EdmOperationParameter(function, "requiredParam", stringTypeReference); + var optionalParam = new EdmOptionalParameter(function, "optionalParam", stringTypeReference, null); + var optionalParamWithDefault = new EdmOptionalParameter(function, "optionalParamWithDefault", stringTypeReference, "Smith"); + function.AddParameter(requiredParam); + function.AddParameter(optionalParam); + function.AddParameter(optionalParamWithDefault); + model.AddElement(function); + model.AddEntityContainer("test", "Default").AddFunctionImport("TestFunction", function); + + // Act & Assert for XML + await WriteAndVerifyXmlAsync(model, "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "").ConfigureAwait(false); + + // Act & Assert for JSON + await WriteAndVerifyJsonAsync(model, @"{ + ""$Version"": ""4.0"", + ""$EntityContainer"": ""test.Default"", + ""test"": { + ""TestFunction"": [ + { + ""$Kind"": ""Function"", + ""$Parameter"": [ + { + ""$Name"": ""requiredParam"" + }, + { + ""$Name"": ""optionalParam"", + ""@Core.OptionalParameter"": {} + }, + { + ""$Name"": ""optionalParamWithDefault"", + ""@Core.OptionalParameter"": { + ""DefaultValue"": ""Smith"" + } + } + ], + ""$ReturnType"": {} + } + ], + ""Default"": { + ""$Kind"": ""EntityContainer"", + ""TestFunction"": { + ""$Kind"": ""FunctionImport"", + ""$Function"": ""test.TestFunction"" + } + } + } +}").ConfigureAwait(false); + } + + [Theory] + [InlineData(CsdlTarget.OData, "", "")] + [InlineData(CsdlTarget.EntityFramework, "", "")] + public async Task TryWriteCsdlAsyncShouldFlushAsync_Async(CsdlTarget csdlTarget, string schemaParentOpeningPartial, string schemaParentClosingPartial) + { + var model = new EdmModel(); + + var customerEntityType = new EdmEntityType("NS", "Customer"); + var key = customerEntityType.AddStructuralProperty("Id", EdmPrimitiveTypeKind.Int32); + customerEntityType.AddKeys(key); + customerEntityType.AddStructuralProperty("Name", EdmPrimitiveTypeKind.String); + model.AddElement(customerEntityType); + + var builder = new StringBuilder(); + using (var writer = XmlWriter.Create(builder, new XmlWriterSettings { Encoding = Encoding.UTF8, Async = true })) + { + Tuple> result = await CsdlWriter.TryWriteCsdlAsync(model, writer, csdlTarget).ConfigureAwait(false); + bool success = result.Item1; + IEnumerable errors = result.Item2; + + if (!success) + { + Assert.True(false, "Serialization was unsuccessful"); + } + + // Xml writer should have flushed whatever is in the buffer before TryWriteCsdl is exited + Assert.Equal( + builder.ToString(), + "" + + "" + + schemaParentOpeningPartial + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + schemaParentClosingPartial + + ""); + } + } + + internal static async Task WriteAndVerifyXmlAsync(IEdmModel model, string expected, CsdlTarget target = CsdlTarget.OData) + { + using (StringWriter sw = new StringWriter()) + { + XmlWriterSettings settings = new XmlWriterSettings() + { + Encoding = Encoding.UTF8, + Async = true + }; + + using (XmlWriter xw = XmlWriter.Create(sw, settings)) + { + Tuple> result = await CsdlWriter.TryWriteCsdlAsync(model, xw, target).ConfigureAwait(false); + bool ok = result.Item1; + IEnumerable errors = result.Item2; + + await xw.FlushAsync().ConfigureAwait(false); + } + + string actual = sw.ToString(); + Assert.Equal(expected, actual); + } + } + + internal async Task WriteAndVerifyJsonAsync(IEdmModel model, string expected, bool indented = true, bool isIeee754Compatible = false) + { +#if NETCOREAPP3_1_OR_GREATER + using (MemoryStream memStream = new MemoryStream()) + { + JsonWriterOptions options = new JsonWriterOptions + { + Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, + Indented = indented, + SkipValidation = false + }; + + using (Utf8JsonWriter jsonWriter = new Utf8JsonWriter(memStream, options)) + { + CsdlJsonWriterSettings settings = CsdlJsonWriterSettings.Default; + settings.IsIeee754Compatible = isIeee754Compatible; + var (ok, errors) = await CsdlWriter.TryWriteCsdlAsync(model, jsonWriter, settings).ConfigureAwait(false); + await jsonWriter.FlushAsync().ConfigureAwait(false); + Assert.True(ok); + } + + memStream.Seek(0, SeekOrigin.Begin); + string actual = new StreamReader(memStream).ReadToEnd(); + Assert.Equal(expected, actual); + } +#endif + } + } +} diff --git a/test/FunctionalTests/Microsoft.OData.Edm.Tests/Csdl/CsdlWriterTests.TargetPath.Async.cs b/test/FunctionalTests/Microsoft.OData.Edm.Tests/Csdl/CsdlWriterTests.TargetPath.Async.cs new file mode 100644 index 0000000000..b930bfd44a --- /dev/null +++ b/test/FunctionalTests/Microsoft.OData.Edm.Tests/Csdl/CsdlWriterTests.TargetPath.Async.cs @@ -0,0 +1,417 @@ +//--------------------------------------------------------------------- +// +// Copyright (C) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information. +// +//--------------------------------------------------------------------- + +using Microsoft.OData.Edm.Csdl; +using Microsoft.OData.Edm.Vocabularies; +using System.Linq; +using System.Threading.Tasks; +using Xunit; + +namespace Microsoft.OData.Edm.Tests.Csdl +{ + public partial class CsdlWriterTests + { + + + [Fact] + public async Task ShouldWriteAnnotationForEntitySetProperty_Async() + { + // Arrange + EdmModel model = new EdmModel(); + EdmEntityType customer = new EdmEntityType("NS", "Customer"); + customer.AddKeys(customer.AddStructuralProperty("Id", EdmCoreModel.Instance.GetInt32(false))); + customer.AddStructuralProperty("Name", EdmPrimitiveTypeKind.String, isNullable: false); + model.AddElement(customer); + + EdmEntityContainer container = new EdmEntityContainer("NS", "Default"); + EdmEntitySet entitySet = new EdmEntitySet(container, "Customers", customer); + container.AddElement(entitySet); + model.AddElement(container); + + IEdmProperty nameProperty = customer.DeclaredProperties.Where(x => x.Name == "Name").FirstOrDefault(); + + EdmTargetPath targetPath = new EdmTargetPath(container, entitySet, nameProperty); + + EdmTerm term = new EdmTerm("NS", "MyTerm", EdmCoreModel.Instance.GetString(true)); + model.AddElement(term); + EdmVocabularyAnnotation annotation = new EdmVocabularyAnnotation(targetPath, term, new EdmStringConstant("Name OutOfLine MyTerm Value")); + annotation.SetSerializationLocation(model, EdmVocabularyAnnotationSerializationLocation.OutOfLine); + model.SetVocabularyAnnotation(annotation); + + await WriteAndVerifyXmlAsync(model, "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "").ConfigureAwait(false); + + // Act & Assert for JSON + await WriteAndVerifyJsonAsync(model, @"{ + ""$Version"": ""4.0"", + ""$EntityContainer"": ""NS.Default"", + ""NS"": { + ""Customer"": { + ""$Kind"": ""EntityType"", + ""$Key"": [ + ""Id"" + ], + ""Id"": { + ""$Type"": ""Edm.Int32"" + }, + ""Name"": {} + }, + ""MyTerm"": { + ""$Kind"": ""Term"", + ""$Nullable"": true + }, + ""Default"": { + ""$Kind"": ""EntityContainer"", + ""Customers"": { + ""$Collection"": true, + ""$Type"": ""NS.Customer"" + } + }, + ""$Annotations"": { + ""NS.Default/Customers/Name"": { + ""@NS.MyTerm"": ""Name OutOfLine MyTerm Value"" + } + } + } +}").ConfigureAwait(false); + } + + [Fact] + public async Task ShouldWriteAnnotationForEntitySetComplexTypeProperty_Async() + { + // Arrange + EdmModel model = new EdmModel(); + + EdmComplexType addressType = new EdmComplexType("NS", "Address"); + addressType.AddStructuralProperty("Street", EdmPrimitiveTypeKind.String, isNullable: false); + addressType.AddStructuralProperty("City", EdmPrimitiveTypeKind.String, isNullable: false); + addressType.AddStructuralProperty("PostalCode", EdmPrimitiveTypeKind.Int32, isNullable: false); + model.AddElement(addressType); + + EdmEntityType customer = new EdmEntityType("NS", "Customer"); + customer.AddKeys(customer.AddStructuralProperty("Id", EdmCoreModel.Instance.GetInt32(false))); + customer.AddStructuralProperty("Name", EdmPrimitiveTypeKind.String, isNullable: false); + customer.AddStructuralProperty("Address", new EdmComplexTypeReference(addressType, isNullable: false)); + model.AddElement(customer); + + EdmEntityContainer container = new EdmEntityContainer("NS", "Default"); + EdmEntitySet entitySet = new EdmEntitySet(container, "Customers", customer); + container.AddElement(entitySet); + model.AddElement(container); + + IEdmProperty addressProperty = customer.DeclaredProperties.Where(x => x.Name == "Address").FirstOrDefault(); + IEdmProperty streetProperty = addressType.DeclaredProperties.Where(x => x.Name == "Street").FirstOrDefault(); + + EdmTargetPath targetPath = new EdmTargetPath(container, entitySet, addressProperty, streetProperty); + + EdmTerm term = new EdmTerm("NS", "MyTerm", EdmCoreModel.Instance.GetString(true)); + model.AddElement(term); + EdmVocabularyAnnotation annotation = new EdmVocabularyAnnotation(targetPath, term, new EdmStringConstant("Name OutOfLine MyTerm Value")); + annotation.SetSerializationLocation(model, EdmVocabularyAnnotationSerializationLocation.OutOfLine); + model.SetVocabularyAnnotation(annotation); + + await WriteAndVerifyXmlAsync(model, "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "").ConfigureAwait(false); + + // Act & Assert for JSON + await WriteAndVerifyJsonAsync(model, @"{ + ""$Version"": ""4.0"", + ""$EntityContainer"": ""NS.Default"", + ""NS"": { + ""Address"": { + ""$Kind"": ""ComplexType"", + ""Street"": {}, + ""City"": {}, + ""PostalCode"": { + ""$Type"": ""Edm.Int32"" + } + }, + ""Customer"": { + ""$Kind"": ""EntityType"", + ""$Key"": [ + ""Id"" + ], + ""Id"": { + ""$Type"": ""Edm.Int32"" + }, + ""Name"": {}, + ""Address"": { + ""$Type"": ""NS.Address"" + } + }, + ""MyTerm"": { + ""$Kind"": ""Term"", + ""$Nullable"": true + }, + ""Default"": { + ""$Kind"": ""EntityContainer"", + ""Customers"": { + ""$Collection"": true, + ""$Type"": ""NS.Customer"" + } + }, + ""$Annotations"": { + ""NS.Default/Customers/Address/Street"": { + ""@NS.MyTerm"": ""Name OutOfLine MyTerm Value"" + } + } + } +}").ConfigureAwait(false); + } + + [Fact] + public async Task ShouldWriteAnnotationForSingletonComplexTypeProperty_Async() + { + // Arrange + EdmModel model = new EdmModel(); + + EdmComplexType addressType = new EdmComplexType("NS", "Address"); + addressType.AddStructuralProperty("Street", EdmPrimitiveTypeKind.String, isNullable: false); + addressType.AddStructuralProperty("City", EdmPrimitiveTypeKind.String, isNullable: false); + addressType.AddStructuralProperty("PostalCode", EdmPrimitiveTypeKind.Int32, isNullable: false); + model.AddElement(addressType); + + EdmEntityType customer = new EdmEntityType("NS", "Customer"); + customer.AddKeys(customer.AddStructuralProperty("Id", EdmCoreModel.Instance.GetInt32(false))); + customer.AddStructuralProperty("Name", EdmPrimitiveTypeKind.String, isNullable: false); + customer.AddStructuralProperty("Address", new EdmComplexTypeReference(addressType, isNullable: false)); + model.AddElement(customer); + + EdmEntityContainer container = new EdmEntityContainer("NS", "Default"); + EdmEntitySet entitySet = new EdmEntitySet(container, "Customers", customer); + container.AddElement(entitySet); + IEdmSingleton singleton = container.AddSingleton("SpecialCustomer", customer); + model.AddElement(container); + + IEdmProperty addressProperty = customer.DeclaredProperties.Where(x => x.Name == "Address").FirstOrDefault(); + IEdmProperty streetProperty = addressType.DeclaredProperties.Where(x => x.Name == "Street").FirstOrDefault(); + + EdmTargetPath targetPath = new EdmTargetPath(container, singleton, addressProperty, streetProperty); + + EdmTerm term = new EdmTerm("NS", "MyTerm", EdmCoreModel.Instance.GetString(true)); + model.AddElement(term); + EdmVocabularyAnnotation annotation = new EdmVocabularyAnnotation(targetPath, term, new EdmStringConstant("Name OutOfLine MyTerm Value")); + annotation.SetSerializationLocation(model, EdmVocabularyAnnotationSerializationLocation.OutOfLine); + model.SetVocabularyAnnotation(annotation); + + await WriteAndVerifyXmlAsync(model, "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "").ConfigureAwait(false); + + // Act & Assert for JSON + await WriteAndVerifyJsonAsync(model, @"{ + ""$Version"": ""4.0"", + ""$EntityContainer"": ""NS.Default"", + ""NS"": { + ""Address"": { + ""$Kind"": ""ComplexType"", + ""Street"": {}, + ""City"": {}, + ""PostalCode"": { + ""$Type"": ""Edm.Int32"" + } + }, + ""Customer"": { + ""$Kind"": ""EntityType"", + ""$Key"": [ + ""Id"" + ], + ""Id"": { + ""$Type"": ""Edm.Int32"" + }, + ""Name"": {}, + ""Address"": { + ""$Type"": ""NS.Address"" + } + }, + ""MyTerm"": { + ""$Kind"": ""Term"", + ""$Nullable"": true + }, + ""Default"": { + ""$Kind"": ""EntityContainer"", + ""Customers"": { + ""$Collection"": true, + ""$Type"": ""NS.Customer"" + }, + ""SpecialCustomer"": { + ""$Type"": ""NS.Customer"" + } + }, + ""$Annotations"": { + ""NS.Default/SpecialCustomer/Address/Street"": { + ""@NS.MyTerm"": ""Name OutOfLine MyTerm Value"" + } + } + } +}").ConfigureAwait(false); + } + + [Fact] + public async Task ShouldWriteAnnotationForEntitySetDerivedTypeProperty_Async() + { + // Arrange + EdmModel model = new EdmModel(); + + EdmEntityType customer = new EdmEntityType("NS", "Customer"); + customer.AddKeys(customer.AddStructuralProperty("Id", EdmCoreModel.Instance.GetInt32(false))); + customer.AddStructuralProperty("Name", EdmPrimitiveTypeKind.String, isNullable: false); + model.AddElement(customer); + + EdmEntityType vipCustomer = new EdmEntityType("NS", "VipCustomer", customer); + vipCustomer.AddStructuralProperty("VipNumber", EdmPrimitiveTypeKind.String, isNullable: false); + model.AddElement(vipCustomer); + + EdmEntityContainer container = new EdmEntityContainer("NS", "Default"); + EdmEntitySet entitySet = new EdmEntitySet(container, "Customers", customer); + container.AddElement(entitySet); + model.AddElement(container); + + IEdmProperty vipNoProperty = vipCustomer.DeclaredProperties.Where(x => x.Name == "VipNumber").FirstOrDefault(); + + EdmTargetPath targetPath = new EdmTargetPath(container, entitySet, vipCustomer, vipNoProperty); + + EdmTerm term = new EdmTerm("NS", "MyTerm", EdmCoreModel.Instance.GetString(true)); + model.AddElement(term); + EdmVocabularyAnnotation annotation = new EdmVocabularyAnnotation(targetPath, term, new EdmStringConstant("Name OutOfLine MyTerm Value")); + annotation.SetSerializationLocation(model, EdmVocabularyAnnotationSerializationLocation.OutOfLine); + model.SetVocabularyAnnotation(annotation); + + await WriteAndVerifyXmlAsync(model, "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "").ConfigureAwait(false); + + // Act & Assert for JSON + await WriteAndVerifyJsonAsync(model, @"{ + ""$Version"": ""4.0"", + ""$EntityContainer"": ""NS.Default"", + ""NS"": { + ""Customer"": { + ""$Kind"": ""EntityType"", + ""$Key"": [ + ""Id"" + ], + ""Id"": { + ""$Type"": ""Edm.Int32"" + }, + ""Name"": {} + }, + ""VipCustomer"": { + ""$Kind"": ""EntityType"", + ""$BaseType"": ""NS.Customer"", + ""VipNumber"": {} + }, + ""MyTerm"": { + ""$Kind"": ""Term"", + ""$Nullable"": true + }, + ""Default"": { + ""$Kind"": ""EntityContainer"", + ""Customers"": { + ""$Collection"": true, + ""$Type"": ""NS.Customer"" + } + }, + ""$Annotations"": { + ""NS.Default/Customers/NS.VipCustomer/VipNumber"": { + ""@NS.MyTerm"": ""Name OutOfLine MyTerm Value"" + } + } + } +}").ConfigureAwait(false); + } + } +} diff --git a/test/FunctionalTests/Microsoft.OData.Edm.Tests/Csdl/Serialization/EdmModelCsdlSchemaWriterTests.Async.cs b/test/FunctionalTests/Microsoft.OData.Edm.Tests/Csdl/Serialization/EdmModelCsdlSchemaWriterTests.Async.cs new file mode 100644 index 0000000000..722212a8f9 --- /dev/null +++ b/test/FunctionalTests/Microsoft.OData.Edm.Tests/Csdl/Serialization/EdmModelCsdlSchemaWriterTests.Async.cs @@ -0,0 +1,187 @@ +//--------------------------------------------------------------------- +// +// Copyright (C) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information. +// +//--------------------------------------------------------------------- + +using System; +using System.IO; +using System.Threading.Tasks; +using System.Xml; +using Microsoft.OData.Edm.Csdl; +using Microsoft.OData.Edm.Csdl.Serialization; +using Microsoft.OData.Edm.Vocabularies; +using Xunit; + +namespace Microsoft.OData.Edm.Tests.Csdl.Serialization +{ + public partial class EdmModelCsdlSchemaWriterTests + { + #region Action/Function element attribute writer tests. + [Fact] + public async Task BoundOperationShouldWriteIsBoundEqualTrueAttribute_Async() + { + EdmAction action = new EdmAction("Default.Namespace", "Checkout", null /*returnType*/, true /*isBound*/, null /*entitySetPath*/); + action.AddParameter("param", EdmCoreModel.Instance.GetString(true)); + await this.TestWriteActionElementHeaderMethodWithAsync(action, @" await writer.WriteActionElementHeaderAsync(action).ConfigureAwait(false), expected).ConfigureAwait(false); + } + + private async Task TestWriteFunctionElementHeaderMethodWithAsync(EdmFunction function, string expected) + { + await this.EdmModelCsdlSchemaWriterTestAsync(async (writer) => await writer.WriteFunctionElementHeaderAsync(function).ConfigureAwait(false), expected).ConfigureAwait(false); + } + #endregion + + #region OperationImport tests. + [Fact] + public async Task ValidateEntitySetAtttributeCorrectlyWritesOutEntitySet_Async() + { + EdmActionImport actionImport = new EdmActionImport(defaultContainer, "Checkout", defaultCheckoutAction, new EdmPathExpression("Customers")); + + await this.EdmModelCsdlSchemaWriterTestAsync( + async (writer) => await writer.WriteActionImportElementHeaderAsync(actionImport).ConfigureAwait(false), + @" await writer.WriteFunctionImportElementHeaderAsync(functionImport).ConfigureAwait(false), + @" await csdlSchemaWriter.WriteActionImportElementHeaderAsync(actionImport).ConfigureAwait(false); + + var exception = await Assert.ThrowsAsync(errorTest).ConfigureAwait(false); + Assert.Equal(Strings.EdmModel_Validator_Semantic_OperationImportEntitySetExpressionIsInvalid(actionImport.Name), exception.Message); + } + #endregion + + #region ActionImport tests. + [Fact] + public async Task ValidateCorrectActionImportNameAndActionAttributeValueWrittenCorrectly_Async() + { + EdmActionImport actionImport = new EdmActionImport(defaultContainer, "Checkout", defaultCheckoutAction, null); + + await this.EdmModelCsdlSchemaWriterTestAsync( + async (writer) => await writer.WriteActionImportElementHeaderAsync(actionImport).ConfigureAwait(false), + @" await writer.WriteFunctionImportElementHeaderAsync(functionImport).ConfigureAwait(false), + @" await writer.WriteFunctionImportElementHeaderAsync(functionImport).ConfigureAwait(false), + @" await writer.WriteComplexTypeElementHeaderAsync(complexType).ConfigureAwait(false), expected).ConfigureAwait(false); + } + + internal async Task EdmModelCsdlSchemaWriterTestAsync(Func testAction, string expectedPayload) + { + XmlWriter xmlWriter; + MemoryStream memoryStream; + EdmModelCsdlSchemaWriter schemaWriter = CreateEdmModelCsdlSchemaWriterForAsync(out xmlWriter, out memoryStream); + + await testAction(schemaWriter).ConfigureAwait(false); + + await xmlWriter.FlushAsync().ConfigureAwait(false); + + memoryStream.Seek(0, SeekOrigin.Begin); + StreamReader reader = new StreamReader(memoryStream); + + // Removing xml header to make the baseline's more compact and focused on the test at hand. + string result = reader.ReadToEnd().Replace(@"", string.Empty); + Assert.Equal(expectedPayload, result); + } + + private static EdmModelCsdlSchemaWriter CreateEdmModelCsdlSchemaWriterForErrorTestForAsync() + { + XmlWriter writer = null; + MemoryStream memoryStream = null; + + return CreateEdmModelCsdlSchemaWriterForAsync(out writer, out memoryStream); + } + + private static EdmModelCsdlSchemaWriter CreateEdmModelCsdlSchemaWriterForAsync(out XmlWriter xmlWriter, out MemoryStream memoryStream) + { + XmlWriterSettings settings = new XmlWriterSettings + { + Async = true, + }; + + memoryStream = new MemoryStream(); + IEdmModel model = new EdmModel(); + model.SetEdmxVersion(new Version(4, 0)); + var namespaceAliasMappings = model.GetNamespaceAliases(); + Version edmxVersion = model.GetEdmxVersion(); + xmlWriter = XmlWriter.Create(memoryStream, settings); + return new EdmModelCsdlSchemaXmlWriter(model, xmlWriter, edmxVersion); + } + #endregion + } +} diff --git a/test/FunctionalTests/Microsoft.OData.Edm.Tests/Csdl/Serialization/EdmModelCsdlSchemaWriterTests.cs b/test/FunctionalTests/Microsoft.OData.Edm.Tests/Csdl/Serialization/EdmModelCsdlSchemaWriterTests.cs index 3c0c7f6d92..ed543c5c21 100644 --- a/test/FunctionalTests/Microsoft.OData.Edm.Tests/Csdl/Serialization/EdmModelCsdlSchemaWriterTests.cs +++ b/test/FunctionalTests/Microsoft.OData.Edm.Tests/Csdl/Serialization/EdmModelCsdlSchemaWriterTests.cs @@ -17,7 +17,7 @@ namespace Microsoft.OData.Edm.Tests.Csdl.Serialization /// ///Tests EdmModelCsdlSchemaWriter functionalities /// - public class EdmModelCsdlSchemaWriterTests + public partial class EdmModelCsdlSchemaWriterTests { private static readonly EdmEntityContainer defaultContainer = new EdmEntityContainer("Default.NameSpace", "Container"); private static readonly EdmAction defaultCheckoutAction = new EdmAction("Default.NameSpace2", "CheckOut", null); 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 new file mode 100644 index 0000000000..ccd4894c48 --- /dev/null +++ b/test/FunctionalTests/Microsoft.OData.Edm.Tests/Csdl/Serialization/EdmModelCsdlSerializationVisitorTests.Async.cs @@ -0,0 +1,828 @@ +//--------------------------------------------------------------------- +// +// Copyright (C) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information. +// +//--------------------------------------------------------------------- + +using System; +using System.IO; +using System.Threading.Tasks; +#if NETCOREAPP3_1 +using System.Text.Json; +using System.Text.Encodings.Web; +#endif +using System.Xml; +using Microsoft.OData.Edm.Csdl; +using Microsoft.OData.Edm.Csdl.CsdlSemantics; +using Microsoft.OData.Edm.Csdl.Parsing.Ast; +using Microsoft.OData.Edm.Csdl.Serialization; +using Microsoft.OData.Edm.Vocabularies; +using Xunit; + +namespace Microsoft.OData.Edm.Tests.Csdl.Serialization +{ + public partial class EdmModelCsdlSerializationVisitorTests + { + #region Complex Type + [Fact] + public async Task VerifyComplexTypeWrittenCorrectly_Async() + { + // Arrange + EdmComplexType complexType = new EdmComplexType("NS", "Dimensions"); + complexType.AddStructuralProperty("Height", EdmCoreModel.Instance.GetDecimal(6, 2, true)); + complexType.AddStructuralProperty("Weight", EdmCoreModel.Instance.GetDecimal(6, null, true)); + complexType.AddStructuralProperty("Length", EdmCoreModel.Instance.GetDecimal(null, null, false)); + complexType.AddStructuralProperty("Breadth", EdmCoreModel.Instance.GetDecimal(6, 0, true)); + + // Act & Assert for XML + await VisitAndVerifyXmlAsync(v => v.VisitSchemaType(complexType), + @" + + + + +").ConfigureAwait(false); + + // Act & Assert for JSON + await VisitAndVerifyJsonAsync(v => v.VisitSchemaType(complexType), @"{ + ""Dimensions"": { + ""$Kind"": ""ComplexType"", + ""Height"": { + ""$Type"": ""Edm.Decimal"", + ""$Nullable"": true, + ""$Precision"": 6, + ""$Scale"": 2 + }, + ""Weight"": { + ""$Type"": ""Edm.Decimal"", + ""$Nullable"": true, + ""$Precision"": 6 + }, + ""Length"": { + ""$Type"": ""Edm.Decimal"" + }, + ""Breadth"": { + ""$Type"": ""Edm.Decimal"", + ""$Nullable"": true, + ""$Precision"": 6, + ""$Scale"": 0 + } + } +}").ConfigureAwait(false); + } + #endregion + + #region Action + [Fact] + public async Task VerifySchemaWithActionsWrittenCorrectly_Async() + { + // Arrange + EdmSchema schema = new EdmSchema("NS"); + EdmAction action = new EdmAction("NS", "DoStuff", EdmCoreModel.Instance.GetString(true), true, null); + action.AddParameter("param1", EdmCoreModel.Instance.GetString(true)); + schema.AddSchemaElement(action); + + action = new EdmAction("NS", "DoStuff", EdmCoreModel.Instance.GetString(true), true, null); + action.AddParameter("param1", EdmCoreModel.Instance.GetInt32(true)); + schema.AddSchemaElement(action); + + // Act & Assert for XML + await VisitAndVerifyXmlAsync(async (v) => await v.VisitEdmSchemaAsync(schema, null).ConfigureAwait(false), + @" + + + + + + + + +").ConfigureAwait(false); + + // Act & Assert for JSON + await VisitAndVerifyJsonAsync(async (v) => await v.VisitEdmSchemaAsync(schema, null).ConfigureAwait(false), @"{ + ""NS"": { + ""DoStuff"": [ + { + ""$Kind"": ""Action"", + ""$IsBound"": true, + ""$Parameter"": [ + { + ""$Name"": ""param1"", + ""$Nullable"": true + } + ], + ""$ReturnType"": { + ""$Nullable"": true + } + }, + { + ""$Kind"": ""Action"", + ""$IsBound"": true, + ""$Parameter"": [ + { + ""$Name"": ""param1"", + ""$Type"": ""Edm.Int32"", + ""$Nullable"": true + } + ], + ""$ReturnType"": { + ""$Nullable"": true + } + } + ] + } +}").ConfigureAwait(false); + } + #endregion + + #region EntitySet + [Fact] + public async Task VerifyEntitySetWrittenCorrectly_Async() + { + // Arrange + IEdmEntityType entityType = new EdmEntityType("NS", "EntityType"); + IEdmEntityContainer container = new EdmEntityContainer("NS", "Container"); + EdmEntitySet entitySet = new EdmEntitySet(container, "Set", entityType); + + // Act & Assert for XML + await VisitAndVerifyXmlAsync(async (v) => await v.VisitEntityContainerElementsAsync(new[] { entitySet }).ConfigureAwait(false), + @"").ConfigureAwait(false); + + // Act & Assert for JSON + await VisitAndVerifyJsonAsync(async (v) => await v.VisitEntityContainerElementsAsync(new[] { entitySet }).ConfigureAwait(false), + @"{ + ""Set"": { + ""$Collection"": true, + ""$Type"": ""NS.EntityType"" + } +}").ConfigureAwait(false); + + // Act & Assert for non-indent JSON + await VisitAndVerifyJsonAsync(async (v) => await v.VisitEntityContainerElementsAsync(new[] { entitySet }).ConfigureAwait(false), + @"{""Set"":{""$Collection"":true,""$Type"":""NS.EntityType""}}", false).ConfigureAwait(false); + } + + [Fact] + public async Task VerifyEntitySetWithAllInformationsWrittenCorrectly_Async() + { + // Arrange + var person = new EdmEntityType("NS", "Person"); + var entityId = person.AddStructuralProperty("UserName", EdmCoreModel.Instance.GetString(false)); + person.AddKeys(entityId); + + var city = new EdmEntityType("NS", "City"); + var cityId = city.AddStructuralProperty("Name", EdmCoreModel.Instance.GetString(false)); + city.AddKeys(cityId); + + var countryOrRegion = new EdmEntityType("NS", "CountryOrRegion"); + var countryId = countryOrRegion.AddStructuralProperty("Name", EdmCoreModel.Instance.GetString(false)); + countryOrRegion.AddKeys(countryId); + + var complex = new EdmComplexType("NS", "Address"); + complex.AddStructuralProperty("Id", EdmCoreModel.Instance.GetInt32(false)); + var navP = complex.AddUnidirectionalNavigation( + new EdmNavigationPropertyInfo() + { + Name = "City", + Target = city, + TargetMultiplicity = EdmMultiplicity.One, + }); + + var derivedComplex = new EdmComplexType("NS", "WorkAddress", complex); + var navP2 = derivedComplex.AddUnidirectionalNavigation( + new EdmNavigationPropertyInfo() + { + Name = "CountryOrRegion", + Target = countryOrRegion, + TargetMultiplicity = EdmMultiplicity.One, + }); + + person.AddStructuralProperty("HomeAddress", new EdmComplexTypeReference(complex, false)); + person.AddStructuralProperty("WorkAddress", new EdmComplexTypeReference(complex, false)); + person.AddStructuralProperty("Addresses", new EdmCollectionTypeReference(new EdmCollectionType(new EdmComplexTypeReference(complex, false)))); + + model.AddElement(person); + model.AddElement(city); + model.AddElement(countryOrRegion); + model.AddElement(complex); + model.AddElement(derivedComplex); + + var entityContainer = new EdmEntityContainer("NS", "Container"); + model.AddElement(entityContainer); + EdmEntitySet people = new EdmEntitySet(entityContainer, "People", person); + EdmEntitySet cities = new EdmEntitySet(entityContainer, "City", city); + EdmEntitySet countriesOrRegions = new EdmEntitySet(entityContainer, "CountryOrRegion", countryOrRegion); + people.AddNavigationTarget(navP, cities, new EdmPathExpression("HomeAddress/City")); + people.AddNavigationTarget(navP, cities, new EdmPathExpression("Addresses/City")); + people.AddNavigationTarget(navP2, countriesOrRegions, new EdmPathExpression("WorkAddress/NS.WorkAddress/CountryOrRegion")); + entityContainer.AddElement(people); + entityContainer.AddElement(cities); + entityContainer.AddElement(countriesOrRegions); + + EdmTerm term = new EdmTerm("UI", "ReadOnly", EdmPrimitiveTypeKind.Boolean); + IEdmVocabularyAnnotation annotation = new EdmVocabularyAnnotation(people, term, new EdmBooleanConstant(true)); + annotation.SetSerializationLocation(model, EdmVocabularyAnnotationSerializationLocation.Inline); + model.SetVocabularyAnnotation(annotation); + + // Act & Assert for XML + await VisitAndVerifyXmlAsync(async (v) => await v.VisitEntityContainerElementsAsync(new[] { people }).ConfigureAwait(false), + @" + + + + +").ConfigureAwait(false); + + // Act & Assert for JSON + await VisitAndVerifyJsonAsync(async (v) => await v.VisitEntityContainerElementsAsync(new[] { people }).ConfigureAwait(false), + @"{ + ""People"": { + ""$Collection"": true, + ""$Type"": ""NS.Person"", + ""$NavigationPropertyBinding"": { + ""Addresses/City"": ""City"", + ""HomeAddress/City"": ""City"", + ""WorkAddress/NS.WorkAddress/CountryOrRegion"": ""CountryOrRegion"" + }, + ""@UI.ReadOnly"": true + } +}").ConfigureAwait(false); + } + #endregion + + #region Singleton + [Fact] + public async Task VerifySingletonWrittenCorrectly_Async() + { + IEdmEntityType entityType = new EdmEntityType("NS", "EntityType"); + IEdmEntityContainer container = new EdmEntityContainer("NS", "Container"); + EdmSingleton singleton = new EdmSingleton(container, "Me", entityType); + + await VisitAndVerifyXmlAsync(async (v) => await v.VisitEntityContainerElementsAsync(new[] { singleton }).ConfigureAwait(false), + @"").ConfigureAwait(false); + + await VisitAndVerifyJsonAsync(async (v) => await v.VisitEntityContainerElementsAsync(new[] { singleton }).ConfigureAwait(false), @"{ + ""Me"": { + ""$Type"": ""NS.EntityType"" + } +}").ConfigureAwait(false); + + await VisitAndVerifyJsonAsync(async (v) => await v.VisitEntityContainerElementsAsync(new[] { singleton }).ConfigureAwait(false), + @"{""Me"":{""$Type"":""NS.EntityType""}}", false).ConfigureAwait(false); + } + + [Fact] + public async Task VerifySingletonWithAnnotationWrittenCorrectly_Async() + { + var entityType = new EdmEntityType("NS", "EntityType"); + var container = new EdmEntityContainer("NS", "Container"); + var singleton = new EdmSingleton(container, "Me", entityType); + container.AddElement(singleton); + this.model.AddElement(entityType); + this.model.AddElement(container); + + var enumType = new EdmEnumType("NS", "Permission", true); + var read = enumType.AddMember("Read", new EdmEnumMemberValue(0)); + var write = enumType.AddMember("Write", new EdmEnumMemberValue(1)); + var term = new EdmTerm("NS", "MyTerm", new EdmEnumTypeReference(enumType, true)); + this.model.AddElement(term); + + var enumMemberValue = new EdmEnumMemberExpression(read, write); + var annotation = new EdmVocabularyAnnotation(singleton, term, enumMemberValue); + annotation.SetSerializationLocation(model, EdmVocabularyAnnotationSerializationLocation.Inline); + this.model.SetVocabularyAnnotation(annotation); + + await VisitAndVerifyXmlAsync(async (v) => await v.VisitEntityContainerElementsAsync(new[] { singleton }).ConfigureAwait(false), + @" + + NS.Permission/Read NS.Permission/Write + +").ConfigureAwait(false); + + await VisitAndVerifyJsonAsync(async (v) => await v.VisitEntityContainerElementsAsync(new[] { singleton }).ConfigureAwait(false), @"{ + ""Me"": { + ""$Type"": ""NS.EntityType"", + ""@NS.MyTerm"": ""Read,Write"" + } +}").ConfigureAwait(false); + } + + #endregion + + #region Action Import + [Fact] + public async Task VerifyActionImportWrittenCorrectly_Async() + { + // Arrange + var actionImport = new EdmActionImport(defaultContainer, "Checkout", defaultCheckoutAction, null); + + // Act & Assert for XML + await VisitAndVerifyXmlAsync(async (v) => await v.VisitEntityContainerElementsAsync(new IEdmEntityContainerElement[] { actionImport }).ConfigureAwait(false), + @"").ConfigureAwait(false); + + // Act & Assert for JSON + await VisitAndVerifyJsonAsync(async (v) => await v.VisitEntityContainerElementsAsync(new IEdmEntityContainerElement[] { actionImport }).ConfigureAwait(false), + @"{ + ""Checkout"": { + ""$Kind"": ""ActionImport"", + ""$Action"": ""Default.NameSpace2.CheckOut"" + } +}").ConfigureAwait(false); + + // Act & Assert for non-indent JSON + await VisitAndVerifyJsonAsync(async (v) => await v.VisitEntityContainerElementsAsync(new IEdmEntityContainerElement[] { actionImport }).ConfigureAwait(false), + @"{""Checkout"":{""$Kind"":""ActionImport"",""$Action"":""Default.NameSpace2.CheckOut""}}", false).ConfigureAwait(false); + } + + [Fact] + public async Task VerifyTwoIdenticalNamedActionImportsWithNoEntitySetPropertyOnlyWrittenOnce_Async() + { + // Arrange + var actionImport = new EdmActionImport(defaultContainer, "Checkout", defaultCheckoutAction, null); + 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), + @"").ConfigureAwait(false); + + // Act & Assert for JSON + await VisitAndVerifyJsonAsync(async (v) => await v.VisitEntityContainerElementsAsync(new IEdmEntityContainerElement[] { actionImport, actionImport2 }).ConfigureAwait(false), + @"{ + ""Checkout"": { + ""$Kind"": ""ActionImport"", + ""$Action"": ""Default.NameSpace2.CheckOut"" + } +}").ConfigureAwait(false); + } + + [Fact] + public async Task VerifyTwoIdenticalNamedActionImportsWithSameEntitySetOnlyWrittenOnce_Async() + { + // Arrange + var actionImport = new EdmActionImport(defaultContainer, "Checkout", defaultCheckoutAction, new EdmPathExpression("Set")); + 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), + @"").ConfigureAwait(false); + + // Act & Assert for JSON + await VisitAndVerifyJsonAsync(async (v) => await v.VisitEntityContainerElementsAsync(new IEdmEntityContainerElement[] { actionImport, actionImport2 }).ConfigureAwait(false), + @"{ + ""Checkout"": { + ""$Kind"": ""ActionImport"", + ""$Action"": ""Default.NameSpace2.CheckOut"", + ""$EntitySet"": ""Set"" + } +}").ConfigureAwait(false); + } + + [Fact] + public async Task VerifyTwoIdenticalNamedActionImportsWithSameEdmPathOnlyWrittenOnce_Async() + { + // Arrange + var actionImport = new EdmActionImport(defaultContainer, "Checkout", defaultCheckoutAction, new EdmPathExpression("path1", "path2")); + 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), + @"").ConfigureAwait(false); + + // Act & Assert for JSON + await VisitAndVerifyJsonAsync(async (v) => await v.VisitEntityContainerElementsAsync(new IEdmEntityContainerElement[] { actionImport, actionImport2 }).ConfigureAwait(false), + @"{ + ""Checkout"": { + ""$Kind"": ""ActionImport"", + ""$Action"": ""Default.NameSpace2.CheckOut"", + ""$EntitySet"": ""path1/path2"" + } +}").ConfigureAwait(false); + } + + /// + /// From OData Spec, it should be invalid to have overload action import. + /// Need to check with OData TC. + /// + [Fact] + public async Task VerifyIdenticalNamedActionImportsWithDifferentEntitySetPropertiesAreWritten_Async() + { + // Arrange + var actionImportOnSet = new EdmActionImport(defaultContainer, "Checkout", defaultCheckoutAction, new EdmPathExpression("Set")); + var actionImportOnSet2 = new EdmActionImport(defaultContainer, "Checkout", defaultCheckoutAction, new EdmPathExpression("Set2")); + var actionImportWithNoEntitySet = new EdmActionImport(defaultContainer, "Checkout", defaultCheckoutAction, null); + 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), + @" + + +").ConfigureAwait(false); + + // Act & Assert for JSON + await VisitAndVerifyJsonAsync(async (v) => await v.VisitEntityContainerElementsAsync(new IEdmEntityContainerElement[] { actionImportOnSet, actionImportOnSet2, actionImportWithNoEntitySet, actionImportWithUniqueEdmPath }).ConfigureAwait(false), + @"{ + ""Checkout"": { + ""$Kind"": ""ActionImport"", + ""$Action"": ""Default.NameSpace2.CheckOut"", + ""$EntitySet"": ""Set"" + }, + ""Checkout"": { + ""$Kind"": ""ActionImport"", + ""$Action"": ""Default.NameSpace2.CheckOut"", + ""$EntitySet"": ""Set2"" + }, + ""Checkout"": { + ""$Kind"": ""ActionImport"", + ""$Action"": ""Default.NameSpace2.CheckOut"" + }, + ""Checkout"": { + ""$Kind"": ""ActionImport"", + ""$Action"": ""Default.NameSpace2.CheckOut"", + ""$EntitySet"": ""path1/path2"" + } +}").ConfigureAwait(false); + } + #endregion + + #region Function Import + [Fact] + public async Task VerifyFunctionImportWrittenCorrectly_Async() + { + // Arrange + var functionImport = new EdmFunctionImport(defaultContainer, "GetStuff", defaultGetStuffFunction, null, true); + + // Act & Assert for XML + await VisitAndVerifyXmlAsync(async (v) => await v.VisitEntityContainerElementsAsync(new IEdmEntityContainerElement[] { functionImport }).ConfigureAwait(false), + @"").ConfigureAwait(false); + + // Act & Assert for JSON + await VisitAndVerifyJsonAsync(async (v) => await v.VisitEntityContainerElementsAsync(new IEdmEntityContainerElement[] { functionImport }).ConfigureAwait(false), + @"{ + ""GetStuff"": { + ""$Kind"": ""FunctionImport"", + ""$Function"": ""Default.NameSpace2.GetStuff"", + ""$IncludeInServiceDocument"": true + } +}").ConfigureAwait(false); + + // Act & Assert for non-indent JSON + await VisitAndVerifyJsonAsync(async (v) => await v.VisitEntityContainerElementsAsync(new IEdmEntityContainerElement[] { functionImport }).ConfigureAwait(false), + @"{""GetStuff"":{""$Kind"":""FunctionImport"",""$Function"":""Default.NameSpace2.GetStuff"",""$IncludeInServiceDocument"":true}}", false); + } + + [Fact] + public async Task VerifyTwoIdenticalNamedFunctionImportsWithoutEntitySetValueOnlyWrittenOnce_Async() + { + // Arrange + var functionImport = new EdmFunctionImport(defaultContainer, "GetStuff", defaultGetStuffFunction, null, true); + var functionImport2 = new EdmFunctionImport(defaultContainer, "GetStuff", defaultGetStuffFunction, null, true); + + // Act & Assert for XML + await VisitAndVerifyXmlAsync(async (v) => await v.VisitEntityContainerElementsAsync(new IEdmEntityContainerElement[] { functionImport, functionImport2 }).ConfigureAwait(false), + @"").ConfigureAwait(false); + + // Act & Assert for JSON + await VisitAndVerifyJsonAsync(async (v) => await v.VisitEntityContainerElementsAsync(new IEdmEntityContainerElement[] { functionImport, functionImport2 }).ConfigureAwait(false), + @"{ + ""GetStuff"": { + ""$Kind"": ""FunctionImport"", + ""$Function"": ""Default.NameSpace2.GetStuff"", + ""$IncludeInServiceDocument"": true + } +}").ConfigureAwait(false); + } + + [Fact] + public async Task VerifyTwoIdenticalNamedFunctionImportsWithSameEntitySetValueOnlyWrittenOnce_Async() + { + // Arrange + var functionImport = new EdmFunctionImport(defaultContainer, "GetStuff", defaultGetStuffFunction, new EdmPathExpression("Set"), true); + var functionImport2 = new EdmFunctionImport(defaultContainer, "GetStuff", defaultGetStuffFunction, new EdmPathExpression("Set"), true); + + // Act & Assert for XML + await VisitAndVerifyXmlAsync(async (v) => await v.VisitEntityContainerElementsAsync(new IEdmEntityContainerElement[] { functionImport, functionImport2 }).ConfigureAwait(false), + @"").ConfigureAwait(false); + + // Act & Assert for JSON + await VisitAndVerifyJsonAsync(async (v) => await v.VisitEntityContainerElementsAsync(new IEdmEntityContainerElement[] { functionImport, functionImport2 }).ConfigureAwait(false), + @"{ + ""GetStuff"": { + ""$Kind"": ""FunctionImport"", + ""$Function"": ""Default.NameSpace2.GetStuff"", + ""$EntitySet"": ""Set"", + ""$IncludeInServiceDocument"": true + } +}").ConfigureAwait(false); + } + + [Fact] + public async Task VerifyTwoIdenticalNamedFunctionImportsWithSameEntitySetPathValueOnlyWrittenOnce_Async() + { + // Arrange + var functionImport = new EdmFunctionImport(defaultContainer, "GetStuff", defaultGetStuffFunction, new EdmPathExpression("path1", "path2"), true); + var functionImport2 = new EdmFunctionImport(defaultContainer, "GetStuff", defaultGetStuffFunction, new EdmPathExpression("path1", "path2"), true); + + // Act & Assert for XML + await VisitAndVerifyXmlAsync(async (v) => await v.VisitEntityContainerElementsAsync(new IEdmEntityContainerElement[] { functionImport, functionImport2 }).ConfigureAwait(false), + @"").ConfigureAwait(false); + + // Act & Assert for JSON + await VisitAndVerifyJsonAsync(async (v) => await v.VisitEntityContainerElementsAsync(new IEdmEntityContainerElement[] { functionImport, functionImport2 }).ConfigureAwait(false), + @"{ + ""GetStuff"": { + ""$Kind"": ""FunctionImport"", + ""$Function"": ""Default.NameSpace2.GetStuff"", + ""$EntitySet"": ""path1/path2"", + ""$IncludeInServiceDocument"": true + } +}").ConfigureAwait(false); + } + + [Fact] + public async Task VerifyIdenticalNamedFunctionImportsWithDifferentEntitySetPropertiesAreWritten_Async() + { + // Arrange + var functionImportOnSet = new EdmFunctionImport(defaultContainer, "Checkout", defaultGetStuffFunction, new EdmPathExpression("Set"), false); + var functionImportOnSet2 = new EdmFunctionImport(defaultContainer, "Checkout", defaultGetStuffFunction, new EdmPathExpression("Set2"), false); + var functionmportWithNoEntitySet = new EdmFunctionImport(defaultContainer, "Checkout", defaultGetStuffFunction, null, true); + var functionImportWithUniqueEdmPath = new EdmFunctionImport(defaultContainer, "Checkout", defaultGetStuffFunction, new EdmPathExpression("path1", "path2"), false); + + // Act & Assert for XML + await VisitAndVerifyXmlAsync(async (v) => await v.VisitEntityContainerElementsAsync(new IEdmEntityContainerElement[] { functionImportOnSet, functionImportOnSet2, functionmportWithNoEntitySet, functionImportWithUniqueEdmPath }).ConfigureAwait(false), + @" + + +").ConfigureAwait(false); + + // Act & Assert for JSON + await VisitAndVerifyJsonAsync(async (v) => await v.VisitEntityContainerElementsAsync(new IEdmEntityContainerElement[] { functionImportOnSet, functionImportOnSet2, functionmportWithNoEntitySet, functionImportWithUniqueEdmPath }).ConfigureAwait(false), + @"{ + ""Checkout"": { + ""$Kind"": ""FunctionImport"", + ""$Function"": ""Default.NameSpace2.GetStuff"", + ""$EntitySet"": ""Set"" + }, + ""Checkout"": { + ""$Kind"": ""FunctionImport"", + ""$Function"": ""Default.NameSpace2.GetStuff"", + ""$EntitySet"": ""Set2"" + }, + ""Checkout"": { + ""$Kind"": ""FunctionImport"", + ""$Function"": ""Default.NameSpace2.GetStuff"", + ""$IncludeInServiceDocument"": true + }, + ""Checkout"": { + ""$Kind"": ""FunctionImport"", + ""$Function"": ""Default.NameSpace2.GetStuff"", + ""$EntitySet"": ""path1/path2"" + } +}").ConfigureAwait(false); + } + + [Fact] + public async Task VerifySchemaWithFunctionsWrittenCorrectly_Async() + { + // Arrange + EdmSchema schema = new EdmSchema("NS"); + EdmFunction function = new EdmFunction("NS", "GetStuff", EdmCoreModel.Instance.GetString(true), true, null, false); + function.AddParameter("param1", EdmCoreModel.Instance.GetString(true)); + schema.AddSchemaElement(function); + + function = new EdmFunction("NS", "GetStuff", EdmCoreModel.Instance.GetGuid(false), true, null, false); + function.AddParameter("param1", EdmCoreModel.Instance.GetString(true)); + function.AddParameter("param2", EdmCoreModel.Instance.GetInt32(false)); + schema.AddSchemaElement(function); + + // Act & Assert for XML + await VisitAndVerifyXmlAsync(async (v) => await v.VisitEdmSchemaAsync(schema, null).ConfigureAwait(false), + @" + + + + + + + + + +").ConfigureAwait(false); + + // Act & Assert for JSON + await VisitAndVerifyJsonAsync(async (v) => await v.VisitEdmSchemaAsync(schema, null).ConfigureAwait(false), @"{ + ""NS"": { + ""GetStuff"": [ + { + ""$Kind"": ""Function"", + ""$IsBound"": true, + ""$Parameter"": [ + { + ""$Name"": ""param1"", + ""$Nullable"": true + } + ], + ""$ReturnType"": { + ""$Nullable"": true + } + }, + { + ""$Kind"": ""Function"", + ""$IsBound"": true, + ""$Parameter"": [ + { + ""$Name"": ""param1"", + ""$Nullable"": true + }, + { + ""$Name"": ""param2"", + ""$Type"": ""Edm.Int32"" + } + ], + ""$ReturnType"": { + ""$Type"": ""Edm.Guid"" + } + } + ] + } +}").ConfigureAwait(false); + } + #endregion + + #region Out of line annotation + [Fact] + public async Task VerifyOutOfLineAnnotationWrittenCorrectly_Async() + { + // Arrange + EdmSchema schema = new EdmSchema("NS"); + + EdmComplexType complexType = new EdmComplexType("NS", "ComplexType"); + EdmProperty property = complexType.AddStructuralProperty("Name", EdmPrimitiveTypeKind.String); + + EdmTerm term = new EdmTerm("UI", "Thumbnail", EdmPrimitiveTypeKind.Binary); + IEdmVocabularyAnnotation annotation = new EdmVocabularyAnnotation(complexType, term, new EdmBinaryConstant(new byte[] { 0x4f, 0x44, 0x61, 0x74, 0x61 })); + schema.AddVocabularyAnnotation(annotation); + + EdmTerm displayName = new EdmTerm("UI", "DisplayName", EdmPrimitiveTypeKind.Int32); + annotation = new EdmVocabularyAnnotation(complexType, displayName, new EdmIntegerConstant(42)); + schema.AddVocabularyAnnotation(annotation); + + annotation = new EdmVocabularyAnnotation(complexType, displayName, "Tablet", new EdmIntegerConstant(88)); + schema.AddVocabularyAnnotation(annotation); + + annotation = new EdmVocabularyAnnotation(property, displayName, "Tablet", new EdmIntegerConstant(42)); + schema.AddVocabularyAnnotation(annotation); + + // Act & Assert for XML + await VisitAndVerifyXmlAsync(async (v) => await v.VisitEdmSchemaAsync(schema, null).ConfigureAwait(false), + @" + + + + + + + + +").ConfigureAwait(false); + + // Act & Assert for JSON + await VisitAndVerifyJsonAsync(async (v) => await v.VisitEdmSchemaAsync(schema, null).ConfigureAwait(false), @"{ + ""NS"": { + ""$Annotations"": { + ""NS.ComplexType"": { + ""@UI.Thumbnail"": ""T0RhdGE"", + ""@UI.DisplayName"": 42, + ""@UI.DisplayName#Tablet"": 88 + }, + ""NS.ComplexType/Name"": { + ""@UI.DisplayName#Tablet"": 42 + } + } + } +}").ConfigureAwait(false); + } + + [Fact] + public async Task VerifyOutOfLineAnnotationForEnumMemberWrittenCorrectly_Async() + { + // Arrange + EdmSchema schema = new EdmSchema("NS"); + + EdmEnumType enumType = new EdmEnumType("NS", "Color", EdmPrimitiveTypeKind.Int16, true); + var blue = enumType.AddMember("Blue", new EdmEnumMemberValue(0)); + + EdmTerm term = new EdmTerm("UI", "Thumbnail", EdmPrimitiveTypeKind.Binary); + IEdmVocabularyAnnotation annotation = new EdmVocabularyAnnotation(enumType, term, new EdmBinaryConstant(new byte[] { 0x4f, 0x44, 0x61, 0x74, 0x61 })); + schema.AddVocabularyAnnotation(annotation); + + EdmTerm displayName = new EdmTerm("UI", "DisplayName", EdmPrimitiveTypeKind.Int32); + annotation = new EdmVocabularyAnnotation(blue, displayName, new EdmIntegerConstant(42)); + schema.AddVocabularyAnnotation(annotation); + + annotation = new EdmVocabularyAnnotation(blue, displayName, "Tablet", new EdmIntegerConstant(88)); + schema.AddVocabularyAnnotation(annotation); + + // Act & Assert for XML + await VisitAndVerifyXmlAsync(async (v) => await v.VisitEdmSchemaAsync(schema, null).ConfigureAwait(false), + @" + + + + + + + +").ConfigureAwait(false); + + // Act & Assert for JSON + await VisitAndVerifyJsonAsync(async (v) => await v.VisitEdmSchemaAsync(schema, null).ConfigureAwait(false), @"{ + ""NS"": { + ""$Annotations"": { + ""NS.Color"": { + ""@UI.Thumbnail"": ""T0RhdGE"" + }, + ""NS.Color/Blue"": { + ""@UI.DisplayName"": 42, + ""@UI.DisplayName#Tablet"": 88 + } + } + } +}").ConfigureAwait(false); + } + #endregion + + internal async Task VisitAndVerifyXmlAsync(Action testAction, string expected, bool indent = true) + { + XmlWriter xmlWriter; + MemoryStream memStream; + + Version edmxVersion = model.GetEdmxVersion(); + memStream = new MemoryStream(); + xmlWriter = XmlWriter.Create(memStream, new XmlWriterSettings() + { + Indent = indent, + ConformanceLevel = ConformanceLevel.Auto, + Async = true + }); + + var schemaWriter = new EdmModelCsdlSchemaXmlWriter(model, xmlWriter, edmxVersion); + var visitor = new EdmModelCsdlSerializationVisitor(model, schemaWriter); + + testAction(visitor); + await xmlWriter.FlushAsync().ConfigureAwait(false); + + memStream.Seek(0, SeekOrigin.Begin); + StreamReader reader = new StreamReader(memStream); + + // Remove extra xml header text as its not needed. + string result = reader.ReadToEnd().Replace(@"", string.Empty); + Assert.Equal(expected, result); + } + + internal async Task VisitAndVerifyJsonAsync(Action testAction, string expected, bool indent = true, bool wrapper = true) + { +#if NETCOREAPP3_1 + Version edmxVersion = this.model.GetEdmxVersion(); + + using (MemoryStream memStream = new MemoryStream()) + { + JsonWriterOptions options = new JsonWriterOptions + { + Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, + Indented = indent, + SkipValidation = false + }; + + using (Utf8JsonWriter jsonWriter = new Utf8JsonWriter(memStream, options)) + { + var csdlSchemaWriter = new EdmModelCsdlSchemaJsonWriter(this.model, jsonWriter, edmxVersion); + var visitor = new EdmModelCsdlSerializationVisitor(this.model, csdlSchemaWriter); + + // Use {} to wrapper the input. + if (wrapper) + { + jsonWriter.WriteStartObject(); + } + + testAction(visitor); + + if (wrapper) + { + jsonWriter.WriteEndObject(); + } + + await jsonWriter.FlushAsync().ConfigureAwait(false); + } + + memStream.Seek(0, SeekOrigin.Begin); + StreamReader reader = new StreamReader(memStream); + + string result = reader.ReadToEnd(); + Assert.Equal(expected, result); + } +#endif + } + } +} diff --git a/test/FunctionalTests/Microsoft.OData.Edm.Tests/Csdl/Serialization/EdmModelCsdlSerializationVisitorTests.cs b/test/FunctionalTests/Microsoft.OData.Edm.Tests/Csdl/Serialization/EdmModelCsdlSerializationVisitorTests.cs index fcdba5faf3..1ab4378b2e 100644 --- a/test/FunctionalTests/Microsoft.OData.Edm.Tests/Csdl/Serialization/EdmModelCsdlSerializationVisitorTests.cs +++ b/test/FunctionalTests/Microsoft.OData.Edm.Tests/Csdl/Serialization/EdmModelCsdlSerializationVisitorTests.cs @@ -23,7 +23,7 @@ namespace Microsoft.OData.Edm.Tests.Csdl.Serialization /// /// Unit tests of EdmModelCsdlSerializationVisitor. Aiming for whitebox coverage of these methods. /// - public class EdmModelCsdlSerializationVisitorTests + public partial class EdmModelCsdlSerializationVisitorTests { private EdmModel model = new EdmModel(); diff --git a/test/FunctionalTests/Microsoft.OData.Edm.Tests/ScenarioTests/OasisActionsFunctionsRelationshipChangesAcceptanceTests.Async.cs b/test/FunctionalTests/Microsoft.OData.Edm.Tests/ScenarioTests/OasisActionsFunctionsRelationshipChangesAcceptanceTests.Async.cs new file mode 100644 index 0000000000..70ceedf788 --- /dev/null +++ b/test/FunctionalTests/Microsoft.OData.Edm.Tests/ScenarioTests/OasisActionsFunctionsRelationshipChangesAcceptanceTests.Async.cs @@ -0,0 +1,42 @@ +//--------------------------------------------------------------------- +// +// Copyright (C) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information. +// +//--------------------------------------------------------------------- + +using System; +using System.Text; +using System.Threading.Tasks; +using System.Xml; +using System.Xml.Linq; +using System.Collections.Generic; +using Xunit; +using Microsoft.OData.Edm.Csdl; + +namespace Microsoft.OData.Edm.Tests.ScenarioTests +{ + public partial class OasisActionsFunctionsRelationshipChangesAcceptanceTest + { + + [Fact] + public async Task VerifyRepresentativeModelWrittenOutCorrectly_Async() + { + var builder = new StringBuilder(); + using (var writer = XmlWriter.Create(builder, new XmlWriterSettings() { Async = true })) + { + Tuple> result = await CsdlWriter.TryWriteCsdlAsync(this.TestModel.RepresentativeModel, writer, CsdlTarget.OData).ConfigureAwait(false); + bool success = result.Item1; + IEnumerable errors = result.Item2; + Assert.True(success); + Assert.Empty(errors); + await writer.FlushAsync().ConfigureAwait(false); + } + + string actual = builder.ToString(); + var actualXml = XElement.Parse(actual); + var actualNormalized = actualXml.ToString(); + + Assert.Equal(DefaultTestModel.RepresentativeEdmxDocument, actualNormalized); + } + } +} diff --git a/test/FunctionalTests/Microsoft.OData.Edm.Tests/ScenarioTests/OasisActionsFunctionsRelationshipChangesAcceptanceTests.cs b/test/FunctionalTests/Microsoft.OData.Edm.Tests/ScenarioTests/OasisActionsFunctionsRelationshipChangesAcceptanceTests.cs index 3d6cfe894a..4063ddfeb0 100644 --- a/test/FunctionalTests/Microsoft.OData.Edm.Tests/ScenarioTests/OasisActionsFunctionsRelationshipChangesAcceptanceTests.cs +++ b/test/FunctionalTests/Microsoft.OData.Edm.Tests/ScenarioTests/OasisActionsFunctionsRelationshipChangesAcceptanceTests.cs @@ -16,7 +16,7 @@ namespace Microsoft.OData.Edm.Tests.ScenarioTests { - public class OasisActionsFunctionsRelationshipChangesAcceptanceTest + public partial class OasisActionsFunctionsRelationshipChangesAcceptanceTest { private static DefaultTestModel Model; diff --git a/test/FunctionalTests/Microsoft.OData.Edm.Tests/ScenarioTests/OasisRelationshipChangesAcceptanceTests.Async.cs b/test/FunctionalTests/Microsoft.OData.Edm.Tests/ScenarioTests/OasisRelationshipChangesAcceptanceTests.Async.cs new file mode 100644 index 0000000000..4411a8de49 --- /dev/null +++ b/test/FunctionalTests/Microsoft.OData.Edm.Tests/ScenarioTests/OasisRelationshipChangesAcceptanceTests.Async.cs @@ -0,0 +1,41 @@ +//--------------------------------------------------------------------- +// +// Copyright (C) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information. +// +//--------------------------------------------------------------------- + +using System.Collections.Generic; +using System; +using System.Text; +using System.Threading.Tasks; +using System.Xml; +using System.Xml.Linq; +using Xunit; +using Microsoft.OData.Edm.Csdl; + +namespace Microsoft.OData.Edm.Tests.ScenarioTests +{ + public partial class OasisRelationshipChangesAcceptanceTests + { + [Fact] + public async Task WriterShouldContinueToWork_Async() + { + var builder = new StringBuilder(); + using (var writer = XmlWriter.Create(builder, new XmlWriterSettings() { Async = true })) + { + Tuple> result = await CsdlWriter.TryWriteCsdlAsync(this.representativeModel, writer, CsdlTarget.OData).ConfigureAwait(false); + bool success = result.Item1; + IEnumerable errors = result.Item2; + Assert.True(success); + Assert.Empty(errors); + await writer.FlushAsync().ConfigureAwait(false); + } + + string actual = builder.ToString(); + var actualXml = XElement.Parse(actual); + var actualNormalized = actualXml.ToString(); + + Assert.Equal(RepresentativeEdmxDocument, actualNormalized); + } + } +} diff --git a/test/FunctionalTests/Microsoft.OData.Edm.Tests/ScenarioTests/OasisRelationshipChangesAcceptanceTests.cs b/test/FunctionalTests/Microsoft.OData.Edm.Tests/ScenarioTests/OasisRelationshipChangesAcceptanceTests.cs index 9a5109cf0a..054e573eb9 100644 --- a/test/FunctionalTests/Microsoft.OData.Edm.Tests/ScenarioTests/OasisRelationshipChangesAcceptanceTests.cs +++ b/test/FunctionalTests/Microsoft.OData.Edm.Tests/ScenarioTests/OasisRelationshipChangesAcceptanceTests.cs @@ -16,7 +16,7 @@ namespace Microsoft.OData.Edm.Tests.ScenarioTests { - public class OasisRelationshipChangesAcceptanceTests + public partial class OasisRelationshipChangesAcceptanceTests { private const string RepresentativeEdmxDocument = @" diff --git a/test/FunctionalTests/Microsoft.OData.Edm.Tests/Vocabularies/AlternateKeysVocabularyTests.Async.cs b/test/FunctionalTests/Microsoft.OData.Edm.Tests/Vocabularies/AlternateKeysVocabularyTests.Async.cs new file mode 100644 index 0000000000..145c6e13ed --- /dev/null +++ b/test/FunctionalTests/Microsoft.OData.Edm.Tests/Vocabularies/AlternateKeysVocabularyTests.Async.cs @@ -0,0 +1,68 @@ +//--------------------------------------------------------------------- +// +// Copyright (C) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information. +// +//--------------------------------------------------------------------- + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Xml; +using Xunit; +using Microsoft.OData.Edm.Csdl; +using Microsoft.OData.Edm.Validation; +using Microsoft.OData.Edm.Vocabularies.Community.V1; + +namespace Microsoft.OData.Edm.Tests.Vocabularies +{ + public partial class AlternateKeysVocabularyTests + { + [Fact] + public async Task TestAlternateKeysVocabularyModel_Async() + { + const string expectedText = @" + + + + + + + + + + + + + + + + + +"; + + var alternateKeysTerm = model.FindDeclaredTerm("OData.Community.Keys.V1.AlternateKeys"); + Assert.NotNull(alternateKeysTerm); + Assert.Equal(AlternateKeysVocabularyModel.AlternateKeysTerm, alternateKeysTerm); + Assert.Equal("OData.Community.Keys.V1", alternateKeysTerm.Namespace); + Assert.Equal("AlternateKeys", alternateKeysTerm.Name); + + StringWriter sw = new StringWriter(); + IEnumerable errors; + using (var xw = XmlWriter.Create(sw, new XmlWriterSettings { Indent = true, Encoding = Encoding.UTF8, Async = true })) + { + Tuple> result = await model.TryWriteSchemaAsync(xw).ConfigureAwait(false); + Assert.True(result.Item1); + + errors = result.Item2; // Assign the async errors to the variable + } + + string output = sw.ToString(); + + Assert.False(errors.Any(), "No Errors"); + Assert.Equal(expectedText, output); + } + } +} diff --git a/test/FunctionalTests/Microsoft.OData.Edm.Tests/Vocabularies/AlternateKeysVocabularyTests.cs b/test/FunctionalTests/Microsoft.OData.Edm.Tests/Vocabularies/AlternateKeysVocabularyTests.cs index 80198203f5..565713742b 100644 --- a/test/FunctionalTests/Microsoft.OData.Edm.Tests/Vocabularies/AlternateKeysVocabularyTests.cs +++ b/test/FunctionalTests/Microsoft.OData.Edm.Tests/Vocabularies/AlternateKeysVocabularyTests.cs @@ -17,7 +17,7 @@ namespace Microsoft.OData.Edm.Tests.Vocabularies { - public class AlternateKeysVocabularyTests + public partial class AlternateKeysVocabularyTests { private readonly IEdmModel model = AlternateKeysVocabularyModel.Instance; diff --git a/test/FunctionalTests/Microsoft.OData.Edm.Tests/Vocabularies/AuthorizationVocabularyTests.Async.cs b/test/FunctionalTests/Microsoft.OData.Edm.Tests/Vocabularies/AuthorizationVocabularyTests.Async.cs new file mode 100644 index 0000000000..572159bbc7 --- /dev/null +++ b/test/FunctionalTests/Microsoft.OData.Edm.Tests/Vocabularies/AuthorizationVocabularyTests.Async.cs @@ -0,0 +1,156 @@ +//--------------------------------------------------------------------- +// +// Copyright (C) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information. +// +//--------------------------------------------------------------------- + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using System.Xml; +using Microsoft.OData.Edm.Csdl; +using Xunit; + +namespace Microsoft.OData.Edm.Tests.Vocabularies +{ + public partial class AuthorizationVocabularyTests + { + [Fact] + public async Task TestAuthorizationVocabularyModel_Async() + { + const string expectedText = @" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +"; + + var sw = new StringWriter(); + XmlWriterSettings settings = new XmlWriterSettings() { Async = true }; + settings.Indent = true; + settings.Encoding = System.Text.Encoding.UTF8; + + XmlWriter xw = XmlWriter.Create(sw, settings); + Tuple> result = await this._authorizationModel.TryWriteSchemaAsync(xw).ConfigureAwait(false); + await xw.FlushAsync().ConfigureAwait(false); + + IEnumerable errors = result.Item2; + +#if NETCOREAPP1_1 + xw.Dispose(); +#else + xw.Close(); +#endif + string output = sw.ToString(); + + Assert.True(!errors.Any(), "No Errors"); + Assert.Equal(expectedText, output); + } + } +} diff --git a/test/FunctionalTests/Microsoft.OData.Edm.Tests/Vocabularies/AuthorizationVocabularyTests.cs b/test/FunctionalTests/Microsoft.OData.Edm.Tests/Vocabularies/AuthorizationVocabularyTests.cs index 02e9bf006e..9dc2e63166 100644 --- a/test/FunctionalTests/Microsoft.OData.Edm.Tests/Vocabularies/AuthorizationVocabularyTests.cs +++ b/test/FunctionalTests/Microsoft.OData.Edm.Tests/Vocabularies/AuthorizationVocabularyTests.cs @@ -18,7 +18,7 @@ namespace Microsoft.OData.Edm.Tests.Vocabularies /// /// Test authorization vocabulary /// - public class AuthorizationVocabularyTests + public partial class AuthorizationVocabularyTests { private readonly IEdmModel _authorizationModel = AuthorizationVocabularyModel.Instance; diff --git a/test/FunctionalTests/Microsoft.OData.Edm.Tests/Vocabularies/CapabilitiesVocabularyTests.Async.cs b/test/FunctionalTests/Microsoft.OData.Edm.Tests/Vocabularies/CapabilitiesVocabularyTests.Async.cs new file mode 100644 index 0000000000..d7f3cc7a6a --- /dev/null +++ b/test/FunctionalTests/Microsoft.OData.Edm.Tests/Vocabularies/CapabilitiesVocabularyTests.Async.cs @@ -0,0 +1,863 @@ +//--------------------------------------------------------------------- +// +// Copyright (C) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information. +// +//--------------------------------------------------------------------- + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using System.Xml; +using Microsoft.OData.Edm.Csdl; +using Xunit; + +namespace Microsoft.OData.Edm.Tests.Vocabularies +{ + public partial class CapabilitiesVocabularyTests + { + [Fact] + public async Task TestCapabilitiesVocabularyModel_Async() + { + #region Expected Vocabulary Model + const string expectedText = @" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Core.Description + Core.LongDescription + + + + + + + + + + + + + + + + + + + + + + + + + Core.Description + + + + + + + + + + + + + + + + + + + + + + + + + + + Core.Description + + + + + + + + + + + + + + + + + + + + + + + Core.Description + + + + + + + + + + + + + Core.Description + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +"; + #endregion + + StringWriter sw = new StringWriter(); + XmlWriterSettings settings = new XmlWriterSettings() { Async = true }; + settings.Indent = true; + settings.Encoding = System.Text.Encoding.UTF8; + + XmlWriter xw = XmlWriter.Create(sw, settings); + Tuple> result = await this.capVocModel.TryWriteSchemaAsync(xw).ConfigureAwait(false); + IEnumerable errors = result.Item2; + await xw.FlushAsync().ConfigureAwait(false); +#if NETCOREAPP1_1 + xw.Dispose(); +#else + xw.Close(); +#endif + string output = sw.ToString(); + Assert.True(!errors.Any(), "No Errors"); + Assert.Equal(expectedText, output); + } + } +} diff --git a/test/FunctionalTests/Microsoft.OData.Edm.Tests/Vocabularies/CapabilitiesVocabularyTests.cs b/test/FunctionalTests/Microsoft.OData.Edm.Tests/Vocabularies/CapabilitiesVocabularyTests.cs index 4b6cbfef86..28a943b359 100644 --- a/test/FunctionalTests/Microsoft.OData.Edm.Tests/Vocabularies/CapabilitiesVocabularyTests.cs +++ b/test/FunctionalTests/Microsoft.OData.Edm.Tests/Vocabularies/CapabilitiesVocabularyTests.cs @@ -21,7 +21,7 @@ namespace Microsoft.OData.Edm.Tests.Vocabularies /// /// Test capabilities vocabulary /// - public class CapabilitiesVocabularyTests + public partial class CapabilitiesVocabularyTests { private readonly IEdmModel capVocModel = CapabilitiesVocabularyModel.Instance; diff --git a/test/FunctionalTests/Microsoft.OData.Edm.Tests/Vocabularies/CommunityVocabularyTests.Async.cs b/test/FunctionalTests/Microsoft.OData.Edm.Tests/Vocabularies/CommunityVocabularyTests.Async.cs new file mode 100644 index 0000000000..b22074c91e --- /dev/null +++ b/test/FunctionalTests/Microsoft.OData.Edm.Tests/Vocabularies/CommunityVocabularyTests.Async.cs @@ -0,0 +1,49 @@ +//--------------------------------------------------------------------- +// +// Copyright (C) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information. +// +//--------------------------------------------------------------------- + +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Xml; +using System.Text; +using Microsoft.OData.Edm.Csdl; +using Microsoft.OData.Edm.Validation; +using Xunit; +using System.Threading.Tasks; + +namespace Microsoft.OData.Edm.Tests.Vocabularies +{ + public partial class CommunityVocabularyTests + { + + [Fact] + public async Task TestCommunityVocabularyModel_Async() + { + const string expectedUrlEscape = @" + + + + +"; + + var sw = new StringWriter(); + IEnumerable errors; + using (var xw = XmlWriter.Create(sw, new XmlWriterSettings { Indent = true, Encoding = Encoding.UTF8, Async = true })) + { + var result = await model.TryWriteSchemaAsync(xw).ConfigureAwait(false); + bool success = result.Item1; + Assert.True(success); + + errors = result.Item2; + } + + Assert.False(errors.Any(), "No Errors"); + + string output = sw.ToString(); + Assert.True(expectedUrlEscape == output, "expected Community schema not matching"); + } + } +} diff --git a/test/FunctionalTests/Microsoft.OData.Edm.Tests/Vocabularies/CommunityVocabularyTests.cs b/test/FunctionalTests/Microsoft.OData.Edm.Tests/Vocabularies/CommunityVocabularyTests.cs index bce588e686..71f145d50f 100644 --- a/test/FunctionalTests/Microsoft.OData.Edm.Tests/Vocabularies/CommunityVocabularyTests.cs +++ b/test/FunctionalTests/Microsoft.OData.Edm.Tests/Vocabularies/CommunityVocabularyTests.cs @@ -17,7 +17,7 @@ namespace Microsoft.OData.Edm.Tests.Vocabularies { - public class CommunityVocabularyTests + public partial class CommunityVocabularyTests { private readonly IEdmModel model = CommunityVocabularyModel.Instance; diff --git a/test/FunctionalTests/Microsoft.OData.Edm.Tests/Vocabularies/CoreVocabularyTests.Async.cs b/test/FunctionalTests/Microsoft.OData.Edm.Tests/Vocabularies/CoreVocabularyTests.Async.cs new file mode 100644 index 0000000000..60c7bd67d1 --- /dev/null +++ b/test/FunctionalTests/Microsoft.OData.Edm.Tests/Vocabularies/CoreVocabularyTests.Async.cs @@ -0,0 +1,473 @@ +//--------------------------------------------------------------------- +// +// Copyright (C) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information. +// +//--------------------------------------------------------------------- + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using System.Xml; +using Microsoft.OData.Edm.Csdl; +using Xunit; + +namespace Microsoft.OData.Edm.Tests.Vocabularies +{ + public partial class CoreVocabularyTests + { + [Fact] + public async Task TestBaseCoreVocabularyModel_Async() + { + #region Expected Text + const string expectedText = @" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +"; + #endregion + + var s = coreVocModel.FindDeclaredTerm("Org.OData.Core.V1.OptimisticConcurrency"); + Assert.NotNull(s); + Assert.Equal("Org.OData.Core.V1", s.Namespace); + Assert.Equal("OptimisticConcurrency", s.Name); + + var type = s.Type; + Assert.Equal("Collection(Edm.PropertyPath)", type.FullName()); + Assert.Equal(EdmTypeKind.Collection, type.Definition.TypeKind); + + var descriptionTerm = coreVocModel.FindTerm("Org.OData.Core.V1.Description"); + Assert.NotNull(descriptionTerm); + var descriptionType = descriptionTerm.Type.Definition as IEdmPrimitiveType; + Assert.NotNull(descriptionType); + Assert.Equal(EdmPrimitiveTypeKind.String, descriptionType.PrimitiveKind); + + var longDescriptionTerm = coreVocModel.FindTerm("Org.OData.Core.V1.LongDescription"); + Assert.NotNull(longDescriptionTerm); + var longDescriptionType = longDescriptionTerm.Type.Definition as IEdmPrimitiveType; + Assert.NotNull(longDescriptionType); + Assert.Equal(EdmPrimitiveTypeKind.String, longDescriptionType.PrimitiveKind); + + var isLanguageDependentTerm = coreVocModel.FindTerm("Org.OData.Core.V1.IsLanguageDependent"); + Assert.NotNull(isLanguageDependentTerm); + var isLanguageDependentType = isLanguageDependentTerm.Type.Definition as IEdmTypeDefinition; + Assert.NotNull(isLanguageDependentType); + Assert.Equal(EdmPrimitiveTypeKind.Boolean, isLanguageDependentType.UnderlyingType.PrimitiveKind); + + var requiresExplicitBindingTerm = coreVocModel.FindTerm("Org.OData.Core.V1.RequiresExplicitBinding"); + Assert.NotNull(requiresExplicitBindingTerm); + var requiresExplicitBindingType = requiresExplicitBindingTerm.Type.Definition as IEdmTypeDefinition; + Assert.NotNull(requiresExplicitBindingType); + Assert.Equal(EdmPrimitiveTypeKind.Boolean, requiresExplicitBindingType.UnderlyingType.PrimitiveKind); + + var explicitOperationBindingsTerm = coreVocModel.FindTerm("Org.OData.Core.V1.ExplicitOperationBindings"); + Assert.NotNull(explicitOperationBindingsTerm); + var explicitOperationBindingsType = explicitOperationBindingsTerm.Type.Definition; + Assert.NotNull(explicitOperationBindingsType); + Assert.Equal("Collection(Org.OData.Core.V1.QualifiedBoundOperationName)", explicitOperationBindingsType.FullTypeName()); + Assert.Equal(EdmTypeKind.Collection, explicitOperationBindingsType.TypeKind); + + var qualifiedBoundOperationNameType = coreVocModel.FindType("Org.OData.Core.V1.QualifiedBoundOperationName"); + Assert.NotNull(qualifiedBoundOperationNameType); + Assert.Equal(qualifiedBoundOperationNameType, explicitOperationBindingsType.AsElementType()); + + var sw = new StringWriter(); + var settings = new XmlWriterSettings() + { + Indent = true, + Encoding = System.Text.Encoding.UTF8, + Async = true + }; + + XmlWriter xw = XmlWriter.Create(sw, settings); + Tuple> result = await coreVocModel.TryWriteSchemaAsync(xw).ConfigureAwait(false); + IEnumerable errors = result.Item2; + + await xw.FlushAsync().ConfigureAwait(false); +#if NETCOREAPP1_1 + xw.Dispose(); +#else + xw.Close(); +#endif + string output = sw.ToString(); + Assert.False(errors.Any(), "No Errors"); + Assert.Equal(expectedText, output); + } + } +} diff --git a/test/FunctionalTests/Microsoft.OData.Edm.Tests/Vocabularies/CoreVocabularyTests.cs b/test/FunctionalTests/Microsoft.OData.Edm.Tests/Vocabularies/CoreVocabularyTests.cs index 11c095062f..3214040ea2 100644 --- a/test/FunctionalTests/Microsoft.OData.Edm.Tests/Vocabularies/CoreVocabularyTests.cs +++ b/test/FunctionalTests/Microsoft.OData.Edm.Tests/Vocabularies/CoreVocabularyTests.cs @@ -18,7 +18,7 @@ namespace Microsoft.OData.Edm.Tests.Vocabularies /// /// Test core vocabulary /// - public class CoreVocabularyTests + public partial class CoreVocabularyTests { private readonly IEdmModel coreVocModel = CoreVocabularyModel.Instance; diff --git a/test/FunctionalTests/Microsoft.OData.Edm.Tests/Vocabularies/ValidationVocabularyTests.Async.cs b/test/FunctionalTests/Microsoft.OData.Edm.Tests/Vocabularies/ValidationVocabularyTests.Async.cs new file mode 100644 index 0000000000..e9f502e8e8 --- /dev/null +++ b/test/FunctionalTests/Microsoft.OData.Edm.Tests/Vocabularies/ValidationVocabularyTests.Async.cs @@ -0,0 +1,140 @@ +//--------------------------------------------------------------------- +// +// Copyright (C) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information. +// +//--------------------------------------------------------------------- + +using System.Collections.Generic; +using System; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using System.Xml; +using Microsoft.OData.Edm.Csdl; +using Xunit; + +namespace Microsoft.OData.Edm.Tests.Vocabularies +{ + public partial class ValidationVocabularyTests + { + [Fact] + public async Task TestValidationVocabularyModel_Async() + { + #region Expected Text + const string expectedText = @" + + + + + + + + + + + Core.SymbolicName + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Validation.Exclusive + + + + + + + + Validation.Exclusive + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +"; + #endregion + + var sw = new StringWriter(); + var settings = new XmlWriterSettings() + { + Indent = true, + Encoding = System.Text.Encoding.UTF8, + Async = true + }; + + XmlWriter xw = XmlWriter.Create(sw, settings); + Tuple> result = await this._validationModel.TryWriteSchemaAsync(xw).ConfigureAwait(false); + IEnumerable errors = result.Item2; + await xw.FlushAsync().ConfigureAwait(false); +#if NETCOREAPP1_1 + xw.Dispose(); +#else + xw.Close(); +#endif + string output = sw.ToString(); + + Assert.True(!errors.Any(), "No Errors"); + Assert.Equal(expectedText, output); + } + } +} diff --git a/test/FunctionalTests/Microsoft.OData.Edm.Tests/Vocabularies/ValidationVocabularyTests.cs b/test/FunctionalTests/Microsoft.OData.Edm.Tests/Vocabularies/ValidationVocabularyTests.cs index d5aabbd830..f95a2eabdc 100644 --- a/test/FunctionalTests/Microsoft.OData.Edm.Tests/Vocabularies/ValidationVocabularyTests.cs +++ b/test/FunctionalTests/Microsoft.OData.Edm.Tests/Vocabularies/ValidationVocabularyTests.cs @@ -20,7 +20,7 @@ namespace Microsoft.OData.Edm.Tests.Vocabularies /// /// Test validation vocabulary /// - public class ValidationVocabularyTests + public partial class ValidationVocabularyTests { private readonly IEdmModel _validationModel = ValidationVocabularyModel.Instance; @@ -140,7 +140,7 @@ public void TestValidationVocabularyModel() } [Fact] - public void TestOpenTypePropertyConstraint() + public void TestOpenTypePropertyConstraint() { var modelCsdl = @" diff --git a/test/PublicApiTests/BaseLine/Microsoft.OData.PublicApi.net45.bsl b/test/PublicApiTests/BaseLine/Microsoft.OData.PublicApi.net45.bsl index fd85dd37bc..5d4a73d22e 100644 --- a/test/PublicApiTests/BaseLine/Microsoft.OData.PublicApi.net45.bsl +++ b/test/PublicApiTests/BaseLine/Microsoft.OData.PublicApi.net45.bsl @@ -2995,6 +2995,16 @@ public sealed class Microsoft.OData.Edm.Csdl.SchemaWriter { ExtensionAttribute(), ] public static bool TryWriteSchema (Microsoft.OData.Edm.IEdmModel model, System.Xml.XmlWriter writer, out System.Collections.Generic.IEnumerable`1[[Microsoft.OData.Edm.Validation.EdmError]]& errors) + + [ + ExtensionAttribute(), + ] + public static System.Threading.Tasks.Task`1[[System.Tuple`2[[System.Boolean],[System.Collections.Generic.IEnumerable`1[[Microsoft.OData.Edm.Validation.EdmError]]]]]] TryWriteSchemaAsync (Microsoft.OData.Edm.IEdmModel model, System.Func`2[[System.String],[System.Xml.XmlWriter]] writerProvider) + + [ + ExtensionAttribute(), + ] + public static System.Threading.Tasks.Task`1[[System.Tuple`2[[System.Boolean],[System.Collections.Generic.IEnumerable`1[[Microsoft.OData.Edm.Validation.EdmError]]]]]] TryWriteSchemaAsync (Microsoft.OData.Edm.IEdmModel model, System.Xml.XmlWriter writer) } [ @@ -3100,7 +3110,13 @@ public class Microsoft.OData.Edm.Csdl.CsdlWriter { protected static string GetVersionString (System.Version version) public static bool TryWriteCsdl (Microsoft.OData.Edm.IEdmModel model, System.Xml.XmlWriter writer, Microsoft.OData.Edm.Csdl.CsdlTarget target, out System.Collections.Generic.IEnumerable`1[[Microsoft.OData.Edm.Validation.EdmError]]& errors) + [ + AsyncStateMachineAttribute(), + ] + public static System.Threading.Tasks.Task`1[[System.Tuple`2[[System.Boolean],[System.Collections.Generic.IEnumerable`1[[Microsoft.OData.Edm.Validation.EdmError]]]]]] TryWriteCsdlAsync (Microsoft.OData.Edm.IEdmModel model, System.Xml.XmlWriter writer, Microsoft.OData.Edm.Csdl.CsdlTarget target) + protected virtual void WriteCsdl () + protected virtual System.Threading.Tasks.Task WriteCsdlAsync () } [ diff --git a/test/PublicApiTests/BaseLine/Microsoft.OData.PublicApi.netstandard1.1.bsl b/test/PublicApiTests/BaseLine/Microsoft.OData.PublicApi.netstandard1.1.bsl index 79f11cb3b8..618408e19a 100644 --- a/test/PublicApiTests/BaseLine/Microsoft.OData.PublicApi.netstandard1.1.bsl +++ b/test/PublicApiTests/BaseLine/Microsoft.OData.PublicApi.netstandard1.1.bsl @@ -2995,6 +2995,16 @@ public sealed class Microsoft.OData.Edm.Csdl.SchemaWriter { ExtensionAttribute(), ] public static bool TryWriteSchema (Microsoft.OData.Edm.IEdmModel model, System.Xml.XmlWriter writer, out System.Collections.Generic.IEnumerable`1[[Microsoft.OData.Edm.Validation.EdmError]]& errors) + + [ + ExtensionAttribute(), + ] + public static System.Threading.Tasks.Task`1[[System.Tuple`2[[System.Boolean],[System.Collections.Generic.IEnumerable`1[[Microsoft.OData.Edm.Validation.EdmError]]]]]] TryWriteSchemaAsync (Microsoft.OData.Edm.IEdmModel model, System.Func`2[[System.String],[System.Xml.XmlWriter]] writerProvider) + + [ + ExtensionAttribute(), + ] + public static System.Threading.Tasks.Task`1[[System.Tuple`2[[System.Boolean],[System.Collections.Generic.IEnumerable`1[[Microsoft.OData.Edm.Validation.EdmError]]]]]] TryWriteSchemaAsync (Microsoft.OData.Edm.IEdmModel model, System.Xml.XmlWriter writer) } [ @@ -3100,7 +3110,13 @@ public class Microsoft.OData.Edm.Csdl.CsdlWriter { protected static string GetVersionString (System.Version version) public static bool TryWriteCsdl (Microsoft.OData.Edm.IEdmModel model, System.Xml.XmlWriter writer, Microsoft.OData.Edm.Csdl.CsdlTarget target, out System.Collections.Generic.IEnumerable`1[[Microsoft.OData.Edm.Validation.EdmError]]& errors) + [ + AsyncStateMachineAttribute(), + ] + public static System.Threading.Tasks.Task`1[[System.Tuple`2[[System.Boolean],[System.Collections.Generic.IEnumerable`1[[Microsoft.OData.Edm.Validation.EdmError]]]]]] TryWriteCsdlAsync (Microsoft.OData.Edm.IEdmModel model, System.Xml.XmlWriter writer, Microsoft.OData.Edm.Csdl.CsdlTarget target) + protected virtual void WriteCsdl () + protected virtual System.Threading.Tasks.Task WriteCsdlAsync () } [ diff --git a/test/PublicApiTests/BaseLine/Microsoft.OData.PublicApi.netstandard2.0.bsl b/test/PublicApiTests/BaseLine/Microsoft.OData.PublicApi.netstandard2.0.bsl index fd85dd37bc..5d4a73d22e 100644 --- a/test/PublicApiTests/BaseLine/Microsoft.OData.PublicApi.netstandard2.0.bsl +++ b/test/PublicApiTests/BaseLine/Microsoft.OData.PublicApi.netstandard2.0.bsl @@ -2995,6 +2995,16 @@ public sealed class Microsoft.OData.Edm.Csdl.SchemaWriter { ExtensionAttribute(), ] public static bool TryWriteSchema (Microsoft.OData.Edm.IEdmModel model, System.Xml.XmlWriter writer, out System.Collections.Generic.IEnumerable`1[[Microsoft.OData.Edm.Validation.EdmError]]& errors) + + [ + ExtensionAttribute(), + ] + public static System.Threading.Tasks.Task`1[[System.Tuple`2[[System.Boolean],[System.Collections.Generic.IEnumerable`1[[Microsoft.OData.Edm.Validation.EdmError]]]]]] TryWriteSchemaAsync (Microsoft.OData.Edm.IEdmModel model, System.Func`2[[System.String],[System.Xml.XmlWriter]] writerProvider) + + [ + ExtensionAttribute(), + ] + public static System.Threading.Tasks.Task`1[[System.Tuple`2[[System.Boolean],[System.Collections.Generic.IEnumerable`1[[Microsoft.OData.Edm.Validation.EdmError]]]]]] TryWriteSchemaAsync (Microsoft.OData.Edm.IEdmModel model, System.Xml.XmlWriter writer) } [ @@ -3100,7 +3110,13 @@ public class Microsoft.OData.Edm.Csdl.CsdlWriter { protected static string GetVersionString (System.Version version) public static bool TryWriteCsdl (Microsoft.OData.Edm.IEdmModel model, System.Xml.XmlWriter writer, Microsoft.OData.Edm.Csdl.CsdlTarget target, out System.Collections.Generic.IEnumerable`1[[Microsoft.OData.Edm.Validation.EdmError]]& errors) + [ + AsyncStateMachineAttribute(), + ] + public static System.Threading.Tasks.Task`1[[System.Tuple`2[[System.Boolean],[System.Collections.Generic.IEnumerable`1[[Microsoft.OData.Edm.Validation.EdmError]]]]]] TryWriteCsdlAsync (Microsoft.OData.Edm.IEdmModel model, System.Xml.XmlWriter writer, Microsoft.OData.Edm.Csdl.CsdlTarget target) + protected virtual void WriteCsdl () + protected virtual System.Threading.Tasks.Task WriteCsdlAsync () } [ From d09561e402807126327d575344705b20b8370944 Mon Sep 17 00:00:00 2001 From: Samuel Wanjohi Date: Tue, 10 Sep 2024 17:16:33 +0300 Subject: [PATCH 2/2] Added custom Task.CompletedTask for older .NET frameworks. Added missing ConfigureAwait(false) --- src/Microsoft.OData.Edm/Csdl/CsdlWriter.cs | 4 +- .../Serialization/EdmModelCsdlSchemaWriter.cs | 27 +-- .../EdmModelCsdlSchemaXmlWriter.cs | 7 +- src/Microsoft.OData.Edm/EdmModelVisitor.cs | 199 +++++++++--------- src/Microsoft.OData.Edm/Helpers/TaskUtils.cs | 38 ++++ .../Csdl/CsdlWriterTests.Async.cs | 72 +++++++ 6 files changed, 230 insertions(+), 117 deletions(-) create mode 100644 src/Microsoft.OData.Edm/Helpers/TaskUtils.cs diff --git a/src/Microsoft.OData.Edm/Csdl/CsdlWriter.cs b/src/Microsoft.OData.Edm/Csdl/CsdlWriter.cs index 08f6716dcb..c45ebe3128 100644 --- a/src/Microsoft.OData.Edm/Csdl/CsdlWriter.cs +++ b/src/Microsoft.OData.Edm/Csdl/CsdlWriter.cs @@ -14,7 +14,7 @@ using System.Threading.Tasks; using System.Xml; using Microsoft.OData.Edm.Csdl.Serialization; - +using Microsoft.OData.Edm.Helpers; using Microsoft.OData.Edm.Validation; namespace Microsoft.OData.Edm.Csdl @@ -184,7 +184,7 @@ protected virtual void WriteCsdl() /// Task represents an asynchronous operation that may or may not return a result. protected virtual Task WriteCsdlAsync() { - return Task.FromResult(0); + return TaskUtils.CompletedTask; } /// diff --git a/src/Microsoft.OData.Edm/Csdl/Serialization/EdmModelCsdlSchemaWriter.cs b/src/Microsoft.OData.Edm/Csdl/Serialization/EdmModelCsdlSchemaWriter.cs index f796e4360e..5fe17ecbe4 100644 --- a/src/Microsoft.OData.Edm/Csdl/Serialization/EdmModelCsdlSchemaWriter.cs +++ b/src/Microsoft.OData.Edm/Csdl/Serialization/EdmModelCsdlSchemaWriter.cs @@ -8,6 +8,7 @@ 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 @@ -106,7 +107,7 @@ internal virtual void WriteOutOfLineAnnotationsBegin(IEnumerable>> outOfLineAnnotations) { - return Task.FromResult(0); + return TaskUtils.CompletedTask; } internal virtual void WriteOutOfLineAnnotationsEnd(IEnumerable>> outOfLineAnnotations) @@ -116,7 +117,7 @@ internal virtual void WriteOutOfLineAnnotationsEnd(IEnumerable>> outOfLineAnnotations) { - return Task.FromResult(0); + return TaskUtils.CompletedTask; } internal abstract void WriteStructuralPropertyElementHeader(IEdmStructuralProperty property, bool inlineType); @@ -132,7 +133,7 @@ internal virtual void WriteEnumMemberElementEnd(IEdmEnumMember member) internal virtual Task WriteEnumMemberElementEndAsync(IEdmEnumMember member) { - return Task.FromResult(0); + return TaskUtils.CompletedTask; } internal abstract void WriteNavigationPropertyBinding(IEdmNavigationPropertyBinding binding); @@ -145,7 +146,7 @@ internal virtual void WriteNavigationPropertyBindingsBegin(IEnumerable bindings) { - return Task.FromResult(0); + return TaskUtils.CompletedTask; } internal virtual void WriteNavigationPropertyBindingsEnd(IEnumerable bindings) @@ -155,7 +156,7 @@ internal virtual void WriteNavigationPropertyBindingsEnd(IEnumerable bindings) { - return Task.FromResult(0); + return TaskUtils.CompletedTask; } internal abstract void WriteNullableAttribute(IEdmTypeReference reference); @@ -186,7 +187,7 @@ internal virtual void WriteReferentialConstraintBegin(IEdmReferentialConstraint internal virtual Task WriteReferentialConstraintBeginAsync(IEdmReferentialConstraint referentialConstraint) { - return Task.FromResult(0); + return TaskUtils.CompletedTask; } internal virtual void WriteReferentialConstraintEnd(IEdmReferentialConstraint referentialConstraint) @@ -196,7 +197,7 @@ internal virtual void WriteReferentialConstraintEnd(IEdmReferentialConstraint re internal virtual Task WriteReferentialConstraintEndAsync(IEdmReferentialConstraint referentialConstraint) { - return Task.FromResult(0); + return TaskUtils.CompletedTask; } internal abstract void WriteReferentialConstraintPair(EdmReferentialConstraintPropertyPair pair); @@ -278,7 +279,7 @@ internal virtual void WriteSchemaOperationsHeader(KeyValuePair(KeyValuePair> operations) { - return Task.FromResult(0); + return TaskUtils.CompletedTask; } internal virtual void WriteSchemaOperationsEnd(KeyValuePair> operation) @@ -288,7 +289,7 @@ internal virtual void WriteSchemaOperationsEnd(KeyValuePair> internal virtual Task WriteSchemaOperationsEndAsync(KeyValuePair> operation) { - return Task.FromResult(0); + return TaskUtils.CompletedTask; } internal virtual void WriteOperationParametersBegin(IEnumerable parameters) @@ -298,7 +299,7 @@ internal virtual void WriteOperationParametersBegin(IEnumerable parameters) { - return Task.FromResult(0); + return TaskUtils.CompletedTask; } internal virtual void WriteOperationParametersEnd(IEnumerable parameters) @@ -308,7 +309,7 @@ internal virtual void WriteOperationParametersEnd(IEnumerable parameters) { - return Task.FromResult(0); + return TaskUtils.CompletedTask; } internal abstract void WriteDateTimeOffsetConstantExpressionElement(IEdmDateTimeOffsetConstantExpression expression); @@ -378,7 +379,7 @@ internal virtual void WriteIsOfExpressionType(IEdmIsTypeExpression expression, b internal virtual Task WriteIsOfExpressionTypeAsync(IEdmIsTypeExpression expression, bool inlineType) { - return Task.FromResult(0); + return TaskUtils.CompletedTask; } internal abstract void WriteCastExpressionElementHeader(IEdmCastExpression expression, bool inlineType); @@ -394,7 +395,7 @@ internal virtual void WriteCastExpressionType(IEdmCastExpression expression, boo internal virtual Task WriteCastExpressionTypeAsync(IEdmCastExpression expression, bool inlineType) { - return Task.FromResult(0); + return TaskUtils.CompletedTask; } internal abstract void WriteEnumMemberExpressionElement(IEdmEnumMemberExpression expression); diff --git a/src/Microsoft.OData.Edm/Csdl/Serialization/EdmModelCsdlSchemaXmlWriter.cs b/src/Microsoft.OData.Edm/Csdl/Serialization/EdmModelCsdlSchemaXmlWriter.cs index 3f563b8302..0417f3abe4 100644 --- a/src/Microsoft.OData.Edm/Csdl/Serialization/EdmModelCsdlSchemaXmlWriter.cs +++ b/src/Microsoft.OData.Edm/Csdl/Serialization/EdmModelCsdlSchemaXmlWriter.cs @@ -13,6 +13,7 @@ using System.Xml; using Microsoft.OData.Edm.Csdl.CsdlSemantics; using Microsoft.OData.Edm.Csdl.Parsing.Ast; +using Microsoft.OData.Edm.Helpers; using Microsoft.OData.Edm.Vocabularies; using Microsoft.OData.Edm.Vocabularies.V1; @@ -718,7 +719,7 @@ internal override Task WriteTypeDefinitionAttributesAsync(IEdmTypeDefinitionRefe return this.WriteSpatialTypeAttributesAsync(actualTypeReference.AsSpatial()); } - return Task.FromResult(0); + return TaskUtils.CompletedTask; } /// @@ -811,7 +812,7 @@ internal override Task WriteSpatialTypeAttributesAsync(IEdmSpatialTypeReference return this.WriteOptionalAttributeAsync(CsdlConstants.Attribute_Srid, reference.SpatialReferenceIdentifier, CsdlConstants.Default_SpatialGeometrySrid, SridAsXml); } - return Task.FromResult(0); + return TaskUtils.CompletedTask; } /// @@ -1348,7 +1349,7 @@ internal override Task WriteInlineExpressionAsync(IEdmExpression expression) return this.WriteRequiredAttributeAsync(CsdlConstants.Attribute_TimeOfDay, ((IEdmTimeOfDayConstantExpression)expression).Value, EdmValueWriter.TimeOfDayAsXml); default: Debug.Assert(false, "Attempted to inline an expression that was not one of the expected inlineable types."); - return Task.FromResult(0); + return TaskUtils.CompletedTask; } } diff --git a/src/Microsoft.OData.Edm/EdmModelVisitor.cs b/src/Microsoft.OData.Edm/EdmModelVisitor.cs index 786a0bc2fe..f71928ff87 100644 --- a/src/Microsoft.OData.Edm/EdmModelVisitor.cs +++ b/src/Microsoft.OData.Edm/EdmModelVisitor.cs @@ -7,6 +7,7 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; +using Microsoft.OData.Edm.Helpers; using Microsoft.OData.Edm.Vocabularies; namespace Microsoft.OData.Edm @@ -236,19 +237,19 @@ public virtual async Task VisitEntityContainerElementsAsync(IEnumerable + /// Already completed task. + /// + private static Task _completedTask; + + // + /// Returns already completed task instance. + /// + public static Task CompletedTask + { + get + { +#if NETSTANDARD2_0_OR_GREATER || NETCOREAPP2_0_OR_GREATER + _completedTask = Task.CompletedTask; +#else + // Note that in case of two threads competing here we would create two completed tasks, but only one + // will be stored in the static variable. In any case, they are identical for all other purposes, + // so it doesn't matter which one wins + if (_completedTask == null) + { + // Create a TaskCompletionSource - since there's no non-generic version use a dummy one + // and then cast to the non-generic version. + TaskCompletionSource taskCompletionSource = new TaskCompletionSource(); + taskCompletionSource.SetResult(null); + _completedTask = taskCompletionSource.Task; + } +#endif + return _completedTask; + } + } + } +} diff --git a/test/FunctionalTests/Microsoft.OData.Edm.Tests/Csdl/CsdlWriterTests.Async.cs b/test/FunctionalTests/Microsoft.OData.Edm.Tests/Csdl/CsdlWriterTests.Async.cs index 91ae70db46..9ff737f075 100644 --- a/test/FunctionalTests/Microsoft.OData.Edm.Tests/Csdl/CsdlWriterTests.Async.cs +++ b/test/FunctionalTests/Microsoft.OData.Edm.Tests/Csdl/CsdlWriterTests.Async.cs @@ -6,6 +6,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Linq; using System.Text; @@ -2959,6 +2960,60 @@ public async Task TryWriteCsdlAsyncShouldFlushAsync_Async(CsdlTarget csdlTarget, } } +#if NETSTANDARD2_0 + [Fact] + public async Task TryWriteCsdlAsyncFromLargeModelToJson_Async() + { + // Arrange + var model = CreateLargeTestModel(); + using(var memStream = new MemoryStream()) + { + JsonWriterOptions options = new JsonWriterOptions + { + Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, + Indented = indented, + SkipValidation = false + }; + + using (Utf8JsonWriter jsonWriter = new Utf8JsonWriter(memStream, options)) + { + CsdlJsonWriterSettings settings = CsdlJsonWriterSettings.Default; + settings.IsIeee754Compatible = isIeee754Compatible; + var (ok, errors) = await CsdlWriter.TryWriteCsdlAsync(model, jsonWriter, settings).ConfigureAwait(false); + await jsonWriter.FlushAsync().ConfigureAwait(false); + Assert.True(ok); + } + } + } +#endif + + [Fact] + public async Task TryWriteCsdlAsyncFromLargeModelToXml_Async() + { + // Arrange + var model = CreateLargeTestModel(); + using (var stream = new MemoryStream()) + { + using (var writer = XmlWriter.Create(stream, new XmlWriterSettings { Async = true })) + { + var target = CsdlTarget.OData; + + // Act + var stopwatch = Stopwatch.StartNew(); + + Tuple> result = await CsdlWriter.TryWriteCsdlAsync(model, writer, target).ConfigureAwait(false); + bool success = result.Item1; + IEnumerable errors = result.Item2; + + stopwatch.Stop(); + + // Assert + Assert.True(success); + Assert.Empty(errors); + } + } + } + internal static async Task WriteAndVerifyXmlAsync(IEdmModel model, string expected, CsdlTarget target = CsdlTarget.OData) { using (StringWriter sw = new StringWriter()) @@ -3010,5 +3065,22 @@ internal async Task WriteAndVerifyJsonAsync(IEdmModel model, string expected, bo } #endif } + + private IEdmModel CreateLargeTestModel() + { + var model = new EdmModel(); + var container = new EdmEntityContainer("NS", "Container"); + model.AddElement(container); + + for (int i = 0; i < 2000; i++) + { + var entityType = new EdmEntityType("NS", $"EntityType{i}"); + entityType.AddKeys(entityType.AddStructuralProperty("Id", EdmPrimitiveTypeKind.Int32)); + model.AddElement(entityType); + container.AddEntitySet($"EntitySet{i}", entityType); + } + + return model; + } } }