MDev.Dotnet.Azure.ContainerApps 1.14.0

dotnet add package MDev.Dotnet.Azure.ContainerApps --version 1.14.0
                    
NuGet\Install-Package MDev.Dotnet.Azure.ContainerApps -Version 1.14.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="MDev.Dotnet.Azure.ContainerApps" Version="1.14.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="MDev.Dotnet.Azure.ContainerApps" Version="1.14.0" />
                    
Directory.Packages.props
<PackageReference Include="MDev.Dotnet.Azure.ContainerApps" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add MDev.Dotnet.Azure.ContainerApps --version 1.14.0
                    
#r "nuget: MDev.Dotnet.Azure.ContainerApps, 1.14.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package MDev.Dotnet.Azure.ContainerApps@1.14.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=MDev.Dotnet.Azure.ContainerApps&version=1.14.0
                    
Install as a Cake Addin
#tool nuget:?package=MDev.Dotnet.Azure.ContainerApps&version=1.14.0
                    
Install as a Cake Tool

MDev.Dotnet

Welcome to MDev.Dotnet - a collection of production-ready helpers and tools designed to accelerate .NET web application development and simplify cloud service integration. This repository provides battle-tested libraries that streamline common development tasks and Azure PaaS service configurations.

Table of Contents


Overview

This repository contains four main packages that help you build robust .NET applications with Azure integration:

  • AspNetCore helpers for standard web API configuration
  • Container Apps helpers for OpenTelemetry, authentication, and async operations
  • CosmosDb helpers for Entity Framework Core integration
  • Storage Account helpers for blob and queue operations

Each package follows consistent patterns and integrates seamlessly with the .NET dependency injection system.


Quick Start

  1. Install the NuGet package you need:

    dotnet add package MDev.Dotnet.AspNetCore
    dotnet add package MDev.Dotnet.Azure.ContainerApps
    dotnet add package MDev.Dotnet.Azure.CosmosDb
    dotnet add package MDev.Dotnet.Azure.StorageAccount
    
  2. Configure your services in Program.cs

  3. Add required configuration sections to appsettings.json

  4. Start using the helpers in your application


Packages

MDev.Dotnet.AspNetCore

🎯 Highlight

Streamline ASP.NET Core web API configuration with pre-configured controllers, routing, API versioning, and configuration management. This package eliminates boilerplate code and enforces best practices for web API development.

Key Features:

  • ✅ Simplified controller registration with built-in logging for 400 errors
  • ✅ Automatic API versioning support
  • ✅ Configuration binding with type safety
  • ✅ Lowercase URL routing
  • ✅ OpenAPI/Swagger configuration
  • ✅ Route prefix middleware
  • ✅ Synchronous IO support when needed

📚 Explain

This package provides extension methods that wrap common ASP.NET Core configurations into single-line registrations. It helps you:

  1. Controller Registration - Registers controllers with automatic model validation logging
  2. Configuration Management - Binds configuration sections to strongly-typed objects
  3. API Versioning - Adds versioning support to your APIs
  4. OpenAPI Support - Configures OpenAPI documentation with customizable options
  5. Route Prefixing - Adds global route prefixes to your API endpoints

Main Extension Methods:

  • RegisterControllers<T>() - Registers MVC controllers with logging and versioning
  • RegisterConfiguration() - Loads appsettings.json with environment-specific overrides
  • BindConfiguration<T>() - Binds configuration sections to POCOs
  • RegisterOpenApi() - Configures OpenAPI/Swagger documentation
  • UseRoutePrefix() - Adds a route prefix to all endpoints

🔗 Resources

⚙️ Operation

Installation
dotnet add package MDev.Dotnet.AspNetCore
Basic Setup in Program.cs
using MDev.Dotnet.AspNetCore.Apis.Extensions;

var builder = WebApplication.CreateBuilder(args);

// Register configuration
builder.RegisterConfiguration();

// Register controllers with API versioning
builder.RegisterControllers<Program>();

// Register OpenAPI
builder.RegisterOpenApi();

var app = builder.Build();

