MDev.Dotnet.Azure.StorageAccount 1.14.0

dotnet add package MDev.Dotnet.Azure.StorageAccount --version 1.14.0
                    
NuGet\Install-Package MDev.Dotnet.Azure.StorageAccount -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.StorageAccount" 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.StorageAccount" Version="1.14.0" />
                    
Directory.Packages.props
<PackageReference Include="MDev.Dotnet.Azure.StorageAccount" />
                    
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.StorageAccount --version 1.14.0
                    
#r "nuget: MDev.Dotnet.Azure.StorageAccount, 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.StorageAccount@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.StorageAccount&version=1.14.0
                    
Install as a Cake Addin
#tool nuget:?package=MDev.Dotnet.Azure.StorageAccount&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 39 2/8/2026
1.13.0 435 12/10/2025
1.13.0-preview-080 423 12/10/2025
1.12.0 178 11/24/2025
1.12.0-preview-078 267 11/13/2025
1.11.0 1,179 8/3/2025
1.11.0-preview-076 129 7/29/2025
1.10.0 567 7/22/2025
1.10.0-preview-074 556 7/22/2025
1.10.0-preview-073 663 7/20/2025
1.10.0-preview-072 325 7/20/2025
1.10.0-preview-071 132 7/18/2025
1.10.0-preview-069 172 7/16/2025
1.9.0 180 7/15/2025
1.9.0-preview-067 185 7/13/2025
1.8.0 180 6/17/2025
1.8.0-preview-065 173 6/17/2025
1.8.0-preview-064 210 6/16/2025
1.8.0-preview-063 174 6/16/2025
1.8.0-preview-062 148 6/16/2025
1.7.0 880 4/23/2025
1.7.0-preview-060 308 4/23/2025
1.7.0-preview-059 178 4/23/2025
1.6.0 229 4/21/2025
1.6.0-preview-057 185 4/21/2025
1.5.0 216 4/21/2025
1.5.0-preview-054 191 4/21/2025
1.5.0-preview-053 191 4/20/2025
1.4.0 681 4/2/2025
1.4.0-preview-051 173 4/2/2025
1.4.0-preview-050 198 4/2/2025
1.3.0 203 4/1/2025
1.3.0-preview-048 138 3/29/2025
1.2.0 500 3/24/2025
1.2.0-preview-046 434 2/26/2025
1.1.0 346 2/19/2025
1.1.0-preview-044 132 2/19/2025
1.1.0-preview-043 186 2/19/2025
1.1.0-preview-042 128 2/19/2025
1.1.0-preview-041 140 2/13/2025
1.1.0-preview-040 136 1/18/2025
1.1.0-preview-037 454 1/12/2025
1.1.0-preview-036 287 12/5/2024
1.0.0 605 11/17/2024
1.0.0-preview-024 116 11/17/2024
1.0.0-preview-023 129 11/14/2024
1.0.0-preview-022 118 11/7/2024
1.0.0-preview-020 78 11/7/2024
1.0.0-preview-019 376 10/21/2024
1.0.0-preview-017 102 10/19/2024
1.0.0-preview-016 207 10/19/2024
1.0.0-preview-015 164 10/18/2024
1.0.0-preview-013 407 8/19/2024
1.0.0-preview-012 286 8/18/2024
1.0.0-preview-011 256 7/23/2024
1.0.0-preview-010 136 7/23/2024
1.0.0-preview-009 146 7/18/2024
1.0.0-preview-008 132 7/18/2024
1.0.0-preview-007 126 7/18/2024
1.0.0-preview-006 127 7/17/2024
1.0.0-preview-005 123 7/17/2024
1.0.0-preview-004 130 7/16/2024
1.0.0-preview-003 118 7/16/2024
1.0.0-preview-002 130 7/16/2024