Persilsoft.Storage.Factory 1.0.2

dotnet add package Persilsoft.Storage.Factory --version 1.0.2
                    
NuGet\Install-Package Persilsoft.Storage.Factory -Version 1.0.2
                    
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="Persilsoft.Storage.Factory" Version="1.0.2" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Persilsoft.Storage.Factory" Version="1.0.2" />
                    
Directory.Packages.props
<PackageReference Include="Persilsoft.Storage.Factory" />
                    
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 Persilsoft.Storage.Factory --version 1.0.2
                    
#r "nuget: Persilsoft.Storage.Factory, 1.0.2"
                    
#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 Persilsoft.Storage.Factory@1.0.2
                    
#: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=Persilsoft.Storage.Factory&version=1.0.2
                    
Install as a Cake Addin
#tool nuget:?package=Persilsoft.Storage.Factory&version=1.0.2
                    
Install as a Cake Tool

Persilsoft.Storage.Factory

NuGet NuGet Downloads License: MIT .NET 8

Factory pattern implementation for Persilsoft Storage Libraries. Provides a unified interface to create and manage storage services across multiple cloud providers (MinIO, Azure Blob Storage, AWS S3). Simplifies multi-cloud storage architecture and enables provider switching at runtime.

โœจ Features

  • ๐Ÿญ Factory Pattern - Clean abstraction for creating storage services
  • โ˜๏ธ Multi-Cloud Support - MinIO, Azure Blob Storage, and AWS S3
  • ๐Ÿ”„ Runtime Provider Switching - Change providers dynamically
  • โš™๏ธ Configuration-Based - Provider selection via configuration
  • ๐Ÿ”Œ Dependency Injection - First-class DI support
  • ๐ŸŽฏ Strongly Typed - Type-safe service creation
  • ๐Ÿ“ Comprehensive Logging - Built-in logging support
  • ๐Ÿ›ก๏ธ Production Ready - Memory-safe and fully tested

๐Ÿ“ฆ Installation

dotnet add package Persilsoft.Storage.Factory

This package includes all storage providers:

  • Persilsoft.Storage.Minio
  • Persilsoft.Storage.AzureBlob
  • Persilsoft.Storage.S3

๐Ÿš€ Quick Start

1. Configuration

Add storage settings to your appsettings.json:

{
  "ObjectStorageOptions": {
    "DefaultProvider": "MinIO",
    "MinIO": {
      "Endpoint": "localhost:9000",
      "PublicEndpoint": "storage.example.com",
      "AccessKey": "your-minio-key",
      "SecretKey": "your-minio-secret",
      "WithSSL": true
    },
    "Azure": {
      "ConnectionString": "DefaultEndpointsProtocol=https;AccountName=...",
      "AccountName": "mystorageaccount"
    },
    "S3": {
      "AccessKey": "your-aws-access-key",
      "SecretKey": "your-aws-secret-key",
      "Region": "us-east-1"
    }
  }
}

2. Register Services

In your Program.cs or Startup.cs:

using Persilsoft.Storage.Factory;

// Register all storage providers and factory
builder.Services.AddObjectStorage(builder.Configuration);

3. Use the Factory

Option 1: Use Specific Provider
public class DocumentService
{
    private readonly IObjectStorageFactory _factory;
    
    public DocumentService(IObjectStorageFactory factory)
    {
        _factory = factory;
    }
    
    public async Task SaveToMinIO(Stream file, string fileName)
    {
        var storage = _factory.CreateService("minio");
        
        var url = await storage.UploadFileAsync(
            file,
            "documents",
            fileName,
            "application/pdf"
        );
        
        return url;
    }
    
    public async Task SaveToAzure(Stream file, string fileName)
    {
        var storage = _factory.CreateService("azure");
        
        var url = await storage.UploadFileAsync(
            file,
            "documents",
            fileName,
            "application/pdf"
        );
        
        return url;
    }
}
Option 2: Use Default Provider
public class DocumentService
{
    private readonly IObjectStorageService _storage;
    
    // Automatically injects the default provider (configured in appsettings)
    public DocumentService(IObjectStorageService storage)
    {
        _storage = storage;
    }
    
    public async Task<string> SaveDocument(Stream file, string fileName)
    {
        var url = await _storage.UploadFileAsync(
            file,
            "documents",
            fileName,
            "application/pdf"
        );
        
        return url;
    }
}
Option 3: Dynamic Provider Selection
public class SmartStorageService
{
    private readonly IObjectStorageFactory _factory;
    
    public SmartStorageService(IObjectStorageFactory factory)
    {
        _factory = factory;
    }
    
    public async Task<string> SaveFile(
        Stream file, 
        string fileName, 
        StorageProvider provider)
    {
        // Select provider at runtime
        var providerName = provider switch
        {
            StorageProvider.MinIO => "minio",
            StorageProvider.Azure => "azure",
            StorageProvider.S3 => "s3",
            _ => throw new ArgumentException("Invalid provider")
        };
        
        var storage = _factory.CreateService(providerName);
        
        return await storage.UploadFileAsync(
            file,
            "documents",
            fileName,
            "application/octet-stream"
        );
    }
}