// Add route prefix (optional)
app.UseRoutePrefix("api");

app.MapControllers();
app.MapOpenApi();

app.Run();
Configuration Binding Example
// Define your settings class
public class MyAppSettings
{
    public string ApiKey { get; set; }
    public int Timeout { get; set; }
}

// In Program.cs
builder.BindConfiguration<MyAppSettings>(out var settings, "MyAppSettings");

// Or inject via IOptions<T>
builder.BindConfiguration<MyAppSettings>("MyAppSettings");

// In your service
public class MyService
{
    private readonly MyAppSettings _settings;
    
    public MyService(IOptions<MyAppSettings> settings)
    {
        _settings = settings.Value;
    }
}
appsettings.json Example
{
  "MyAppSettings": {
    "ApiKey": "your-api-key",
    "Timeout": 30
  }
}
Advanced Controller Registration
// With synchronous IO support
builder.RegisterControllers<Program>(
    allowSynchronousIO: true,
    mvcOptions: options => {
        // Add custom MVC options
        options.MaxModelValidationErrors = 50;
    }
);
OpenAPI Configuration
// Force HTTPS and hide server URLs
builder.RegisterOpenApi(
    forceHttpsServers: true,
    includeServerUrls: false
);

MDev.Dotnet.Azure.ContainerApps

🎯 Highlight

Supercharge your Azure Container Apps with integrated OpenTelemetry, authentication helpers, and Dapr-based async operations. This package simplifies container app configuration and monitoring.

Key Features:

  • ✅ OpenTelemetry integration with Azure Monitor or custom OTLP endpoints
  • ✅ Container Apps authentication helpers
  • ✅ Dapr pub/sub async operation handlers
  • ✅ Built-in metrics, tracing, and logging
  • ✅ Configurable status code handling
  • ✅ Dapr API token validation

📚 Explain

This package builds on top of MDev.Dotnet.AspNetCore and adds Azure Container Apps-specific functionality:

  1. OpenTelemetry Configuration - Automatic setup for Azure Monitor or Dynatrace with customizable metrics
  2. Authentication Helpers - Easy access to Container Apps authentication context
  3. Async Operations - Base controller for handling Dapr pub/sub messages
  4. Dapr Integration - Attribute-based API token validation

Main Components:

  • RegisterOpenTelemetry() - Configures OpenTelemetry with Azure Monitor or OTLP
  • DaprHandlerController - Base controller for Dapr pub/sub handlers
  • IAsyncOperationRequestMessageHandler - Interface for async operation handlers
  • RequireDaprApiTokenAttribute - Validates Dapr API tokens
  • MsClientPrincipal - Access Container Apps authentication context

🔗 Resources

⚙️ Operation

Installation
dotnet add package MDev.Dotnet.Azure.ContainerApps
OpenTelemetry Setup
using MDev.Dotnet.AspNetCore.OpenTelemetry.Apis.Extensions;

var builder = WebApplication.CreateBuilder(args);

// Register OpenTelemetry with custom meters
var customMeters = new List<string>
{
    "MyApp.Orders",
    "MyApp.Payments"
};

builder.RegisterOpenTelemetry(customMeters);

var app = builder.Build();
app.Run();
appsettings.json for OpenTelemetry
{
  "OpenTelemetry": {
    "ServiceType": "AppInsights",
    "IgnoreErrorStatusCode": [404, 401]
  }
}

For custom OTLP endpoints (e.g., Dynatrace):

{
  "OpenTelemetry": {
    "ServiceType": "Custom"
  },
  "OTEL_ENDPOINT": "https://your-otlp-endpoint.com",
  "OTEL_ENDPOINT_AUTH": "Api-Token your-token",
  "CONTAINER_APP_NAME": "myapp",
  "CONTAINER_APP_REPLICA_NAME": "myapp-revision-1"
}
Implementing Async Operations with Dapr

Step 1: Create a Message Handler

using MDev.Dotnet.AspNetCore.AsyncOperations.Abstracts;
using MDev.Dotnet.AspNetCore.AsyncOperations.Messages;

