Table of Contents
Introduction
A Real-Time Scenario: Smart Grid Load Balancing in Urban Power Networks
Dependency Injection in .NET Isolated Azure Functions
Configuring Environment Variables for Local Development
End-to-End Example: Secure, Testable, and Maintainable Code
Best Practices for Enterprise Deployment
Conclusion
Introduction
In the world of cloud-native serverless applications, writing functions that are testable, secure, and maintainable isn’t optional—it’s foundational. Nowhere is this more critical than in infrastructure systems where failure cascades can trigger city-wide outages.
Let’s explore dependency injection and environment configuration in .NET Isolated Azure Functions through a live scenario: real-time smart grid load balancing for a metropolitan power utility.
A Real-Time Scenario: Smart Grid Load Balancing in Urban Power Networks
The city’s power grid ingests telemetry from 50,000+ smart meters every 5 seconds. An Azure Function processes this stream to detect overload risks and triggers automatic load-shedding protocols.
Requirements:
Inject telemetry clients, circuit breaker logic, and audit loggers without tight coupling
Securely manage secrets (e.g., IoT Hub connection strings) in both cloud and local dev
Run identical logic on a developer’s laptop and in Azure—no “works on my machine” excuses
This is where .NET Isolated Process DI and proper environment configuration become non-negotiable.
![PlantUML Diagram]()
Dependency Injection in .NET Isolated Azure Functions
Unlike in-process functions, .NET Isolated runs in its own process and uses a startup-based DI model similar to ASP.NET Core.
You define services in a Program.cs
file:
// Program.cs
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using SmartGrid.Services;
var host = new HostBuilder()
.ConfigureFunctionsWorkerDefaults()
.ConfigureServices(services =>
{
// Register telemetry service
services.AddSingleton<ITelemetryClient, GridTelemetryClient>();
// Register audit logger with scoped lifetime
services.AddScoped<IAuditLogger, AzureMonitorAuditLogger>();
// Register configuration-bound options
services.Configure<GridConfig>(Environment.GetEnvironmentVariables()
.ToDictionary(k => k.Key, v => v.Value));
})
.Build();
host.Run();
Then inject dependencies directly into your function class:
// LoadBalancerFunction.cs
public class LoadBalancerFunction
{
private readonly ITelemetryClient _telemetry;
private readonly IAuditLogger _logger;
private readonly GridConfig _config;
public LoadBalancerFunction(
ITelemetryClient telemetry,
IAuditLogger logger,
IOptions<GridConfig> config)
{
_telemetry = telemetry;
_logger = logger;
_config = config.Value;
}
[Function("ProcessMeterData")]
public async Task Run([EventHubTrigger("meter-readings", Connection = "EventHubConnection")] string[] events)
{
foreach (var evt in events)
{
var risk = await _telemetry.AnalyzeOverloadRiskAsync(evt);
if (risk > _config.Threshold)
{
await _logger.LogAsync($"Overload detected: {risk}");
// Trigger load-shedding
}
}
}
}
This design enables unit testing, mocking, and runtime flexibility—all while keeping the function stateless and scalable.
Configuring Environment Variables for Local Development
During local development, you never hardcode secrets. Instead, use local.settings.json
:
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"EventHubConnection": "Endpoint=sb://dev-grid.servicebus.windows.net/;SharedAccessKey=...",
"GRID_THRESHOLD": "0.85",
"FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated"
}
}
Critical: This file is never committed to source control. Add it to .gitignore
.
At runtime, Azure Functions automatically loads these values into the environment. In code, you access them via Environment.GetEnvironmentVariable("GRID_THRESHOLD")
or through strongly-typed IOptions<T>
as shown above.
For production, you override these values in the Azure portal or via Azure Key Vault references:
EventHubConnection = @Microsoft.KeyVault(SecretUri=https://grid-kv.vault.azure.net/secrets/EventHubConnStr)
This ensures zero secret leakage and consistent configuration across environments.
End-to-End Example: Secure, Testable, and Maintainable Code
With DI and environment config in place, your function becomes:
Testable: Mock ITelemetryClient
in unit tests
Secure: Secrets never in code
Portable: Same binary runs locally and in Azure
Observable: Audit logs and metrics wired via injected services
A developer can simulate a grid overload on their laptop using local Event Hub emulator and local.settings.json
—then deploy the exact same artifact to production with confidence.
Best Practices for Enterprise Deployment
Always use IOptions<T>
for configuration—never read env vars inline.
Register services with appropriate lifetimes: Singleton
for stateless clients, Scoped
for per-invocation logic.
Use Azure Key Vault + Managed Identity in production—never raw secrets.
Validate config on startup to fail fast.
Keep local.settings.json
out of Git—use a local.settings.sample.json
template instead.
Conclusion
In enterprise-grade serverless systems, how you manage dependencies and configuration determines your resilience. The .NET Isolated model gives you full control—use it wisely. By embracing dependency injection and secure environment management, you transform Azure Functions from simple scripts into robust, auditable, and production-ready components of critical infrastructure—whether you’re balancing city power grids, monitoring ICU vitals, or processing financial transactions. Remember: Great architecture isn’t just about scale—it’s about safety, testability, and trust.