diff --git a/src/Microsoft.AspNet.OData.Shared/Formatter/ODataStreamMediaTypeMapping.cs b/src/Microsoft.AspNet.OData.Shared/Formatter/ODataStreamMediaTypeMapping.cs new file mode 100644 index 0000000000..aea4019a34 --- /dev/null +++ b/src/Microsoft.AspNet.OData.Shared/Formatter/ODataStreamMediaTypeMapping.cs @@ -0,0 +1,19 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +namespace Microsoft.AspNet.OData.Formatter +{ + /// + /// Media type mapping that associates requests with stream property. + /// + public partial class ODataStreamMediaTypeMapping + { + /// + /// Initializes a new instance of the class. + /// + public ODataStreamMediaTypeMapping() + : base("application/octet-stream") + { + } + } +} diff --git a/src/Microsoft.AspNet.OData.Shared/Formatter/Serialization/DefaultODataSerializerProvider.cs b/src/Microsoft.AspNet.OData.Shared/Formatter/Serialization/DefaultODataSerializerProvider.cs index 1f183f16d9..3a863c676b 100644 --- a/src/Microsoft.AspNet.OData.Shared/Formatter/Serialization/DefaultODataSerializerProvider.cs +++ b/src/Microsoft.AspNet.OData.Shared/Formatter/Serialization/DefaultODataSerializerProvider.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Linq; using Microsoft.AspNet.OData.Common; +using Microsoft.AspNet.OData.Routing; using Microsoft.Extensions.DependencyInjection; using Microsoft.OData; using Microsoft.OData.Edm; @@ -120,8 +121,9 @@ internal ODataSerializer GetODataPayloadSerializerImpl(Type type, Func(); } diff --git a/src/Microsoft.AspNet.OData.Shared/Formatter/Serialization/ODataResourceSerializer.cs b/src/Microsoft.AspNet.OData.Shared/Formatter/Serialization/ODataResourceSerializer.cs index c9cd8dcf72..02ffd1b8c8 100644 --- a/src/Microsoft.AspNet.OData.Shared/Formatter/Serialization/ODataResourceSerializer.cs +++ b/src/Microsoft.AspNet.OData.Shared/Formatter/Serialization/ODataResourceSerializer.cs @@ -592,6 +592,7 @@ private void WriteResource(object graph, ODataWriter writer, ODataSerializerCont else { writer.WriteStart(resource); + WriteStreamProperties(selectExpandNode, resourceContext, writer); WriteComplexProperties(selectExpandNode, resourceContext, writer); WriteDynamicComplexProperties(resourceContext, writer); WriteNavigationLinks(selectExpandNode, resourceContext, writer); @@ -633,6 +634,7 @@ await writer.WriteEntityReferenceLinkAsync(new ODataEntityReferenceLink else { await writer.WriteStartAsync(resource); + await WriteStreamPropertiesAsync(selectExpandNode, resourceContext, writer); await WriteComplexPropertiesAsync(selectExpandNode, resourceContext, writer); await WriteDynamicComplexPropertiesAsync(resourceContext, writer); await WriteNavigationLinksAsync(selectExpandNode, resourceContext, writer); @@ -1269,6 +1271,58 @@ private async Task WriteComplexPropertiesAsync(SelectExpandNode selectExpandNode } } + private void WriteStreamProperties(SelectExpandNode selectExpandNode, ResourceContext resourceContext, ODataWriter writer) + { + Contract.Assert(selectExpandNode != null); + Contract.Assert(resourceContext != null); + Contract.Assert(writer != null); + + if (selectExpandNode.SelectedStructuralProperties != null) + { + IEnumerable structuralProperties = selectExpandNode.SelectedStructuralProperties; + + foreach (IEdmStructuralProperty structuralProperty in structuralProperties) + { + if (structuralProperty.Type != null && structuralProperty.Type.IsStream()) + { + ODataStreamPropertyInfo property = CreateStreamProperty(structuralProperty, resourceContext); + + if (property != null) + { + writer.WriteStart(property); + writer.WriteEnd(); + } + } + } + } + } + + private async Task WriteStreamPropertiesAsync(SelectExpandNode selectExpandNode, ResourceContext resourceContext, ODataWriter writer) + { + Contract.Assert(selectExpandNode != null); + Contract.Assert(resourceContext != null); + Contract.Assert(writer != null); + + if (selectExpandNode.SelectedStructuralProperties != null) + { + IEnumerable structuralProperties = selectExpandNode.SelectedStructuralProperties; + + foreach (IEdmStructuralProperty structuralProperty in structuralProperties) + { + if (structuralProperty.Type != null && structuralProperty.Type.IsStream()) + { + ODataStreamPropertyInfo property = CreateStreamProperty(structuralProperty, resourceContext); + + if (property != null) + { + await writer.WriteStartAsync(property); + await writer.WriteEndAsync(); + } + } + } + } + } + private IEnumerable> GetPropertiesToWrite(SelectExpandNode selectExpandNode, ResourceContext resourceContext) { IDictionary complexProperties = selectExpandNode.SelectedComplexTypeProperties; @@ -1561,6 +1615,12 @@ private IEnumerable CreateStructuralPropertyBag(SelectExpandNode foreach (IEdmStructuralProperty structuralProperty in structuralProperties) { + if (structuralProperty.Type != null && structuralProperty.Type.IsStream()) + { + // skip the stream property, the stream property is written in its own logic + continue; + } + ODataProperty property = CreateStructuralProperty(structuralProperty, resourceContext); if (property != null) { @@ -1572,6 +1632,54 @@ private IEnumerable CreateStructuralPropertyBag(SelectExpandNode return properties; } + /// + /// Creates the to be written for the given stream property. + /// + /// The EDM structural property being written. + /// The context for the entity instance being written. + /// The to write. + internal virtual ODataStreamPropertyInfo CreateStreamProperty(IEdmStructuralProperty structuralProperty, ResourceContext resourceContext) + { + if (structuralProperty == null) + { + throw Error.ArgumentNull("structuralProperty"); + } + + if (resourceContext == null) + { + throw Error.ArgumentNull("resourceContext"); + } + + if (structuralProperty.Type == null || !structuralProperty.Type.IsStream()) + { + return null; + } + + if (resourceContext.SerializerContext.MetadataLevel != ODataMetadataLevel.FullMetadata) + { + return null; + } + + // TODO: we need to return ODataStreamReferenceValue if + // 1) If we have the EditLink link builder + // 2) If we have the ReadLink link builder + // 3) If we have the Core.AcceptableMediaTypes annotation associated with the Stream property + + // We need a way for the user to specify a mediatype for an instance of a stream property. + // If specified, we should explicitly write the streamreferencevalue and not let ODL fill it in. + + // Although the mediatype is represented as an instance annotation in JSON, it's really control information. + // So we shouldn't use instance annotations to tell us the media type, but have a separate way to specify the media type. + // Perhaps we define an interface (and stream wrapper class that derives from stream and implements the interface) that exposes a MediaType property. + // If the stream property implements this interface, and it specifies a media-type other than application/octet-stream, we explicitly create and write a StreamReferenceValue with that media type. + // We could also use this type to expose properties for things like ReadLink and WriteLink(and even ETag) + // that the user could specify to something other than the default convention + // if they wanted to provide custom routes for reading/writing the stream values or custom ETag values for the stream. + + // So far, let's return null and let OData.lib to calculate the ODataStreamReferenceValue by conventions. + return null; + } + /// /// Creates the to be written for the given entity and the structural property. /// diff --git a/src/Microsoft.AspNet.OData.Shared/Microsoft.AspNet.OData.Shared.projitems b/src/Microsoft.AspNet.OData.Shared/Microsoft.AspNet.OData.Shared.projitems index 6b48eccf27..8bd2116e5d 100644 --- a/src/Microsoft.AspNet.OData.Shared/Microsoft.AspNet.OData.Shared.projitems +++ b/src/Microsoft.AspNet.OData.Shared/Microsoft.AspNet.OData.Shared.projitems @@ -62,6 +62,7 @@ + diff --git a/src/Microsoft.AspNet.OData.Shared/Routing/ODataPathSegmentExtensions.cs b/src/Microsoft.AspNet.OData.Shared/Routing/ODataPathSegmentExtensions.cs index 82cc695a1d..d9a5b64480 100644 --- a/src/Microsoft.AspNet.OData.Shared/Routing/ODataPathSegmentExtensions.cs +++ b/src/Microsoft.AspNet.OData.Shared/Routing/ODataPathSegmentExtensions.cs @@ -18,6 +18,41 @@ namespace Microsoft.AspNet.OData.Routing /// internal static class ODataPathSegmentExtensions { + /// + /// Gets a value indicating whether the given path is a stream property path. + /// + /// The given odata path. + /// true/false + public static bool IsStreamPropertyPath(this ODataPath path) + { + if (path == null) + { + return false; + } + + PropertySegment propertySegment = path.Segments.LastOrDefault() as PropertySegment; + if (propertySegment == null) + { + return false; + } + + IEdmTypeReference propertyType = propertySegment.Property.Type; + if (propertyType == null) + { + return false; + } + + // Edm.Stream, or a type definition whose underlying type is Edm.Stream, + // cannot be used in collections or for non-binding parameters to functions or actions. + // So, we don't need to test it but leave the codes here for awareness. + //if (propertyType.IsCollection()) + //{ + // propertyType = propertyType.AsCollection().ElementType(); + //} + + return propertyType.IsStream(); + } + public static string TranslatePathTemplateSegment(this PathTemplateSegment pathTemplatesegment, out string value) { if (pathTemplatesegment == null) diff --git a/src/Microsoft.AspNet.OData/Formatter/ODataMediaTypeFormatter.cs b/src/Microsoft.AspNet.OData/Formatter/ODataMediaTypeFormatter.cs index b66175ff48..560ac95cb4 100644 --- a/src/Microsoft.AspNet.OData/Formatter/ODataMediaTypeFormatter.cs +++ b/src/Microsoft.AspNet.OData/Formatter/ODataMediaTypeFormatter.cs @@ -291,6 +291,15 @@ public override Task WriteToStreamAsync(Type type, object value, Stream writeStr try { + if (typeof(Stream).IsAssignableFrom(type)) + { + // Ideally, it should go into the "ODataRawValueSerializer", + // However, OData lib doesn't provide the method to overwrite/copyto stream + // So, Here's the workaround + Stream objStream = value as Stream; + return CopyStreamAsync(objStream, writeStream); + } + HttpConfiguration configuration = Request.GetConfiguration(); if (configuration == null) { @@ -335,6 +344,16 @@ public override Task WriteToStreamAsync(Type type, object value, Stream writeStr } } + private static async Task CopyStreamAsync(Stream source, Stream destination) + { + if (source != null) + { + await source.CopyToAsync(destination); + } + + await destination.FlushAsync(); + } + // To factor out request, just pass in a function to get base address. We'd get rid of // BaseAddressFactory and request. private Uri GetBaseAddressInternal(HttpRequestMessage request) diff --git a/src/Microsoft.AspNet.OData/Formatter/ODataMediaTypeFormatters.cs b/src/Microsoft.AspNet.OData/Formatter/ODataMediaTypeFormatters.cs index a3bad72510..5c1cbdd6cb 100644 --- a/src/Microsoft.AspNet.OData/Formatter/ODataMediaTypeFormatters.cs +++ b/src/Microsoft.AspNet.OData/Formatter/ODataMediaTypeFormatters.cs @@ -50,6 +50,7 @@ private static ODataMediaTypeFormatter CreateRawValue() formatter.MediaTypeMappings.Add(new ODataEnumValueMediaTypeMapping()); formatter.MediaTypeMappings.Add(new ODataBinaryValueMediaTypeMapping()); formatter.MediaTypeMappings.Add(new ODataCountMediaTypeMapping()); + formatter.MediaTypeMappings.Add(new ODataStreamMediaTypeMapping()); return formatter; } diff --git a/src/Microsoft.AspNet.OData/Formatter/ODataStreamMediaTypeMapping.cs b/src/Microsoft.AspNet.OData/Formatter/ODataStreamMediaTypeMapping.cs new file mode 100644 index 0000000000..f45ddde865 --- /dev/null +++ b/src/Microsoft.AspNet.OData/Formatter/ODataStreamMediaTypeMapping.cs @@ -0,0 +1,28 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System.Net.Http; +using System.Net.Http.Formatting; +using Microsoft.AspNet.OData.Common; +using Microsoft.AspNet.OData.Extensions; +using Microsoft.AspNet.OData.Routing; + +namespace Microsoft.AspNet.OData.Formatter +{ + /// + /// Media type mapping that associates requests with stream property. + /// + public partial class ODataStreamMediaTypeMapping : MediaTypeMapping + { + /// + public override double TryMatchMediaType(HttpRequestMessage request) + { + if (request == null) + { + throw Error.ArgumentNull("request"); + } + + return request.ODataProperties().Path.IsStreamPropertyPath() ? 1 : 0; + } + } +} diff --git a/src/Microsoft.AspNet.OData/Microsoft.AspNet.OData.csproj b/src/Microsoft.AspNet.OData/Microsoft.AspNet.OData.csproj index a45066c0ae..3e8ce28e59 100644 --- a/src/Microsoft.AspNet.OData/Microsoft.AspNet.OData.csproj +++ b/src/Microsoft.AspNet.OData/Microsoft.AspNet.OData.csproj @@ -88,6 +88,7 @@ + diff --git a/src/Microsoft.AspNetCore.OData/Formatter/ODataOutputFormatter.cs b/src/Microsoft.AspNetCore.OData/Formatter/ODataOutputFormatter.cs index 8f2d55435a..70c6f06e0d 100644 --- a/src/Microsoft.AspNetCore.OData/Formatter/ODataOutputFormatter.cs +++ b/src/Microsoft.AspNetCore.OData/Formatter/ODataOutputFormatter.cs @@ -220,6 +220,15 @@ public override Task WriteResponseBodyAsync(OutputFormatterWriteContext context, } HttpResponse response = context.HttpContext.Response; + if (typeof(Stream).IsAssignableFrom(type)) + { + // Ideally, it should go into the "ODataRawValueSerializer", + // However, OData lib doesn't provide the method to overwrite/copyto stream + // So, Here's the workaround + Stream objStream = context.Object as Stream; + return CopyStreamAsync(objStream, response); + } + Uri baseAddress = GetBaseAddressInternal(request); MediaTypeHeaderValue contentType = GetContentType(response.Headers[HeaderNames.ContentType].FirstOrDefault()); @@ -249,6 +258,16 @@ public override Task WriteResponseBodyAsync(OutputFormatterWriteContext context, getODataSerializerContext); } + private static async Task CopyStreamAsync(Stream source, HttpResponse response) + { + if (source != null) + { + await source.CopyToAsync(response.Body); + } + + await response.Body.FlushAsync(); + } + /// /// Internal method used for selecting the base address to be used with OData uris. /// If the consumer has provided a delegate for overriding our default implementation, diff --git a/src/Microsoft.AspNetCore.OData/Formatter/ODataOutputFormatterFactory.cs b/src/Microsoft.AspNetCore.OData/Formatter/ODataOutputFormatterFactory.cs index b5e3a8b539..8b15b58b04 100644 --- a/src/Microsoft.AspNetCore.OData/Formatter/ODataOutputFormatterFactory.cs +++ b/src/Microsoft.AspNetCore.OData/Formatter/ODataOutputFormatterFactory.cs @@ -48,6 +48,7 @@ private static ODataOutputFormatter CreateRawValue() formatter.MediaTypeMappings.Add(new ODataPrimitiveValueMediaTypeMapping()); formatter.MediaTypeMappings.Add(new ODataEnumValueMediaTypeMapping()); formatter.MediaTypeMappings.Add(new ODataCountMediaTypeMapping()); + formatter.MediaTypeMappings.Add(new ODataStreamMediaTypeMapping()); return formatter; } diff --git a/src/Microsoft.AspNetCore.OData/Formatter/ODataStreamMediaTypeMapping.cs b/src/Microsoft.AspNetCore.OData/Formatter/ODataStreamMediaTypeMapping.cs new file mode 100644 index 0000000000..ed6cb77e25 --- /dev/null +++ b/src/Microsoft.AspNetCore.OData/Formatter/ODataStreamMediaTypeMapping.cs @@ -0,0 +1,28 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using Microsoft.AspNet.OData.Common; +using Microsoft.AspNet.OData.Extensions; +using Microsoft.AspNet.OData.Routing; +using Microsoft.AspNetCore.Http; + +namespace Microsoft.AspNet.OData.Formatter +{ + /// + /// Media type mapping that associates requests with stream property. + /// + /// This class derives from a platform-specific class. + public partial class ODataStreamMediaTypeMapping : MediaTypeMapping + { + /// + public override double TryMatchMediaType(HttpRequest request) + { + if (request == null) + { + throw Error.ArgumentNull("request"); + } + + return request.ODataFeature().Path.IsStreamPropertyPath() ? 1 : 0; + } + } +} diff --git a/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Microsoft.AspNet.OData.Test.Shared.projitems b/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Microsoft.AspNet.OData.Test.Shared.projitems index 062494493b..1d970dc204 100644 --- a/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Microsoft.AspNet.OData.Test.Shared.projitems +++ b/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Microsoft.AspNet.OData.Test.Shared.projitems @@ -397,6 +397,7 @@ + diff --git a/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/ODataStreamPropertyTest.cs b/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/ODataStreamPropertyTest.cs new file mode 100644 index 0000000000..af1d6d4229 --- /dev/null +++ b/test/UnitTest/Microsoft.AspNet.OData.Test.Shared/ODataStreamPropertyTest.cs @@ -0,0 +1,222 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +#if NETCORE +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Threading.Tasks; +using Microsoft.AspNet.OData.Builder; +using Microsoft.AspNet.OData.Extensions; +using Microsoft.AspNet.OData.Test.Abstraction; +using Microsoft.AspNetCore.Mvc; +using Microsoft.OData.Edm; +using Newtonsoft.Json.Linq; +using Xunit; +#else +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Threading.Tasks; +using System.Web.Http; +using Microsoft.AspNet.OData.Builder; +using Microsoft.AspNet.OData.Extensions; +using Microsoft.AspNet.OData.Test.Abstraction; +using Microsoft.OData.Edm; +using Newtonsoft.Json.Linq; +using Xunit; +#endif + +namespace Microsoft.AspNet.OData.Test +{ + public class ODataStreamPropertyTest + { + [Fact] + public async Task GetMetadata_WithStreamProperty() + { + // Arrange + const string RequestUri = "http://localhost/odata/$metadata"; + + var controllers = new[] { typeof(MetadataController) }; + var server = TestServerFactory.Create(controllers, (config) => + { + config.MapODataServiceRoute("odata", "odata", GetEdmModel()); + }); + var client = TestServerFactory.CreateClient(server); + + // Act + HttpResponseMessage response = await client.GetAsync(RequestUri); + + // Assert + var payload = await response.Content.ReadAsStringAsync(); + Assert.Contains("", payload); + } + + [Fact] + public async Task Get_EntityWithStreamProperty() + { + // Arrange + const string RequestUri = "http://localhost/odata/StreamCustomers(1)"; + + var controllers = new[] { typeof(StreamCustomersController) }; + + var server = TestServerFactory.Create(controllers, (config) => + { + config.MapODataServiceRoute("odata", "odata", GetEdmModel()); + }); + var client = TestServerFactory.CreateClient(server); + + // Act + HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, RequestUri); + request.Headers.Accept.Add(MediaTypeWithQualityHeaderValue.Parse("application/json;odata.metadata=full")); + HttpResponseMessage response = await client.SendAsync(request); + + // Assert + Assert.True(response.IsSuccessStatusCode); + JObject result = JObject.Parse(await response.Content.ReadAsStringAsync()); + + Assert.Equal("http://localhost/odata/$metadata#StreamCustomers/$entity", result["@odata.context"]); + Assert.Equal("#Microsoft.AspNet.OData.Test.StreamCustomer", result["@odata.type"]); + Assert.Equal("\u0002\u0003\u0004\u0005", result["PhotoText"]); + Assert.Equal("http://localhost/odata/StreamCustomers(1)/Photo", result["Photo@odata.mediaEditLink"]); + Assert.Equal("http://localhost/odata/StreamCustomers(1)/Photo", result["Photo@odata.mediaReadLink"]); + } + + [Fact] + public async Task Get_SingleStreamProperty() + { + // Arrange + const string RequestUri = "http://localhost/odata/StreamCustomers(2)/Photo"; + + var controllers = new[] { typeof(StreamCustomersController) }; + + var server = TestServerFactory.Create(controllers, (config) => + { + config.MapODataServiceRoute("odata", "odata", GetEdmModel()); + }); + var client = TestServerFactory.CreateClient(server); + + // Act + HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, RequestUri); + HttpResponseMessage response = await client.SendAsync(request); + + // Assert + Assert.True(response.IsSuccessStatusCode); + + Assert.Equal("application/octet-stream", response.Content.Headers.ContentType.MediaType); + var stream = await response.Content.ReadAsStreamAsync(); + + StreamReader reader = new StreamReader(stream); + string text = reader.ReadToEnd(); + Assert.Equal("\u0003\u0004\u0005\u0006", text); + + byte[] byteArray = stream.ReadAllBytes(); + Assert.Equal(new byte[] { 3, 4, 5, 6 }, byteArray); + } + + private static IEdmModel GetEdmModel() + { + ODataModelBuilder builder = ODataConventionModelBuilderFactory.Create(); + builder.EntitySet("StreamCustomers"); + return builder.GetEdmModel(); + } + } + + public static class StreamExtensions + { + public static byte[] ReadAllBytes(this Stream instream) + { + if (instream is MemoryStream) + return ((MemoryStream)instream).ToArray(); + + using (var memoryStream = new MemoryStream()) + { + instream.CopyTo(memoryStream); + return memoryStream.ToArray(); + } + } + } + + // Controller + public class StreamCustomersController : TestODataController + { + [EnableQuery] + public IQueryable Get() + { + return CreateCustomers().AsQueryable(); + } + + public ITestActionResult Get(int key) + { + IList customers = CreateCustomers(); + StreamCustomer customer = customers.FirstOrDefault(c => c.Id == key); + if (customer == null) + { + return NotFound(); + } + + return Ok(customer); + } + + [HttpGet] + public ITestActionResult GetName(int key) + { + IList customers = CreateCustomers(); + StreamCustomer customer = customers.FirstOrDefault(c => c.Id == key); + if (customer == null) + { + return NotFound(); + } + + return Ok(customer.Name); + } + + [HttpGet] + public ITestActionResult GetPhoto(int key) + { + IList customers = CreateCustomers(); + StreamCustomer customer = customers.FirstOrDefault(c => c.Id == key); + if (customer == null) + { + return NotFound(); + } + + return Ok(customer.Photo); + } + private static IList CreateCustomers() + { + byte[] byteArray = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + IList customers = Enumerable.Range(0, 5).Select(i => + new StreamCustomer + { + Id = i, + Name = "FirstName " + i, + Photo = new MemoryStream(byteArray, i, 4), + }).ToList(); + + foreach (var c in customers) + { + c.PhotoText = new StreamReader(c.Photo).ReadToEnd(); + c.Photo.Seek(0, SeekOrigin.Begin); + } + + return customers; + } + } + + public class StreamCustomer + { + public int Id { get; set; } + + public string Name { get; set; } + + // this property saves the string of the Photo + public string PhotoText { get; set; } + + public Stream Photo { get; set; } + } +} diff --git a/test/UnitTest/Microsoft.AspNet.OData.Test/PublicApi/Microsoft.AspNet.OData.PublicApi.bsl b/test/UnitTest/Microsoft.AspNet.OData.Test/PublicApi/Microsoft.AspNet.OData.PublicApi.bsl index f8daaa30ac..e9bb64ae4f 100644 --- a/test/UnitTest/Microsoft.AspNet.OData.Test/PublicApi/Microsoft.AspNet.OData.PublicApi.bsl +++ b/test/UnitTest/Microsoft.AspNet.OData.Test/PublicApi/Microsoft.AspNet.OData.PublicApi.bsl @@ -2240,6 +2240,12 @@ public class Microsoft.AspNet.OData.Formatter.ODataPrimitiveValueMediaTypeMappin protected virtual bool IsMatch (Microsoft.OData.UriParser.PropertySegment propertySegment) } +public class Microsoft.AspNet.OData.Formatter.ODataStreamMediaTypeMapping : System.Net.Http.Formatting.MediaTypeMapping { + public ODataStreamMediaTypeMapping () + + public virtual double TryMatchMediaType (System.Net.Http.HttpRequestMessage request) +} + public class Microsoft.AspNet.OData.Formatter.QueryStringMediaTypeMapping : System.Net.Http.Formatting.MediaTypeMapping { public QueryStringMediaTypeMapping (string queryStringParameterName, System.Net.Http.Headers.MediaTypeHeaderValue mediaType) public QueryStringMediaTypeMapping (string queryStringParameterName, string mediaType) @@ -3478,6 +3484,7 @@ public class Microsoft.AspNet.OData.Formatter.Serialization.ODataResourceSeriali public virtual Microsoft.OData.ODataFunction CreateODataFunction (Microsoft.OData.Edm.IEdmFunction function, ResourceContext resourceContext) public virtual Microsoft.OData.ODataResource CreateResource (SelectExpandNode selectExpandNode, ResourceContext resourceContext) public virtual SelectExpandNode CreateSelectExpandNode (ResourceContext resourceContext) + internal virtual Microsoft.OData.ODataStreamPropertyInfo CreateStreamProperty (Microsoft.OData.Edm.IEdmStructuralProperty structuralProperty, ResourceContext resourceContext) public virtual Microsoft.OData.ODataProperty CreateStructuralProperty (Microsoft.OData.Edm.IEdmStructuralProperty structuralProperty, ResourceContext resourceContext) public virtual void WriteDeltaObjectInline (object graph, Microsoft.OData.Edm.IEdmTypeReference expectedType, Microsoft.OData.ODataWriter writer, ODataSerializerContext writeContext) public virtual System.Threading.Tasks.Task WriteDeltaObjectInlineAsync (object graph, Microsoft.OData.Edm.IEdmTypeReference expectedType, Microsoft.OData.ODataWriter writer, ODataSerializerContext writeContext) diff --git a/test/UnitTest/Microsoft.AspNetCore.OData.Test/PublicApi/Microsoft.AspNetCore.OData.PublicApi.bsl b/test/UnitTest/Microsoft.AspNetCore.OData.Test/PublicApi/Microsoft.AspNetCore.OData.PublicApi.bsl index cfd6e90c38..fc3fbb915b 100644 --- a/test/UnitTest/Microsoft.AspNetCore.OData.Test/PublicApi/Microsoft.AspNetCore.OData.PublicApi.bsl +++ b/test/UnitTest/Microsoft.AspNetCore.OData.Test/PublicApi/Microsoft.AspNetCore.OData.PublicApi.bsl @@ -2375,6 +2375,12 @@ public class Microsoft.AspNet.OData.Formatter.ODataPrimitiveValueMediaTypeMappin protected virtual bool IsMatch (Microsoft.OData.UriParser.PropertySegment propertySegment) } +public class Microsoft.AspNet.OData.Formatter.ODataStreamMediaTypeMapping : MediaTypeMapping { + public ODataStreamMediaTypeMapping () + + public virtual double TryMatchMediaType (Microsoft.AspNetCore.Http.HttpRequest request) +} + public class Microsoft.AspNet.OData.Formatter.QueryStringMediaTypeMapping : MediaTypeMapping { public QueryStringMediaTypeMapping (string queryStringParameterName, string mediaType) public QueryStringMediaTypeMapping (string queryStringParameterName, string queryStringParameterValue, string mediaType) @@ -3680,6 +3686,7 @@ public class Microsoft.AspNet.OData.Formatter.Serialization.ODataResourceSeriali public virtual Microsoft.OData.ODataFunction CreateODataFunction (Microsoft.OData.Edm.IEdmFunction function, ResourceContext resourceContext) public virtual Microsoft.OData.ODataResource CreateResource (SelectExpandNode selectExpandNode, ResourceContext resourceContext) public virtual SelectExpandNode CreateSelectExpandNode (ResourceContext resourceContext) + internal virtual Microsoft.OData.ODataStreamPropertyInfo CreateStreamProperty (Microsoft.OData.Edm.IEdmStructuralProperty structuralProperty, ResourceContext resourceContext) public virtual Microsoft.OData.ODataProperty CreateStructuralProperty (Microsoft.OData.Edm.IEdmStructuralProperty structuralProperty, ResourceContext resourceContext) public virtual void WriteDeltaObjectInline (object graph, Microsoft.OData.Edm.IEdmTypeReference expectedType, Microsoft.OData.ODataWriter writer, ODataSerializerContext writeContext) public virtual System.Threading.Tasks.Task WriteDeltaObjectInlineAsync (object graph, Microsoft.OData.Edm.IEdmTypeReference expectedType, Microsoft.OData.ODataWriter writer, ODataSerializerContext writeContext) diff --git a/test/UnitTest/Microsoft.AspNetCore.OData.Test/PublicApi/Microsoft.AspNetCore3x.OData.PublicApi.bsl b/test/UnitTest/Microsoft.AspNetCore.OData.Test/PublicApi/Microsoft.AspNetCore3x.OData.PublicApi.bsl index f2b3d7047d..53317d10d5 100644 --- a/test/UnitTest/Microsoft.AspNetCore.OData.Test/PublicApi/Microsoft.AspNetCore3x.OData.PublicApi.bsl +++ b/test/UnitTest/Microsoft.AspNetCore.OData.Test/PublicApi/Microsoft.AspNetCore3x.OData.PublicApi.bsl @@ -2550,6 +2550,12 @@ public class Microsoft.AspNet.OData.Formatter.ODataPrimitiveValueMediaTypeMappin protected virtual bool IsMatch (Microsoft.OData.UriParser.PropertySegment propertySegment) } +public class Microsoft.AspNet.OData.Formatter.ODataStreamMediaTypeMapping : MediaTypeMapping { + public ODataStreamMediaTypeMapping () + + public virtual double TryMatchMediaType (Microsoft.AspNetCore.Http.HttpRequest request) +} + public class Microsoft.AspNet.OData.Formatter.QueryStringMediaTypeMapping : MediaTypeMapping { public QueryStringMediaTypeMapping (string queryStringParameterName, string mediaType) public QueryStringMediaTypeMapping (string queryStringParameterName, string queryStringParameterValue, string mediaType) @@ -3879,6 +3885,7 @@ public class Microsoft.AspNet.OData.Formatter.Serialization.ODataResourceSeriali public virtual Microsoft.OData.ODataFunction CreateODataFunction (Microsoft.OData.Edm.IEdmFunction function, ResourceContext resourceContext) public virtual Microsoft.OData.ODataResource CreateResource (SelectExpandNode selectExpandNode, ResourceContext resourceContext) public virtual SelectExpandNode CreateSelectExpandNode (ResourceContext resourceContext) + internal virtual Microsoft.OData.ODataStreamPropertyInfo CreateStreamProperty (Microsoft.OData.Edm.IEdmStructuralProperty structuralProperty, ResourceContext resourceContext) public virtual Microsoft.OData.ODataProperty CreateStructuralProperty (Microsoft.OData.Edm.IEdmStructuralProperty structuralProperty, ResourceContext resourceContext) public virtual void WriteDeltaObjectInline (object graph, Microsoft.OData.Edm.IEdmTypeReference expectedType, Microsoft.OData.ODataWriter writer, ODataSerializerContext writeContext) public virtual System.Threading.Tasks.Task WriteDeltaObjectInlineAsync (object graph, Microsoft.OData.Edm.IEdmTypeReference expectedType, Microsoft.OData.ODataWriter writer, ODataSerializerContext writeContext)