public class OrderProcessingHandler : IAsyncOperationRequestMessageHandler
{
    public string OperationType => "ProcessOrder";
    
    public async Task<object> HandleAsync(
        AsyncOperationRequestMessage message, 
        CancellationToken cancellationToken)
    {
        // Process your order
        var orderId = message.Data["orderId"].ToString();
        
        // Your business logic here
        await ProcessOrderAsync(orderId);
        
        return new { Success = true, OrderId = orderId };
    }
}

Step 2: Register the Handler

builder.Services.AddScoped<IAsyncOperationRequestMessageHandler, OrderProcessingHandler>();
builder.Services.AddScoped<AsyncOperationRequestsService>();

Step 3: Create Your Dapr Controller

using MDev.Dotnet.AspNetCore.AsyncOperations.Controllers.v1;
using MDev.Dotnet.Dapr.Attributes;
using Microsoft.AspNetCore.Mvc;

[ApiController]
[Route("api/v1/dapr")]
public class MyDaprController : DaprHandlerController
{
    public MyDaprController(AsyncOperationRequestsService service) 
        : base(service)
    {
    }
    
    [HttpPost("orders")]
    [RequireDaprApiToken]
    public override Task<IActionResult> OperationRequestAsync(
        [FromBody] AsyncOperationRequestMessage item,
        CancellationToken cancellationToken)
    {
        return base.OperationRequestAsync(item, cancellationToken);
    }
}
Accessing Container Apps Authentication
using MDev.Dotnet.Azure.ContainerApps.Authentication.Extensions;

public class MyController : ControllerBase
{
    private readonly IHttpContextAccessor _httpContextAccessor;
    
    public MyController(IHttpContextAccessor httpContextAccessor)
    {
        _httpContextAccessor = httpContextAccessor;
    }
    
    [HttpGet]
    public IActionResult GetUserInfo()
    {
        var principal = _httpContextAccessor.GetMsClientPrincipal();
        
        if (principal != null)
        {
            return Ok(new 
            { 
                UserId = principal.UserId,
                UserName = principal.UserDetails,
                Roles = principal.UserRoles
            });
        }
        
        return Unauthorized();
    }
}

MDev.Dotnet.Azure.CosmosDb

🎯 Highlight

Simplify Azure CosmosDb integration with Entity Framework Core. This package provides a one-line registration for CosmosDb contexts with managed identity support.

Key Features:

  • ✅ Simple DbContext registration for CosmosDb
  • ✅ Managed Identity (TokenCredential) support
  • ✅ Configuration-based setup
  • ✅ Scoped DbContext lifetime management

📚 Explain

This package simplifies the integration of Azure CosmosDb with Entity Framework Core by:

  1. DbContext Registration - Automatically configures EF Core with CosmosDb provider
  2. Credential Management - Uses Azure TokenCredential for secure authentication
  3. Configuration Binding - Reads CosmosDb settings from appsettings.json

Main Extension Method:

  • RegisterCosmosDb<T>() - Registers a DbContext with CosmosDb provider

🔗 Resources

⚙️ Operation

Installation
dotnet add package MDev.Dotnet.Azure.CosmosDb
dotnet add package Azure.Identity
Define Your DbContext
using Microsoft.EntityFrameworkCore;

public class MyAppDbContext : DbContext
{
    public MyAppDbContext(DbContextOptions<MyAppDbContext> options) 
        : base(options)
    {
    }
    
    public DbSet<Product> Products { get; set; }
    public DbSet<Order> Orders { get; set; }
    
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Product>()
            .ToContainer("Products")
            .HasPartitionKey(p => p.Category);
            
        modelBuilder.Entity<Order>()
            .ToContainer("Orders")
            .HasPartitionKey(o => o.CustomerId);
    }
}

public class Product
{
    public string Id { get; set; }
    public string Name { get; set; }
    public string Category { get; set; }
    public decimal Price { get; set; }
}

