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
<PackageReference Include="Persilsoft.Storage.Factory" Version="1.0.2" />
<PackageVersion Include="Persilsoft.Storage.Factory" Version="1.0.2" />
<PackageReference Include="Persilsoft.Storage.Factory" />
paket add Persilsoft.Storage.Factory --version 1.0.2
#r "nuget: Persilsoft.Storage.Factory, 1.0.2"
#:package Persilsoft.Storage.Factory@1.0.2
#addin nuget:?package=Persilsoft.Storage.Factory&version=1.0.2
#tool nuget:?package=Persilsoft.Storage.Factory&version=1.0.2
Persilsoft.Storage.Factory
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.MinioPersilsoft.Storage.AzureBlobPersilsoft.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:
- Persilsoft.Storage.Minio - MinIO storage
- Persilsoft.Storage.AzureBlob - Azure Blob Storage
- Persilsoft.Storage.S3 - AWS S3
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 | Versions 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. |
-
net10.0
- Persilsoft.Storage.AzureBlob (>= 1.0.0)
- Persilsoft.Storage.Minio (>= 1.0.5)
- Persilsoft.Storage.S3 (>= 1.0.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
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