๐Ÿ“š Detailed Usage Examples

Multi-Cloud Backup Strategy

Upload to primary storage and backup to secondary:

public class BackupService
{
    private readonly IObjectStorageFactory _factory;
    private readonly ILogger<BackupService> _logger;
    
    public BackupService(
        IObjectStorageFactory factory,
        ILogger<BackupService> logger)
    {
        _factory = factory;
        _logger = logger;
    }
    
    public async Task<BackupResult> SaveWithBackup(
        Stream file, 
        string fileName)
    {
        var result = new BackupResult();
        
        // Save to primary (MinIO)
        try
        {
            var minioStorage = _factory.CreateService("minio");
            result.PrimaryUrl = await minioStorage.UploadFileAsync(
                file,
                "documents",
                fileName,
                "application/pdf"
            );
            _logger.LogInformation("Saved to primary storage: {Url}", result.PrimaryUrl);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Failed to save to primary storage");
            throw;
        }
        
        // Backup to secondary (Azure)
        try
        {
            file.Position = 0; // Reset stream
            
            var azureStorage = _factory.CreateService("azure");
            result.BackupUrl = await azureStorage.UploadFileAsync(
                file,
                "documents-backup",
                fileName,
                "application/pdf"
            );
            _logger.LogInformation("Saved to backup storage: {Url}", result.BackupUrl);
        }
        catch (Exception ex)
        {
            _logger.LogWarning(ex, "Failed to save to backup storage");
            // Don't fail - backup is optional
        }
        
        return result;
    }
}

Configuration-Driven Provider Selection

public class ConfigurableStorageService
{
    private readonly IObjectStorageFactory _factory;
    private readonly IConfiguration _configuration;
    
    public ConfigurableStorageService(
        IObjectStorageFactory factory,
        IConfiguration configuration)
    {
        _factory = factory;
        _configuration = configuration;
    }
    
    public async Task<string> SaveFile(Stream file, string fileName)
    {
        // Get provider from environment-specific configuration
        var provider = _configuration["StorageProvider"] ?? "minio";
        
        var storage = _factory.CreateService(provider);
        
        return await storage.UploadFileAsync(
            file,
            "uploads",
            fileName,
            "application/octet-stream"
        );
    }
}

Feature Flags with Provider Switching

public class FeatureFlagStorageService
{
    private readonly IObjectStorageFactory _factory;
    private readonly IFeatureManager _featureManager;
    
    public FeatureFlagStorageService(
        IObjectStorageFactory factory,
        IFeatureManager featureManager)
    {
        _factory = factory;
        _featureManager = featureManager;
    }
    
    public async Task<string> SaveFile(Stream file, string fileName)
    {
        // Use S3 if feature flag is enabled, otherwise use MinIO
        var useS3 = await _featureManager.IsEnabledAsync("UseAwsS3");
        var provider = useS3 ? "s3" : "minio";
        
        var storage = _factory.CreateService(provider);
        
        return await storage.UploadFileAsync(
            file,
            "uploads",
            fileName,
            "application/octet-stream"
        );
    }
}

Cost Optimization by File Size

public class CostOptimizedStorageService
{
    private readonly IObjectStorageFactory _factory;
    
    public CostOptimizedStorageService(IObjectStorageFactory factory)
    {
        _factory = factory;
    }
    
    public async Task<string> SaveFile(Stream file, string fileName)
    {
        // Use MinIO for large files (cheaper), Azure for small files (faster)
        var fileSizeMB = file.Length / 1024 / 1024;
        var provider = fileSizeMB > 100 ? "minio" : "azure";
        
        var storage = _factory.CreateService(provider);
        
        return await storage.UploadFileAsync(
            file,
            "uploads",
            fileName,
            "application/octet-stream"
        );
    }
}

Tenant-Specific Storage

public class TenantStorageService
{
    private readonly IObjectStorageFactory _factory;
    
    public TenantStorageService(IObjectStorageFactory factory)
    {
        _factory = factory;
    }
    
    public async Task<string> SaveForTenant(
        Stream file, 
        string fileName, 
        string tenantId)
    {
        // Different tenants use different storage providers
        var provider = GetProviderForTenant(tenantId);
        var storage = _factory.CreateService(provider);
        
        return await storage.UploadFileAsync(
            file,
            $"tenant-{tenantId}",
            fileName,
            "application/octet-stream"
        );
    }
    
    private string GetProviderForTenant(string tenantId)
    {
        // Example: Premium tenants get Azure, others get MinIO
        return tenantId.StartsWith("premium-") ? "azure" : "minio";
    }
}

โš™๏ธ Configuration Options

General Configuration

{
  "ObjectStorageOptions": {
    "DefaultProvider": "MinIO",
    "EnableMultipleProviders": true
  }
}
Option Type Default Description
DefaultProvider string "MinIO" Default storage provider to use
EnableMultipleProviders bool false Enable simultaneous use of multiple providers