public class Order
{
    public string Id { get; set; }
    public string CustomerId { get; set; }
    public List<string> ProductIds { get; set; }
    public decimal Total { get; set; }
}
Register CosmosDb in Program.cs
using Azure.Identity;
using MDev.Dotnet.Azure.CosmosDb.Startup;

var builder = WebApplication.CreateBuilder(args);

// Use DefaultAzureCredential for managed identity
var credential = new DefaultAzureCredential();

// Or use specific credential type
// var credential = new ManagedIdentityCredential();
// var credential = new ClientSecretCredential(tenantId, clientId, clientSecret);

builder.RegisterCosmosDb<MyAppDbContext>(credential);

var app = builder.Build();
app.Run();
appsettings.json Configuration
{
  "CosmosDb": {
    "Endpoint": "https://your-account.documents.azure.com:443/",
    "DatabaseName": "MyAppDatabase"
  }
}
Using the DbContext
public class ProductService
{
    private readonly MyAppDbContext _dbContext;
    
    public ProductService(MyAppDbContext dbContext)
    {
        _dbContext = dbContext;
    }
    
    public async Task<Product> GetProductAsync(string id, string category)
    {
        return await _dbContext.Products
            .WithPartitionKey(category)
            .FirstOrDefaultAsync(p => p.Id == id);
    }
    
    public async Task CreateProductAsync(Product product)
    {
        _dbContext.Products.Add(product);
        await _dbContext.SaveChangesAsync();
    }
    
    public async Task<List<Product>> GetProductsByCategoryAsync(string category)
    {
        return await _dbContext.Products
            .WithPartitionKey(category)
            .ToListAsync();
    }
}
EnsureCreated (Development Only)
// In Program.cs for development environments
using (var scope = app.Services.CreateScope())
{
    var dbContext = scope.ServiceProvider.GetRequiredService<MyAppDbContext>();
    await dbContext.Database.EnsureCreatedAsync();
}

MDev.Dotnet.Azure.StorageAccount

🎯 Highlight

Streamline Azure Storage operations with ready-to-use helpers for blob storage and queue management. Includes SAS token generation, blob operations, and queue client management.

Key Features:

  • ✅ Blob upload/download with metadata support
  • ✅ SAS token generation for secure blob access
  • ✅ Container management (create, delete, list)
  • ✅ Multiple queue client support with key-based registration
  • ✅ Managed Identity (TokenCredential) support
  • ✅ Base64 encoding support for queue messages

📚 Explain

This package provides two main helpers for Azure Storage operations:

  1. PersistentService - Comprehensive blob storage operations

    • Upload/download blobs
    • Generate SAS URIs for secure access
    • Container management
    • Blob copying and deletion
    • Metadata support
  2. QueuesService - Queue client management

    • Multiple queue support per application
    • Key-based queue client registration
    • Base64 message encoding option

Main Components:

  • RegisterAzureStorage() - Registers BlobServiceClient and QueueClients
  • PersistentService - Scoped service for blob operations
  • QueuesService - Keyed service for queue operations

🔗 Resources

⚙️ Operation

Installation
dotnet add package MDev.Dotnet.Azure.StorageAccount
dotnet add package Azure.Identity
Register Storage Account in Program.cs
using Azure.Identity;
using MDev.Dotnet.Azure.StorageAccount.Startup;

var builder = WebApplication.CreateBuilder(args);

// Use DefaultAzureCredential for managed identity
var credential = new DefaultAzureCredential();

builder.RegisterAzureStorage(credential);

var app = builder.Build();
app.Run();
appsettings.json Configuration
{
  "StorageAccount": {
    "BlobsEndpoint": "https://yourstorageaccount.blob.core.windows.net",
    "QueuesEndpoint": "https://yourstorageaccount.queue.core.windows.net",
    "QueueMessagesEncodeBase64": false,
    "Queues": [
      {
        "Id": "orders",
        "Queues": ["order-processing", "order-notifications"]
      },
      {
        "Id": "reports",
        "Queues": ["report-generation"]
      }
    ]
  }
}
Using PersistentService for Blob Operations
using MDev.Dotnet.Azure.StorageAccount.Helpers;

