// Complete disposable console probe. Publisher compilation/execution: UNVERIFIED. // This project does not contain the synthetic catalog's imaginary NuGet package. using System.Text; using System.Text.Json; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; var results = new List(); foreach (var item in new[] { (Id: "config_missing", Json: "{}", Expected: "Guest"), (Id: "config_null", Json: "{\"Name\":null}", Expected: "Guest"), (Id: "config_empty", Json: "{\"Name\":\"\"}", Expected: "Guest"), (Id: "config_name", Json: "{\"Name\":\" Neda \"}", Expected: "Neda") }) { using var stream = new MemoryStream(Encoding.UTF8.GetBytes(item.Json)); var config = new ConfigurationBuilder().AddJsonStream(stream).Build(); using var configLifetime = (IDisposable)config; // Build creates a disposable ConfigurationRoot. var options = new DisplayOptions(); config.Bind(options); var observed = DisplayName(options.Name); Require(observed == item.Expected, item.Id); results.Add(new { test_id = item.Id, observed, state = "PASS" }); } var caught = false; try { await FailDependencyAsync(); } catch (InvalidOperationException) { caught = true; } Require(caught, "async_failure"); results.Add(new { test_id = "async_failure", observed = "CAUGHT_AT_AWAIT", state = "PASS" }); var effects = 0; using (var cancellation = new CancellationTokenSource()) { cancellation.Cancel(); var cancelled = false; try { await EffectAsync(cancellation.Token); } catch (OperationCanceledException) { cancelled = true; } Require(cancelled && effects == 0, "async_cancellation"); } results.Add(new { test_id = "async_cancellation", observed = "CANCELLED_BEFORE_EFFECT", state = "PASS" }); using var services = new ServiceCollection().AddScoped().BuildServiceProvider( new ServiceProviderOptions { ValidateScopes = true, ValidateOnBuild = true }); ScopeProbe scoped; bool disposedBeforeAwait; using (var scope = services.CreateScope()) { scoped = scope.ServiceProvider.GetRequiredService(); disposedBeforeAwait = scoped.Disposed; await scoped.WorkAsync(CancellationToken.None); Require(!scoped.Disposed, "scope_lifetime_before_disposal"); } Require(!disposedBeforeAwait && scoped.Disposed, "scope_lifetime"); results.Add(new { test_id = "scope_lifetime", observed = new { disposed_before_await = disposedBeforeAwait, disposed_after_await = scoped.Disposed }, state = "PASS" }); Console.WriteLine(JsonSerializer.Serialize(new { scope = "DISPOSABLE_CONSOLE_ONLY", runtime_version = Environment.Version.ToString(), tests = results })); static string DisplayName(string? name) => string.IsNullOrWhiteSpace(name) ? "Guest" : name.Trim(); static void Require(bool condition, string test) { if (!condition) throw new InvalidOperationException("Probe failed: " + test); } static async Task FailDependencyAsync() { await Task.Yield(); throw new InvalidOperationException("Synthetic dependency failure"); } async Task EffectAsync(CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); await Task.Delay(1, cancellationToken); cancellationToken.ThrowIfCancellationRequested(); effects++; } public sealed class DisplayOptions { public string? Name { get; set; } = "Guest"; } public sealed class ScopeProbe : IDisposable { public bool Disposed { get; private set; } public async Task WorkAsync(CancellationToken cancellationToken) { ObjectDisposedException.ThrowIf(Disposed, this); await Task.Delay(1, cancellationToken); ObjectDisposedException.ThrowIf(Disposed, this); } public void Dispose() => Disposed = true; }