Provider-Specific Configuration

Each provider has its own configuration section. See individual provider documentation:

๐Ÿญ Supported Providers

MinIO

var storage = _factory.CreateService("minio");
  • Self-hosted S3-compatible storage
  • High performance
  • Cost effective
  • Documentation

Azure Blob Storage

var storage = _factory.CreateService("azure");
// or
var storage = _factory.CreateService("azureblob");
  • Microsoft Azure cloud storage
  • Global CDN integration
  • Enterprise features
  • Documentation

AWS S3

var storage = _factory.CreateService("s3");
// or
var storage = _factory.CreateService("aws");
  • Amazon Web Services storage
  • Industry standard
  • Extensive ecosystem
  • Documentation

๐Ÿ” Security Best Practices

Use User Secrets in Development

dotnet user-secrets set "ObjectStorageOptions:MinIO:AccessKey" "dev-key"
dotnet user-secrets set "ObjectStorageOptions:MinIO:SecretKey" "dev-secret"

Use Environment Variables in Production

export ObjectStorageOptions__MinIO__AccessKey="prod-key"
export ObjectStorageOptions__MinIO__SecretKey="prod-secret"

Use Azure Key Vault / AWS Secrets Manager

builder.Configuration.AddAzureKeyVault(...);
// or
builder.Configuration.AddSecretsManager(...);

๐Ÿงช Testing

Unit Testing with Factory

[Fact]
public async Task SaveFile_UsesCorrectProvider()
{
    // Arrange
    var services = new ServiceCollection();
    var configuration = new ConfigurationBuilder()
        .AddInMemoryCollection(new Dictionary<string, string>
        {
            ["ObjectStorageOptions:DefaultProvider"] = "MinIO",
            ["ObjectStorageOptions:MinIO:Endpoint"] = "localhost:9000",
            // ... other config
        })
        .Build();
    
    services.AddObjectStorage(configuration);
    var provider = services.BuildServiceProvider();
    
    var factory = provider.GetRequiredService<IObjectStorageFactory>();
    
    // Act
    var storage = factory.CreateService("minio");
    
    // Assert
    Assert.NotNull(storage);
    Assert.IsType<MinioObjectStorageService>(storage);
}

Integration Testing

public class StorageIntegrationTests : IClassFixture<WebApplicationFactory<Program>>
{
    private readonly WebApplicationFactory<Program> _factory;
    
    public StorageIntegrationTests(WebApplicationFactory<Program> factory)
    {
        _factory = factory;
    }
    
    [Fact]
    public async Task UploadFile_WithMinIO_Succeeds()
    {
        // Arrange
        var client = _factory.CreateClient();
        var content = new MultipartFormDataContent();
        var fileContent = new ByteArrayContent(Encoding.UTF8.GetBytes("test"));
        content.Add(fileContent, "file", "test.txt");
        
        // Act
        var response = await client.PostAsync("/api/files", content);
        
        // Assert
        response.EnsureSuccessStatusCode();
    }
}

๐Ÿ“‹ Requirements

  • .NET 10.0 or later
  • At least one storage provider configured

๐Ÿ”— Provider Packages

This factory works with the following providers:

All provider packages are automatically included when you install the Factory package.

๐ŸŽฏ Use Cases

When to Use the Factory

โœ… Good Use Cases:

  • Multi-cloud applications
  • Provider migration scenarios
  • Tenant-specific storage requirements
  • Cost optimization strategies
  • Feature flags for storage selection
  • Backup and redundancy scenarios

โŒ When NOT to Use:

  • Single provider applications (use provider package directly)
  • Simple storage needs
  • Performance-critical paths where DI overhead matters

๐Ÿš€ Migration Guide

From Direct Provider Usage

Before:

public class MyService
{
    private readonly MinioObjectStorageService _storage;
    
    public MyService(MinioObjectStorageService storage)
    {
        _storage = storage;
    }
}

After:

public class MyService
{
    private readonly IObjectStorageFactory _factory;
    
    public MyService(IObjectStorageFactory factory)
    {
        _factory = factory;
    }
    
    public async Task DoWork()
    {
        var storage = _factory.CreateService("minio");
        // Use storage
    }
}

๐Ÿ“– Documentation

For more information:

๐Ÿ› Issues & Support

Found a bug or have a feature request?

๐Ÿ“„ License

This project is licensed under the MIT License - see the LICENSE file for details.

๐Ÿ‘ค Author

Edinson Aldaz - Persilsoft

๐Ÿ™ Acknowledgments

Built on top of:


Made with โค๏ธ by Persilsoft

Product Compatible and additional computed target framework versions.
.NET 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.0.2 100 12/12/2025
1.0.1 282 12/8/2025
1.0.0 281 12/7/2025

v1.0.2 Release Notes:
     - Fixed critical memory leak in service resolution
     - Changed Factory lifecycle from Singleton to Scoped for correct DI behavior
     - Improved service provider pattern implementation
     - Enhanced logging for service creation
     - Added comprehensive XML documentation
     - Better exception messages and parameter validation