public class FileStorageService
{
    private readonly PersistentService _persistentService;
    private readonly ILogger<FileStorageService> _logger;
    
    public FileStorageService(
        PersistentService persistentService,
        ILogger<FileStorageService> logger)
    {
        _persistentService = persistentService;
        _logger = logger;
    }
    
    // Upload a file
    public async Task<string> UploadFileAsync(
        Stream fileStream, 
        string fileName,
        string containerName = "documents")
    {
        var metadata = new Dictionary<string, string>
        {
            { "UploadedBy", "user@example.com" },
            { "UploadDate", DateTime.UtcNow.ToString("O") }
        };
        
        await _persistentService.SaveOnBlobAsync(
            containerName, 
            fileStream, 
            fileName,
            metadata);
            
        return await _persistentService.RetreiveBlobUriAsync(
            containerName, 
            fileName);
    }
    
    // Download a file
    public async Task<Stream> DownloadFileAsync(
        string containerName, 
        string fileName)
    {
        return await _persistentService.DownloadBlobContentAsync(
            containerName, 
            fileName);
    }
    
    // Generate secure access URL
    public async Task<string> GetSecureUrlAsync(
        string containerName, 
        string fileName)
    {
        return await _persistentService.RetreiveBlobUriAsync(
            containerName, 
            fileName);
    }
    
    // Delete a file
    public async Task DeleteFileAsync(
        string containerName, 
        string fileName)
    {
        await _persistentService.DeleteBlobIfExistsAsync(
            containerName, 
            fileName);
    }
    
    // List all files with prefix
    public async Task<List<string>> ListFilesAsync(
        string containerName, 
        string prefix)
    {
        return await _persistentService.RetreiveBlobsUriAsync(
            containerName, 
            prefix);
    }
    
    // Copy a file
    public async Task CopyFileAsync(
        string sourceContainer,
        string sourceFileName,
        string destContainer,
        string destFileName)
    {
        await _persistentService.CopyBlocFileAsync(
            sourceContainer,
            sourceFileName,
            destContainer,
            destFileName);
    }
}
Using QueuesService for Queue Operations
using MDev.Dotnet.Azure.StorageAccount.Helpers;

public class OrderProcessingService
{
    private readonly QueuesService _ordersQueue;
    private readonly ILogger<OrderProcessingService> _logger;
    
    // Inject using keyed service
    public OrderProcessingService(
        [FromKeyedServices("orders")] QueuesService ordersQueue,
        ILogger<OrderProcessingService> logger)
    {
        _ordersQueue = ordersQueue;
        _logger = logger;
    }
    
    public async Task QueueOrderAsync(string orderId)
    {
        var message = new OrderMessage
        {
            OrderId = orderId,
            Timestamp = DateTime.UtcNow
        };
        
        var json = JsonSerializer.Serialize(message);
        
        // Send to first queue in the "orders" group
        await _ordersQueue.Clients[0].SendMessageAsync(json);
        
        _logger.LogInformation("Order {OrderId} queued", orderId);
    }
}

public class OrderMessage
{
    public string OrderId { get; set; }
    public DateTime Timestamp { get; set; }
}
Complete Example: File Upload API
[ApiController]
[Route("api/[controller]")]
public class FilesController : ControllerBase
{
    private readonly PersistentService _persistentService;
    
    public FilesController(PersistentService persistentService)
    {
        _persistentService = persistentService;
    }
    
    [HttpPost("upload")]
    public async Task<IActionResult> UploadFile(IFormFile file)
    {
        if (file == null || file.Length == 0)
            return BadRequest("No file uploaded");
            
        using var stream = file.OpenReadStream();
        
        var fileName = $"{Guid.NewGuid()}-{file.FileName}";
        var metadata = new Dictionary<string, string>
        {
            { "OriginalName", file.FileName },
            { "ContentType", file.ContentType },
            { "Size", file.Length.ToString() }
        };
        
        await _persistentService.SaveOnBlobAsync(
            "uploads",
            stream,
            fileName,
            metadata);
            
        var url = await _persistentService.RetreiveBlobUriAsync(
            "uploads",
            fileName);
            
        return Ok(new { Url = url, FileName = fileName });
    }
    
