public void TryGetReader_ReturnsCachedDelegate_WhenTypeImplementsMultipleIAsyncEnumerableContracts() { // Arrange var options = new MvcOptions(); var readerFactory = new AsyncEnumerableReader(options); var asyncEnumerable1 = new MultiAsyncEnumerable(); var asyncEnumerable2 = new MultiAsyncEnumerable(); // Act Assert.True(readerFactory.TryGetReader(asyncEnumerable1.GetType(), out var reader1)); Assert.True(readerFactory.TryGetReader(asyncEnumerable2.GetType(), out var reader2)); // Assert Assert.Same(reader1, reader2); }
public void TryGetReader_ReturnsDifferentInstancesForDifferentEnumerables() { // Arrange var options = new MvcOptions(); var readerFactory = new AsyncEnumerableReader(options); var enumerable1 = TestEnumerable(); var enumerable2 = TestEnumerable2(); // Act Assert.True(readerFactory.TryGetReader(enumerable1.GetType(), out var reader1)); Assert.True(readerFactory.TryGetReader(enumerable2.GetType(), out var reader2)); // Assert Assert.NotSame(reader1, reader2); }
public void TryGetReader_ReturnsCachedDelegate() { // Arrange var options = new MvcOptions(); var readerFactory = new AsyncEnumerableReader(options); var asyncEnumerable1 = TestEnumerable(); var asyncEnumerable2 = TestEnumerable(); // Act Assert.True(readerFactory.TryGetReader(asyncEnumerable1.GetType(), out var reader1)); Assert.True(readerFactory.TryGetReader(asyncEnumerable2.GetType(), out var reader2)); // Assert Assert.Same(reader1, reader2); }
/// <inheritdoc /> public override async Task WriteResponseBodyAsync(OutputFormatterWriteContext context, Encoding selectedEncoding) { if (context == null) { throw new ArgumentNullException(nameof(context)); } if (selectedEncoding == null) { throw new ArgumentNullException(nameof(selectedEncoding)); } // Compat mode for derived options _jsonOptions ??= context.HttpContext.RequestServices.GetRequiredService <IOptions <MvcNewtonsoftJsonOptions> >().Value; var response = context.HttpContext.Response; var responseStream = response.Body; FileBufferingWriteStream?fileBufferingWriteStream = null; if (!_mvcOptions.SuppressOutputFormatterBuffering) { fileBufferingWriteStream = new FileBufferingWriteStream(_jsonOptions.OutputFormatterMemoryBufferThreshold); responseStream = fileBufferingWriteStream; } var value = context.Object; if (value is not null && _asyncEnumerableReaderFactory.TryGetReader(value.GetType(), out var reader)) { _logger ??= context.HttpContext.RequestServices.GetRequiredService <ILogger <NewtonsoftJsonOutputFormatter> >(); Log.BufferingAsyncEnumerable(_logger, value); value = await reader(value); } try { await using (var writer = context.WriterFactory(responseStream, selectedEncoding)) { using var jsonWriter = CreateJsonWriter(writer); var jsonSerializer = CreateJsonSerializer(context); jsonSerializer.Serialize(jsonWriter, value); } if (fileBufferingWriteStream != null) { response.ContentLength = fileBufferingWriteStream.Length; await fileBufferingWriteStream.DrainBufferAsync(response.BodyWriter); } } finally { if (fileBufferingWriteStream != null) { await fileBufferingWriteStream.DisposeAsync(); } } }
public async Task Reader_ThrowsIfIAsyncEnumerableThrows() { // Arrange var enumerable = ThrowingAsyncEnumerable(); var options = new MvcOptions(); var readerFactory = new AsyncEnumerableReader(options); // Act & Assert Assert.True(readerFactory.TryGetReader(enumerable.GetType(), out var reader)); await Assert.ThrowsAsync <TimeZoneNotFoundException>(() => reader(enumerable, default)); }
public void TryGetReader_ReturnsFalse_IfTypeIsNotIAsyncEnumerable(Type type) { // Arrange var options = new MvcOptions(); var readerFactory = new AsyncEnumerableReader(options); var asyncEnumerable = TestEnumerable(); // Act var result = readerFactory.TryGetReader(type, out var reader); // Assert Assert.False(result); }
public async Task CachedDelegate_CanReadEnumerableInstanceMultipleTimes_ThatProduceDifferentResults() { // Arrange var options = new MvcOptions(); var readerFactory = new AsyncEnumerableReader(options); var asyncEnumerable1 = TestEnumerable(); var asyncEnumerable2 = TestEnumerable(4); // Act Assert.True(readerFactory.TryGetReader(asyncEnumerable1.GetType(), out var reader)); // Assert Assert.Equal(new[] { "0", "1", "2" }, await reader(asyncEnumerable1, default)); Assert.Equal(new[] { "0", "1", "2", "3" }, await reader(asyncEnumerable2, default)); }
public async Task Reader_PassesCancellationTokenToIAsyncEnumerable() { // Arrange var enumerable = AsyncEnumerable(); var options = new MvcOptions(); CancellationToken token = default; var readerFactory = new AsyncEnumerableReader(options); var cts = new CancellationTokenSource(); // Act & Assert Assert.True(readerFactory.TryGetReader(enumerable.GetType(), out var reader)); await reader(enumerable, cts.Token); cts.Cancel(); Assert.Equal(cts.Token, token); async IAsyncEnumerable <string> AsyncEnumerable([EnumeratorCancellation] CancellationToken cancellationToken = default)
public async Task TryGetReader_ReturnsReaderForIAsyncEnumerableOfValueType() { // Arrange var options = new MvcOptions(); var readerFactory = new AsyncEnumerableReader(options); var asyncEnumerable = PrimitiveEnumerable(); // Act var result = readerFactory.TryGetReader(asyncEnumerable.GetType(), out var reader); // Assert Assert.True(result); var readCollection = await reader(asyncEnumerable, default); var collection = Assert.IsAssignableFrom <ICollection <int> >(readCollection); Assert.Equal(new[] { 0, 1, 2, }, collection); }
public async Task TryGetReader_ReturnsReaderForIAsyncEnumerable() { // Arrange var options = new MvcOptions(); var readerFactory = new AsyncEnumerableReader(options); var asyncEnumerable = TestEnumerable(); // Act var result = readerFactory.TryGetReader(asyncEnumerable.GetType(), out var reader); // Assert Assert.True(result); var readCollection = await reader(asyncEnumerable, default); var collection = Assert.IsAssignableFrom <ICollection <string> >(readCollection); Assert.Equal(new[] { "0", "1", "2", }, collection); }
public async Task Reader_ThrowsIfBufferLimitIsReached() { // Arrange var enumerable = TestEnumerable(11); var expected = $"'AsyncEnumerableReader' reached the configured maximum size of the buffer when enumerating a value of type '{enumerable.GetType()}'. " + "This limit is in place to prevent infinite streams of 'IAsyncEnumerable<>' from continuing indefinitely. If this is not a programming mistake, " + $"consider ways to reduce the collection size, or consider manually converting '{enumerable.GetType()}' into a list rather than increasing the limit."; var options = new MvcOptions { MaxIAsyncEnumerableBufferLimit = 10 }; var readerFactory = new AsyncEnumerableReader(options); // Act Assert.True(readerFactory.TryGetReader(enumerable.GetType(), out var reader)); var ex = await Assert.ThrowsAsync <InvalidOperationException>(() => reader(enumerable, default)); // Assert Assert.Equal(expected, ex.Message); }
public async Task Reader_ReadsIAsyncEnumerable_ImplementingMultipleAsyncEnumerableInterfaces() { // This test ensures the reader does not fail if you have a type that implements IAsyncEnumerable for multiple Ts // Arrange var options = new MvcOptions(); var readerFactory = new AsyncEnumerableReader(options); var asyncEnumerable = new MultiAsyncEnumerable(); // Act var result = readerFactory.TryGetReader(asyncEnumerable.GetType(), out var reader); // Assert Assert.True(result); var readCollection = await reader(asyncEnumerable, default); var collection = Assert.IsAssignableFrom <ICollection <object> >(readCollection); Assert.Equal(new[] { "0", "1", "2", }, collection); }
/// <summary> /// Executes the <see cref="JsonResult"/> and writes the response. /// </summary> /// <param name="context">The <see cref="ActionContext"/>.</param> /// <param name="result">The <see cref="JsonResult"/>.</param> /// <returns>A <see cref="Task"/> which will complete when writing has completed.</returns> public async Task ExecuteAsync(ActionContext context, JsonResult result) { if (context == null) { throw new ArgumentNullException(nameof(context)); } if (result == null) { throw new ArgumentNullException(nameof(result)); } var jsonSerializerSettings = GetSerializerSettings(result); var response = context.HttpContext.Response; ResponseContentTypeHelper.ResolveContentTypeAndEncoding( result.ContentType, response.ContentType, (DefaultContentType, Encoding.UTF8), MediaType.GetEncoding, out var resolvedContentType, out var resolvedContentTypeEncoding); response.ContentType = resolvedContentType; if (result.StatusCode != null) { response.StatusCode = result.StatusCode.Value; } Log.JsonResultExecuting(_logger, result.Value); var responseStream = response.Body; FileBufferingWriteStream?fileBufferingWriteStream = null; if (!_mvcOptions.SuppressOutputFormatterBuffering) { fileBufferingWriteStream = new FileBufferingWriteStream(); responseStream = fileBufferingWriteStream; } try { var value = result.Value; if (value != null && _asyncEnumerableReaderFactory.TryGetReader(value.GetType(), out var reader)) { Log.BufferingAsyncEnumerable(_logger, value); try { value = await reader(value, context.HttpContext.RequestAborted); } catch (OperationCanceledException) when(context.HttpContext.RequestAborted.IsCancellationRequested) { } if (context.HttpContext.RequestAborted.IsCancellationRequested) { return; } } await using (var writer = _writerFactory.CreateWriter(responseStream, resolvedContentTypeEncoding)) { using var jsonWriter = new JsonTextWriter(writer); jsonWriter.ArrayPool = _charPool; jsonWriter.CloseOutput = false; jsonWriter.AutoCompleteOnClose = false; var jsonSerializer = JsonSerializer.Create(jsonSerializerSettings); jsonSerializer.Serialize(jsonWriter, value); } if (fileBufferingWriteStream != null) { await fileBufferingWriteStream.DrainBufferAsync(response.Body); } } finally { if (fileBufferingWriteStream != null) { await fileBufferingWriteStream.DisposeAsync(); } } }