
Previous article: AI Integration Boom: Infusing ASP.NET Core with ML.NET & OpenAI (Part-24 of 40)
Table of Contents
Introduction to Cloud-Native ASP.NET Core
Azure Cloud Services Deep Dive
AWS Cloud Integration Mastery
Global Scaling Strategies
Cloud Storage Solutions
Microservices in Cloud
Production Deployment Patterns
Real-World Case Study
Best Practices & Optimization
1. Introduction to Cloud-Native ASP.NET Core
1.1 What is Cloud-Native Development?
Cloud-native development represents a paradigm shift in how we build, deploy, and scale applications. Unlike traditional approaches, cloud-native applications are designed from the ground up to leverage cloud infrastructure, enabling unprecedented scalability, resilience, and flexibility.
Key Characteristics:
Microservices architecture
Containerization
Dynamic orchestration
DevOps continuous delivery
Infrastructure as code
1.2 Why ASP.NET Core for Cloud?
ASP.NET Core has evolved into the perfect framework for cloud-native development:
// Program.cs - Modern ASP.NET Core cloud-ready setup
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Configuration;
using Azure.Identity;
using Amazon.Extensions.NETCore.Setup;
var builder = WebApplication.CreateBuilder(args);
// Cloud-optimized configuration
builder.Configuration
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddEnvironmentVariables() // Critical for cloud deployment
.AddUserSecrets<Program>() // For local development
.AddAzureKeyVault( // For production secrets
new Uri(builder.Configuration["KeyVault:Endpoint"]),
new DefaultAzureCredential());
// Cloud-optimized services
builder.Services.AddControllers();
builder.Services.AddHealthChecks(); // Essential for cloud monitoring
builder.Services.AddApplicationInsightsTelemetry();
// Configure for cloud scalability
builder.Services.Configure<HostOptions>(options =>
{
options.ShutdownTimeout = TimeSpan.FromSeconds(30); // Graceful shutdown
});
var app = builder.Build();
// Cloud-specific middleware pipeline
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error"); // Production error handling
app.UseHsts(); // HTTP Strict Transport Security
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
// Health check endpoint for cloud load balancers
app.MapHealthChecks("/health");
app.MapControllers();
await app.RunAsync();
1.3 Real-World Business Impact
Scenario : E-commerce Platform Scaling for Holiday Season
Imagine an e-commerce platform that normally handles 10,000 daily users but needs to scale to 1,000,000 users during Black Friday sales. Traditional infrastructure would collapse under this load, but cloud-native ASP.NET Core applications can scale dynamically.
// Startup configuration for auto-scaling
public void ConfigureServices(IServiceCollection services)
{
// Configure for horizontal scaling
services.AddDistributedMemoryCache(); // Use Redis in production
// Stateless design for scale-out
services.Configure<CookiePolicyOptions>(options =>
{
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
// Add session with distributed cache support
services.AddSession(options =>
{
options.Cookie.HttpOnly = true;
options.Cookie.IsEssential = true;
options.IdleTimeout = TimeSpan.FromMinutes(20);
});
}
2. Azure Cloud Services Deep Dive
2.1 Azure App Service for ASP.NET Core
Azure App Service provides fully managed platform for deploying web applications with built-in scaling, security, and monitoring.
2.1.1 Deployment Configuration
appsettings.json for Azure
{
"Azure": {
"AppService": {
"InstanceId": "{WEBSITE_INSTANCE_ID}",
"SKU": "{WEBSITE_SKU}",
"ResourceGroup": "{WEBSITE_RESOURCE_GROUP}"
}
},
"ConnectionStrings": {
"AzureSQL": "Server=tcp:{server}.database.windows.net;Database={db};User ID={user};Password={password};Trusted_Connection=False;Encrypt=True;"
},
"ApplicationInsights": {
"InstrumentationKey": "{your_instrumentation_key}"
}
}
2.1.2 Program.cs Configuration for Azure
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Azure.Identity;
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((context, config) =>
{
var builtConfig = config.Build();
// Azure App Configuration for centralized configuration management
if (!context.HostingEnvironment.IsDevelopment())
{
config.AddAzureAppConfiguration(options =>
{
options.Connect(builtConfig["ConnectionStrings:AppConfig"])
.ConfigureKeyVault(kv =>
{
kv.SetCredential(new DefaultAzureCredential());
});
});
}
})
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
// Azure-specific configurations
webBuilder.ConfigureKestrel(serverOptions =>
{
serverOptions.Limits.MaxRequestBodySize = 52428800; // 50MB
serverOptions.Limits.MaxConcurrentConnections = 100;
serverOptions.Limits.MaxConcurrentUpgradedConnections = 100;
});
})
.UseAzureAppServices() // Azure-specific logging and diagnostics
.ConfigureLogging((context, logging) =>
{
logging.AddAzureWebAppDiagnostics();
});
}
2.1.3 Advanced Azure Deployment with Bicep
main.bicep - Infrastructure as Code:
param location string = 'eastus'
param appName string = 'my-aspnet-app'
param skuName string = 'S1'
param skuCapacity int = 2
resource appServicePlan 'Microsoft.Web/serverfarms@2021-03-01' = {
name: 'plan-${appName}'
location: location
sku: {
name: skuName
capacity: skuCapacity
}
properties: {
reserved: true
}
}
resource webApp 'Microsoft.Web/sites@2021-03-01' = {
name: appName
location: location
properties: {
serverFarmId: appServicePlan.id
siteConfig: {
netFrameworkVersion: 'v6.0'
alwaysOn: true
appSettings: [
{
name: 'APPINSIGHTS_INSTRUMENTATIONKEY'
value: appInsights.properties.InstrumentationKey
}
]
}
httpsOnly: true
}
identity: {
type: 'SystemAssigned'
}
}
resource appInsights 'Microsoft.Insights/components@2020-02-02' = {
name: 'ai-${appName}'
location: location
properties: {
Application_Type: 'web'
Flow_Type: 'Bluefield'
}
}
2.2 Azure SQL Database Integration
2.2.1 Entity Framework Core with Azure SQL
// DataContext.cs
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
public class ApplicationDbContext : DbContext
{
private readonly IConfiguration _configuration;
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options,
IConfiguration configuration)
: base(options)
{
_configuration = configuration;
}
public DbSet<Product> Products { get; set; }
public DbSet<Order> Orders { get; set; }
public DbSet<Customer> Customers { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
// Use retry logic for cloud database
optionsBuilder.UseSqlServer(
_configuration.GetConnectionString("AzureSQL"),
options =>
{
options.EnableRetryOnFailure(
maxRetryCount: 5,
maxRetryDelay: TimeSpan.FromSeconds(30),
errorNumbersToAdd: null);
});
}
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// Configure for cloud performance
modelBuilder.Entity<Product>(entity =>
{
entity.HasKey(e => e.Id);
entity.HasIndex(e => e.CategoryId); // Important for query performance
entity.Property(e => e.Price).HasColumnType("decimal(18,2)");
});
// Configure soft delete pattern
modelBuilder.Entity<Customer>(entity =>
{
entity.HasQueryFilter(e => !e.IsDeleted);
});
}
}
// Startup configuration
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>((serviceProvider, options) =>
{
var configuration = serviceProvider.GetRequiredService<IConfiguration>();
options.UseSqlServer(
configuration.GetConnectionString("AzureSQL"),
sqlOptions =>
{
sqlOptions.EnableRetryOnFailure(
maxRetryCount: 5,
maxRetryDelay: TimeSpan.FromSeconds(30),
errorNumbersToAdd: null);
});
});
}
2.2.2 Database Resilience with Polly
// DatabaseResilienceService.cs
using Polly;
using Polly.Retry;
using System.Data.SqlClient;
public class DatabaseResilienceService
{
private readonly AsyncRetryPolicy _retryPolicy;
private readonly ILogger<DatabaseResilienceService> _logger;
public DatabaseResilienceService(ILogger<DatabaseResilienceService> logger)
{
_logger = logger;
_retryPolicy = Policy
.Handle<SqlException>(ex => IsTransientError(ex))
.Or<TimeoutException>()
.WaitAndRetryAsync(
retryCount: 3,
sleepDurationProvider: retryAttempt =>
TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)),
onRetry: (exception, timeSpan, retryCount, context) =>
{
_logger.LogWarning(exception,
$"Database operation failed. Retrying attempt {retryCount} after {timeSpan.TotalSeconds}s.");
});
}
public async Task<T> ExecuteWithResilienceAsync<T>(Func<Task<T>> operation)
{
return await _retryPolicy.ExecuteAsync(operation);
}
private bool IsTransientError(SqlException ex)
{
// SQL Server transient error numbers
int[] transientErrors = { 4060, 40197, 40501, 40613,
49918, 49919, 49920, 11001 };
return transientErrors.Contains(ex.Number);
}
}
// Usage in repository
public class ProductRepository
{
private readonly ApplicationDbContext _context;
private readonly DatabaseResilienceService _resilienceService;
public ProductRepository(ApplicationDbContext context,
DatabaseResilienceService resilienceService)
{
_context = context;
_resilienceService = resilienceService;
}
public async Task<List<Product>> GetProductsByCategoryAsync(int categoryId)
{
return await _resilienceService.ExecuteWithResilienceAsync(async () =>
{
return await _context.Products
.Where(p => p.CategoryId == categoryId && p.IsActive)
.OrderBy(p => p.Name)
.ToListAsync();
});
}
}
2.3 Azure Storage Integration
2.3.1 Blob Storage for File Management
// AzureBlobStorageService.cs
using Azure.Storage.Blobs;
using Azure.Storage.Blobs.Models;
using Microsoft.Extensions.Configuration;
public interface IFileStorageService
{
Task<string> UploadFileAsync(Stream fileStream, string fileName, string contentType);
Task<Stream> DownloadFileAsync(string fileName);
Task<bool> DeleteFileAsync(string fileName);
Task<List<string>> ListFilesAsync(string prefix = null);
}
public class AzureBlobStorageService : IFileStorageService
{
private readonly BlobServiceClient _blobServiceClient;
private readonly string _containerName;
private readonly ILogger<AzureBlobStorageService> _logger;
public AzureBlobStorageService(IConfiguration configuration,
ILogger<AzureBlobStorageService> logger)
{
var connectionString = configuration.GetConnectionString("AzureStorage");
_blobServiceClient = new BlobServiceClient(connectionString);
_containerName = configuration["AzureStorage:ContainerName"] ?? "default";
_logger = logger;
InitializeContainerAsync().GetAwaiter().GetResult();
}
private async Task InitializeContainerAsync()
{
try
{
var containerClient = _blobServiceClient.GetBlobContainerClient(_containerName);
await containerClient.CreateIfNotExistsAsync(PublicAccessType.None);
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to initialize blob container");
throw;
}
}
public async Task<string> UploadFileAsync(Stream fileStream, string fileName, string contentType)
{
try
{
var containerClient = _blobServiceClient.GetBlobContainerClient(_containerName);
var blobClient = containerClient.GetBlobClient(fileName);
await blobClient.UploadAsync(fileStream, new BlobHttpHeaders
{
ContentType = contentType
});
return blobClient.Uri.ToString();
}
catch (Exception ex)
{
_logger.LogError(ex, $"Failed to upload file: {fileName}");
throw;
}
}
public async Task<Stream> DownloadFileAsync(string fileName)
{
try
{
var containerClient = _blobServiceClient.GetBlobContainerClient(_containerName);
var blobClient = containerClient.GetBlobClient(fileName);
var response = await blobClient.DownloadAsync();
return response.Value.Content;
}
catch (Exception ex)
{
_logger.LogError(ex, $"Failed to download file: {fileName}");
throw;
}
}
public async Task<bool> DeleteFileAsync(string fileName)
{
try
{
var containerClient = _blobServiceClient.GetBlobContainerClient(_containerName);
var blobClient = containerClient.GetBlobClient(fileName);
return await blobClient.DeleteIfExistsAsync();
}
catch (Exception ex)
{
_logger.LogError(ex, $"Failed to delete file: {fileName}");
throw;
}
}
public async Task<List<string>> ListFilesAsync(string prefix = null)
{
var fileNames = new List<string>();
var containerClient = _blobServiceClient.GetBlobContainerClient(_containerName);
await foreach (var blobItem in containerClient.GetBlobsAsync(prefix: prefix))
{
fileNames.Add(blobItem.Name);
}
return fileNames;
}
}
// Controller usage
[ApiController]
[Route("api/[controller]")]
public class FilesController : ControllerBase
{
private readonly IFileStorageService _fileStorageService;
private readonly ILogger<FilesController> _logger;
public FilesController(IFileStorageService fileStorageService,
ILogger<FilesController> logger)
{
_fileStorageService = fileStorageService;
_logger = logger;
}
[HttpPost("upload")]
public async Task<IActionResult> UploadFile(IFormFile file)
{
if (file == null || file.Length == 0)
return BadRequest("No file uploaded");
try
{
using var stream = file.OpenReadStream();
var fileUrl = await _fileStorageService.UploadFileAsync(
stream,
Guid.NewGuid().ToString() + Path.GetExtension(file.FileName),
file.ContentType);
return Ok(new { FileUrl = fileUrl });
}
catch (Exception ex)
{
_logger.LogError(ex, "File upload failed");
return StatusCode(500, "File upload failed");
}
}
[HttpGet("download/{fileName}")]
public async Task<IActionResult> DownloadFile(string fileName)
{
try
{
var stream = await _fileStorageService.DownloadFileAsync(fileName);
return File(stream, "application/octet-stream", fileName);
}
catch (Exception ex)
{
_logger.LogError(ex, $"File download failed: {fileName}");
return NotFound();
}
}
}
2.3.2 Table Storage for NoSQL Data
// AzureTableStorageService.cs
using Azure.Data.Tables;
using Microsoft.Extensions.Configuration;
public class AzureTableStorageService<T> where T : class, ITableEntity, new()
{
private readonly TableServiceClient _tableServiceClient;
private readonly TableClient _tableClient;
private readonly string _tableName;
private readonly ILogger<AzureTableStorageService<T>> _logger;
public AzureTableStorageService(IConfiguration configuration,
ILogger<AzureTableStorageService<T>> logger)
{
var connectionString = configuration.GetConnectionString("AzureStorage");
_tableServiceClient = new TableServiceClient(connectionString);
_tableName = typeof(T).Name.ToLower() + "s";
_tableClient = _tableServiceClient.GetTableClient(_tableName);
_logger = logger;
InitializeTableAsync().GetAwaiter().GetResult();
}
private async Task InitializeTableAsync()
{
try
{
await _tableClient.CreateIfNotExistsAsync();
}
catch (Exception ex)
{
_logger.LogError(ex, $"Failed to initialize table: {_tableName}");
throw;
}
}
public async Task<T> GetEntityAsync(string partitionKey, string rowKey)
{
try
{
var response = await _tableClient.GetEntityAsync<T>(partitionKey, rowKey);
return response.Value;
}
catch (Exception ex)
{
_logger.LogError(ex, $"Failed to get entity: {partitionKey}/{rowKey}");
return null;
}
}
public async Task UpsertEntityAsync(T entity)
{
try
{
await _tableClient.UpsertEntityAsync(entity);
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to upsert entity");
throw;
}
}
public async Task DeleteEntityAsync(string partitionKey, string rowKey)
{
try
{
await _tableClient.DeleteEntityAsync(partitionKey, rowKey);
}
catch (Exception ex)
{
_logger.LogError(ex, $"Failed to delete entity: {partitionKey}/{rowKey}");
throw;
}
}
public async Task<List<T>> QueryEntitiesAsync(string filter = null)
{
var entities = new List<T>();
try
{
var query = _tableClient.QueryAsync<T>(filter);
await foreach (var entity in query)
{
entities.Add(entity);
}
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to query entities");
throw;
}
return entities;
}
}
// Example entity
public class UserSession : ITableEntity
{
public string PartitionKey { get; set; } // UserId
public string RowKey { get; set; } // SessionId
public DateTimeOffset? Timestamp { get; set; }
public Azure.ETag ETag { get; set; }
public string UserName { get; set; }
public DateTime LoginTime { get; set; }
public DateTime LastActivity { get; set; }
public string IpAddress { get; set; }
}
3. AWS Cloud Integration Mastery
3.1 AWS Elastic Beanstalk for ASP.NET Core
AWS Elastic Beanstalk provides an easy-to-use service for deploying and scaling web applications.
3.1.1 AWS Configuration and Deployment
aws-beanstalk.config
option_settings:
aws:elasticbeanstalk:application:environment:
ASPNETCORE_ENVIRONMENT: Production
AWS_ACCESS_KEY_ID: your-access-key
AWS_SECRET_ACCESS_KEY: your-secret-key
aws:elasticbeanstalk:container:dotnet:app:
AppPath: /var/app/current
aws:elasticbeanstalk:cloudwatch:logs:
StreamLogs: true
RetentionInDays: 30
aws:elasticbeanstalk:healthreporting:system:
SystemType: enhanced
packages:
yum:
nginx: []
dotnet8: []
files:
"/etc/nginx/conf.d/proxy.conf":
content: |
client_max_body_size 50M;
3.1.2 Program.cs for AWS
using Amazon;
using Amazon.Extensions.NETCore.Setup;
using Amazon.Runtime;
using Amazon.S3;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((context, config) =>
{
config.AddEnvironmentVariables();
// AWS Systems Manager Parameter Store for configuration
if (context.HostingEnvironment.IsProduction())
{
var awsOptions = config.Build().GetAWSOptions();
config.AddSystemsManager("/my-app/production", awsOptions,
TimeSpan.FromMinutes(5));
}
})
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
// AWS-specific configurations
webBuilder.ConfigureKestrel(serverOptions =>
{
serverOptions.Limits.MaxRequestBodySize = 52428800; // 50MB
});
})
.ConfigureLogging((context, logging) =>
{
logging.AddAWSProvider(); // CloudWatch logging
if (context.HostingEnvironment.IsDevelopment())
{
logging.AddConsole();
logging.AddDebug();
}
})
.UseDefaultServiceProvider((context, options) =>
{
options.ValidateScopes = context.HostingEnvironment.IsDevelopment();
options.ValidateOnBuild = true;
});
}
// Startup configuration for AWS
public class Startup
{
public Startup(IConfiguration configuration, IWebHostEnvironment environment)
{
Configuration = configuration;
Environment = environment;
}
public IConfiguration Configuration { get; }
public IWebHostEnvironment Environment { get; }
public void ConfigureServices(IServiceCollection services)
{
// AWS service configuration
services.AddDefaultAWSOptions(Configuration.GetAWSOptions());
services.AddAWSService<IAmazonS3>();
services.AddControllersWithViews();
// Health checks for AWS load balancer
services.AddHealthChecks()
.AddS3HealthCheck("s3-health-check")
.AddDynamoDBHealthCheck("dynamodb-health-check");
// AWS Cognito for authentication
services.AddCognitoIdentity();
// AWS X-Ray for distributed tracing
services.AddXRay("my-aspnet-app");
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseXRay("my-aspnet-app");
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
// Health check endpoint for AWS load balancer
app.UseEndpoints(endpoints =>
{
endpoints.MapHealthChecks("/health");
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
}
}
3.2 AWS S3 Integration
3.2.1 S3 File Storage Service
// AWSS3StorageService.cs
using Amazon.S3;
using Amazon.S3.Model;
using Amazon.S3.Transfer;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
public interface IAwsFileStorageService
{
Task<string> UploadFileAsync(Stream fileStream, string fileName, string contentType);
Task<Stream> DownloadFileAsync(string fileName);
Task<bool> DeleteFileAsync(string fileName);
Task<List<S3Object>> ListFilesAsync(string prefix = null);
Task<string> GeneratePresignedUrlAsync(string fileName, DateTime expiration);
}
public class AwsS3StorageService : IAwsFileStorageService
{
private readonly IAmazonS3 _s3Client;
private readonly string _bucketName;
private readonly ILogger<AwsS3StorageService> _logger;
public AwsS3StorageService(IAmazonS3 s3Client, IConfiguration configuration,
ILogger<AwsS3StorageService> logger)
{
_s3Client = s3Client;
_bucketName = configuration["AWS:S3:BucketName"] ?? "my-app-bucket";
_logger = logger;
InitializeBucketAsync().GetAwaiter().GetResult();
}
private async Task InitializeBucketAsync()
{
try
{
var bucketExists = await _s3Client.DoesS3BucketExistAsync(_bucketName);
if (!bucketExists)
{
var putBucketRequest = new PutBucketRequest
{
BucketName = _bucketName,
UseClientRegion = true
};
await _s3Client.PutBucketAsync(putBucketRequest);
}
}
catch (Exception ex)
{
_logger.LogError(ex, $"Failed to initialize S3 bucket: {_bucketName}");
throw;
}
}
public async Task<string> UploadFileAsync(Stream fileStream, string fileName, string contentType)
{
try
{
var transferUtility = new TransferUtility(_s3Client);
var uploadRequest = new TransferUtilityUploadRequest
{
InputStream = fileStream,
Key = fileName,
BucketName = _bucketName,
ContentType = contentType,
CannedACL = S3CannedACL.Private // Security best practice
};
await transferUtility.UploadAsync(uploadRequest);
return $"https://{_bucketName}.s3.amazonaws.com/{fileName}";
}
catch (Exception ex)
{
_logger.LogError(ex, $"Failed to upload file to S3: {fileName}");
throw;
}
}
public async Task<Stream> DownloadFileAsync(string fileName)
{
try
{
var request = new GetObjectRequest
{
BucketName = _bucketName,
Key = fileName
};
using var response = await _s3Client.GetObjectAsync(request);
var memoryStream = new MemoryStream();
await response.ResponseStream.CopyToAsync(memoryStream);
memoryStream.Position = 0;
return memoryStream;
}
catch (AmazonS3Exception ex) when (ex.ErrorCode == "NoSuchKey")
{
_logger.LogWarning($"File not found in S3: {fileName}");
return null;
}
catch (Exception ex)
{
_logger.LogError(ex, $"Failed to download file from S3: {fileName}");
throw;
}
}
public async Task<bool> DeleteFileAsync(string fileName)
{
try
{
var deleteRequest = new DeleteObjectRequest
{
BucketName = _bucketName,
Key = fileName
};
await _s3Client.DeleteObjectAsync(deleteRequest);
return true;
}
catch (Exception ex)
{
_logger.LogError(ex, $"Failed to delete file from S3: {fileName}");
return false;
}
}
public async Task<List<S3Object>> ListFilesAsync(string prefix = null)
{
try
{
var request = new ListObjectsV2Request
{
BucketName = _bucketName,
Prefix = prefix
};
var response = await _s3Client.ListObjectsV2Async(request);
return response.S3Objects;
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to list files from S3");
throw;
}
}
public async Task<string> GeneratePresignedUrlAsync(string fileName, DateTime expiration)
{
try
{
var request = new GetPreSignedUrlRequest
{
BucketName = _bucketName,
Key = fileName,
Expires = expiration,
Verb = HttpVerb.GET
};
return _s3Client.GetPreSignedURL(request);
}
catch (Exception ex)
{
_logger.LogError(ex, $"Failed to generate presigned URL for: {fileName}");
throw;
}
}
}
// Controller for S3 operations
[ApiController]
[Route("api/[controller]")]
public class AwsFilesController : ControllerBase
{
private readonly IAwsFileStorageService _fileStorageService;
private readonly ILogger<AwsFilesController> _logger;
public AwsFilesController(IAwsFileStorageService fileStorageService,
ILogger<AwsFilesController> logger)
{
_fileStorageService = fileStorageService;
_logger = logger;
}
[HttpPost("upload")]
[RequestSizeLimit(50_000_000)] // 50MB limit
public async Task<IActionResult> UploadFile(IFormFile file)
{
if (file == null || file.Length == 0)
return BadRequest("No file uploaded");
try
{
using var stream = file.OpenReadStream();
var fileUrl = await _fileStorageService.UploadFileAsync(
stream,
$"{Guid.NewGuid()}{Path.GetExtension(file.FileName)}",
file.ContentType);
return Ok(new { FileUrl = fileUrl, FileName = file.FileName });
}
catch (Exception ex)
{
_logger.LogError(ex, "S3 file upload failed");
return StatusCode(500, "File upload failed");
}
}
[HttpGet("download/{fileName}")]
public async Task<IActionResult> DownloadFile(string fileName)
{
try
{
var stream = await _fileStorageService.DownloadFileAsync(fileName);
if (stream == null)
return NotFound();
return File(stream, "application/octet-stream", fileName);
}
catch (Exception ex)
{
_logger.LogError(ex, $"S3 file download failed: {fileName}");
return StatusCode(500, "File download failed");
}
}
[HttpGet("presigned-url/{fileName}")]
public async Task<IActionResult> GetPresignedUrl(string fileName, [FromQuery] int expiresInMinutes = 60)
{
try
{
var expiration = DateTime.UtcNow.AddMinutes(expiresInMinutes);
var presignedUrl = await _fileStorageService.GeneratePresignedUrlAsync(fileName, expiration);
return Ok(new { PresignedUrl = presignedUrl, Expires = expiration });
}
catch (Exception ex)
{
_logger.LogError(ex, $"Failed to generate presigned URL for: {fileName}");
return StatusCode(500, "Failed to generate presigned URL");
}
}
}
3.3 AWS RDS with SQL Server
3.3.1 Entity Framework Core with AWS RDS
// AWS RDS Configuration
public class AwsRdsDbContext : DbContext
{
private readonly IConfiguration _configuration;
private readonly ILogger<AwsRdsDbContext> _logger;
public AwsRdsDbContext(DbContextOptions<AwsRdsDbContext> options,
IConfiguration configuration,
ILogger<AwsRdsDbContext> logger)
: base(options)
{
_configuration = configuration;
_logger = logger;
}
public DbSet<Order> Orders { get; set; }
public DbSet<OrderItem> OrderItems { get; set; }
public DbSet<Customer> Customers { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
var connectionString = _configuration.GetConnectionString("AwsRds");
optionsBuilder.UseSqlServer(connectionString, options =>
{
options.EnableRetryOnFailure(
maxRetryCount: 5,
maxRetryDelay: TimeSpan.FromSeconds(30),
errorNumbersToAdd: null);
options.CommandTimeout(60); // 60 second command timeout
});
// Enable sensitive data logging only in development
if (_configuration.GetValue<bool>("Logging:EnableSensitiveDataLogging"))
{
optionsBuilder.EnableSensitiveDataLogging();
}
}
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// Configure for RDS performance
modelBuilder.Entity<Order>(entity =>
{
entity.HasKey(e => e.Id);
entity.HasIndex(e => e.CustomerId);
entity.HasIndex(e => e.OrderDate);
entity.HasIndex(e => e.Status);
entity.Property(e => e.TotalAmount)
.HasColumnType("decimal(18,2)");
// Configure relationships
entity.HasOne(e => e.Customer)
.WithMany(c => c.Orders)
.HasForeignKey(e => e.CustomerId)
.OnDelete(DeleteBehavior.Restrict);
});
modelBuilder.Entity<OrderItem>(entity =>
{
entity.HasKey(e => e.Id);
entity.HasIndex(e => e.OrderId);
entity.HasIndex(e => e.ProductId);
entity.Property(e => e.UnitPrice)
.HasColumnType("decimal(18,2)");
entity.Property(e => e.TotalPrice)
.HasColumnType("decimal(18,2)")
.HasComputedColumnSql("[UnitPrice] * [Quantity]");
});
// Soft delete configuration
modelBuilder.Entity<Customer>(entity =>
{
entity.HasQueryFilter(e => !e.IsDeleted);
});
}
public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
{
// Audit trail
var entries = ChangeTracker.Entries()
.Where(e => e.Entity is IAuditable &&
(e.State == EntityState.Added || e.State == EntityState.Modified));
foreach (var entityEntry in entries)
{
var auditable = (IAuditable)entityEntry.Entity;
if (entityEntry.State == EntityState.Added)
{
auditable.CreatedAt = DateTime.UtcNow;
auditable.CreatedBy = GetCurrentUserId();
}
else
{
auditable.UpdatedAt = DateTime.UtcNow;
auditable.UpdatedBy = GetCurrentUserId();
}
}
try
{
return await base.SaveChangesAsync(cancellationToken);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error saving changes to database");
throw;
}
}
private string GetCurrentUserId()
{
// Implement your user ID retrieval logic
return "system"; // Placeholder
}
}
// Repository pattern with RDS
public class OrderRepository : IOrderRepository
{
private readonly AwsRdsDbContext _context;
private readonly ILogger<OrderRepository> _logger;
public OrderRepository(AwsRdsDbContext context, ILogger<OrderRepository> logger)
{
_context = context;
_logger = logger;
}
public async Task<Order> GetOrderByIdAsync(int orderId)
{
try
{
return await _context.Orders
.Include(o => o.Customer)
.Include(o => o.OrderItems)
.ThenInclude(oi => oi.Product)
.FirstOrDefaultAsync(o => o.Id == orderId);
}
catch (Exception ex)
{
_logger.LogError(ex, $"Error retrieving order {orderId}");
throw;
}
}
public async Task<List<Order>> GetOrdersByCustomerAsync(int customerId, DateTime? startDate = null, DateTime? endDate = null)
{
try
{
var query = _context.Orders
.Where(o => o.CustomerId == customerId);
if (startDate.HasValue)
query = query.Where(o => o.OrderDate >= startDate.Value);
if (endDate.HasValue)
query = query.Where(o => o.OrderDate <= endDate.Value);
return await query
.OrderByDescending(o => o.OrderDate)
.ToListAsync();
}
catch (Exception ex)
{
_logger.LogError(ex, $"Error retrieving orders for customer {customerId}");
throw;
}
}
public async Task<Order> CreateOrderAsync(Order order)
{
try
{
_context.Orders.Add(order);
await _context.SaveChangesAsync();
return order;
}
catch (Exception ex)
{
_logger.LogError(ex, "Error creating order");
throw;
}
}
public async Task<bool> UpdateOrderStatusAsync(int orderId, OrderStatus status)
{
try
{
var order = await _context.Orders.FindAsync(orderId);
if (order == null)
return false;
order.Status = status;
await _context.SaveChangesAsync();
return true;
}
catch (Exception ex)
{
_logger.LogError(ex, $"Error updating order status for {orderId}");
throw;
}
}
}
4. Global Scaling Strategies
4.1 Content Delivery Network (CDN) Integration
4.1.1 Azure CDN with ASP.NET Core
// Azure CDN Service
using Azure.ResourceManager.Cdn;
using Azure.ResourceManager.Cdn.Models;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
public class AzureCdnService
{
private readonly CdnManagementClient _cdnClient;
private readonly string _resourceGroupName;
private readonly string _profileName;
private readonly ILogger<AzureCdnService> _logger;
public AzureCdnService(IConfiguration configuration, ILogger<AzureCdnService> logger)
{
var connectionString = configuration.GetConnectionString("AzureCdn");
_cdnClient = new CdnManagementClient(new Uri(connectionString),
new DefaultAzureCredential());
_resourceGroupName = configuration["Azure:Cdn:ResourceGroup"];
_profileName = configuration["Azure:Cdn:ProfileName"];
_logger = logger;
}
public async Task PurgeCdnCacheAsync(List<string> contentPaths)
{
try
{
var purgeParameters = new PurgeParameters(contentPaths);
await _cdnClient.Endpoints.PurgeContentAsync(
_resourceGroupName,
_profileName,
"my-endpoint",
purgeParameters);
_logger.LogInformation($"CDN cache purged for {contentPaths.Count} paths");
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to purge CDN cache");
throw;
}
}
public async Task LoadCdnContentAsync(List<string> contentPaths)
{
try
{
var loadParameters = new LoadParameters(contentPaths);
await _cdnClient.Endpoints.LoadContentAsync(
_resourceGroupName,
_profileName,
"my-endpoint",
loadParameters);
_logger.LogInformation($"CDN content loaded for {contentPaths.Count} paths");
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to load CDN content");
throw;
}
}
}
// CDN Helper for ASP.NET Core
public static class CdnHelper
{
private static readonly string _cdnUrl;
static CdnHelper()
{
_cdnUrl = "https://my-cdn.azureedge.net";
}
public static string GetCdnUrl(string relativePath)
{
if (string.IsNullOrEmpty(relativePath))
return relativePath;
// Remove leading slash if present
if (relativePath.StartsWith("/"))
relativePath = relativePath.Substring(1);
return $"{_cdnUrl}/{relativePath}";
}
public static string GetImageUrl(string imageName, string size = "original")
{
return $"{_cdnUrl}/images/{size}/{imageName}";
}
public static string GetScriptUrl(string scriptName, bool minified = true)
{
var extension = minified ? ".min.js" : ".js";
return $"{_cdnUrl}/scripts/{scriptName}{extension}";
}
public static string GetStyleUrl(string styleName, bool minified = true)
{
var extension = minified ? ".min.css" : ".css";
return $"{_cdnUrl}/styles/{styleName}{extension}";
}
}
// Tag Helper for CDN URLs
[HtmlTargetElement("cdn-script")]
public class CdnScriptTagHelper : TagHelper
{
[HtmlAttributeName("src")]
public string Source { get; set; }
[HtmlAttributeName("minified")]
public bool Minified { get; set; } = true;
public override void Process(TagHelperContext context, TagHelperOutput output)
{
output.TagName = "script";
output.Attributes.SetAttribute("src", CdnHelper.GetScriptUrl(Source, Minified));
output.Attributes.SetAttribute("crossorigin", "anonymous");
}
}
[HtmlTargetElement("cdn-style")]
public class CdnStyleTagHelper : TagHelper
{
[HtmlAttributeName("href")]
public string Href { get; set; }
[HtmlAttributeName("minified")]
public bool Minified { get; set; } = true;
public override void Process(TagHelperContext context, TagHelperOutput output)
{
output.TagName = "link";
output.Attributes.SetAttribute("rel", "stylesheet");
output.Attributes.SetAttribute("href", CdnHelper.GetStyleUrl(Href, Minified));
output.Attributes.SetAttribute("crossorigin", "anonymous");
}
}
// Usage in Razor views
@*
<cdn-script src="site" minified="true"></cdn-script>
<cdn-style href="bootstrap" minified="true"></cdn-style>
*@
4.1.2 AWS CloudFront Integration
// AWS CloudFront Service
using Amazon.CloudFront;
using Amazon.CloudFront.Model;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
public class AwsCloudFrontService
{
private readonly IAmazonCloudFront _cloudFrontClient;
private readonly string _distributionId;
private readonly ILogger<AwsCloudFrontService> _logger;
public AwsCloudFrontService(IAmazonCloudFront cloudFrontClient,
IConfiguration configuration,
ILogger<AwsCloudFrontService> logger)
{
_cloudFrontClient = cloudFrontClient;
_distributionId = configuration["AWS:CloudFront:DistributionId"];
_logger = logger;
}
public async Task CreateInvalidationAsync(List<string> paths)
{
try
{
var invalidationBatch = new InvalidationBatch
{
CallerReference = DateTime.UtcNow.Ticks.ToString(),
Paths = new Paths
{
Items = paths,
Quantity = paths.Count
}
};
var request = new CreateInvalidationRequest
{
DistributionId = _distributionId,
InvalidationBatch = invalidationBatch
};
await _cloudFrontClient.CreateInvalidationAsync(request);
_logger.LogInformation($"CloudFront invalidation created for {paths.Count} paths");
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to create CloudFront invalidation");
throw;
}
}
public string GetSignedUrl(string resourceUrl, DateTime expiryTime)
{
var url = CloudFrontSigner.GetCannedSignedURL(
CloudFrontSigner.Protocol.https,
"d123.cloudfront.net",
null, // private key file path would be configured
resourceUrl,
"key-pair-id", // from AWS
expiryTime);
return url;
}
}
// CloudFront URL Helper
public static class CloudFrontHelper
{
private static readonly string _cloudFrontDomain = "d123.cloudfront.net";
public static string GetResourceUrl(string resourcePath)
{
return $"https://{_cloudFrontDomain}/{resourcePath.TrimStart('/')}";
}
public static string GetImageUrl(string imageName, string version = null)
{
var url = $"images/{imageName}";
if (!string.IsNullOrEmpty(version))
{
url += $"?v={version}";
}
return GetResourceUrl(url);
}
public static string GetAssetUrl(string assetPath, bool cacheBust = true)
{
var url = $"assets/{assetPath}";
if (cacheBust)
{
var version = Guid.NewGuid().ToString("N").Substring(0, 8);
url += $"?v={version}";
}
return GetResourceUrl(url);
}
}

Join the conversation! Your thoughts help the community grow.