    [HttpGet("download/{fileName}")]
    public async Task<IActionResult> DownloadFile(string fileName)
    {
        var stream = await _persistentService.DownloadBlobContentAsync(
            "uploads",
            fileName);
            
        if (stream == Stream.Null)
            return NotFound();
            
        return File(stream, "application/octet-stream", fileName);
    }
    
    [HttpDelete("{fileName}")]
    public async Task<IActionResult> DeleteFile(string fileName)
    {
        await _persistentService.DeleteBlobIfExistsAsync(
            "uploads",
            fileName);
            
        return NoContent();
    }
}

Additional Resources


Support / Contribute

If you have any question, problem or suggestion, create an issue or fork the project and create a Pull Request.

Build Status

Quality Gate Status .NET

License

This project is licensed under the terms specified in the LICENSE file.


Made with ❤️ for the .NET Community

Product Compatible and additional computed target framework versions.
.NET net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.14.0 76 2/8/2026
1.13.0 416 12/10/2025
1.13.0-preview-080 424 12/10/2025
1.12.0 177 11/24/2025
1.12.0-preview-078 265 11/13/2025
1.11.0 1,612 8/3/2025
1.11.0-preview-076 149 7/29/2025
1.10.0 576 7/22/2025
1.10.0-preview-074 539 7/22/2025
1.10.0-preview-073 713 7/20/2025
1.10.0-preview-072 345 7/20/2025
1.10.0-preview-071 155 7/18/2025
1.10.0-preview-069 174 7/16/2025
1.9.0 181 7/15/2025
1.9.0-preview-067 184 7/13/2025
1.8.0 186 6/17/2025
1.8.0-preview-065 178 6/17/2025
1.8.0-preview-064 209 6/16/2025
1.8.0-preview-063 179 6/16/2025
1.8.0-preview-062 174 6/16/2025
1.7.0 967 4/23/2025
1.7.0-preview-060 339 4/23/2025
1.7.0-preview-059 203 4/23/2025
1.6.0 222 4/21/2025
1.6.0-preview-057 199 4/21/2025
1.5.0 217 4/21/2025
1.5.0-preview-054 192 4/21/2025
1.5.0-preview-053 212 4/20/2025
1.4.0 739 4/2/2025
1.4.0-preview-051 189 4/2/2025
1.4.0-preview-050 196 4/2/2025
1.3.0 202 4/1/2025
1.3.0-preview-048 130 3/29/2025
1.2.0 506 3/24/2025
1.2.0-preview-046 692 2/26/2025
1.1.0 342 2/19/2025
1.1.0-preview-044 137 2/19/2025
1.1.0-preview-043 212 2/19/2025
1.1.0-preview-042 146 2/19/2025
1.1.0-preview-041 145 2/13/2025
1.1.0-preview-040 188 1/18/2025
1.1.0-preview-037 512 1/12/2025
1.1.0-preview-036 271 12/5/2024
1.0.0 781 11/17/2024
1.0.0-preview-024 128 11/17/2024
1.0.0-preview-023 291 11/14/2024
1.0.0-preview-022 116 11/7/2024
1.0.0-preview-020 84 11/7/2024
1.0.0-preview-019 389 10/21/2024
1.0.0-preview-017 92 10/19/2024
1.0.0-preview-016 137 10/19/2024
1.0.0-preview-015 175 10/18/2024
1.0.0-preview-013 586 8/19/2024
1.0.0-preview-012 299 8/18/2024
1.0.0-preview-011 274 7/23/2024
1.0.0-preview-010 130 7/23/2024
1.0.0-preview-009 156 7/18/2024
1.0.0-preview-008 129 7/18/2024
1.0.0-preview-007 129 7/18/2024
1.0.0-preview-006 133 7/17/2024
1.0.0-preview-005 144 7/17/2024
1.0.0-preview-004 129 7/16/2024
1.0.0-preview-003 116 7/16/2024
1.0.0-preview-002 133 7/16/2024