Sem 2 L3
Sem 2 L3
Application Development
Bishal Gharti Chhetri
bishal.ghartichhetri@islingtoncollege.edu.np
Week-3
Agenda
• HttpContext
• Configuration
• Dependency Injection
| 2
HttpContext
Anatomy of HTTP Message
| 4
HttpContext
• .NET type that represents an HTTP message.
• Its instance holds all HTTP-specific information about the incoming request and outgoing response.
• Provides:
• The incoming request data (headers, query strings, form data, etc.)
• The outgoing response (status, headers, body)
• User and authentication data
• Session state and per-request items
• HttpContext is request-scoped—meaning it exists only for the duration of a single HTTP request.
• Always remember HttpContext lives only for the duration of an HTTP request.
| 5
Key Properties of HttpContext
Property Description Usage Example
Request Contains details about the incoming HTTP request. HttpContext.Request
Response Used to send data back to the client. HttpContext.Response
User Holds information about the authenticated user (ClaimsPrincipal). HttpContext.User
Session Provides access to session state (if enabled). HttpContext.Session
Items A per-request key–value store for transient data. HttpContext.Items
Connection Contains network details (e.g., IP Address, port). HttpContext.Connection
A unique ID for the current request (useful for logging, tracking
TraceIdentifier HttpContext.TraceIdentifier
and diagnosing).
| 6
Accessing HttpContext in Controllers
• In controllers (which inherit from Controller or ControllerBase), you can directly access HttpContext via the base
class.
| 7
Using IHttpContextAccessor in Services
• In non-controller classes (e.g., services), HttpContext is not available by default because it is tied to the request
lifecycle.
• Inject IHttpContextAccessor to retrieve the current HttpContext.
| 8
Example > Accessing User-Agent Header
| 9
Example > Reading Request Body Manually
| 10
Example > Writing Response Manually
| 11
Example > User Authentication
| 12
Example > Accessing Connection Details
| 13
Configuration
Configuration
• ASP.NET Core provides a robust configuration system to read application settings and secrets from multiple sources.
• Example: Connection Strings for Database and Redis, Payment Gateway API Key, JWT Secret Key, Logging
Configuration, SMTP Server Secrets, Cloud Storage Bucket Credentials, OAuth2 Client ID and Secret for Google
Login, Third-Party SMS API Key, Security Certificate Paths, API Rate Limiting parameters, Feature Toggles/Flags for
A/B Testing, Caching Settings (e.g., Cache Duration) etc.
• Configuration Sources: ASP.NET Core loads configuration from multiple sources in a predefined order:
• Command-Line Arguments
• Environment Variables
• User Secrets
• Cloud Services
• appsettings.{Environment}.json
• appsettings.json
| 15
Command-Line Arguments (Highest Priority)
• Purpose: Used for quick runtime overrides without modifying files. Helpful for temporary changes.
• Example: Run the application with a modified logging level.
| 16
Environment Variables (OS)
• Access System-wide or User-wide environment variables from the Operating System.
| 17
Environment Variables (.env)
• Purpose: Used for secrets, environment-specific settings, and deployment overrides. Often stored in .env files and
loaded dynamically.
• Note:
• To load the .env file in your .NET project, you can use the DotNetEnv package.
• Use a double underscore (__) as the separator to represent nested configuration properties in the .env file.
• Example: Set connection strings in an .env file:
| 18
User Secrets (Development Only)
• Purpose: Used to store sensitive data securely during development, without committing to version control.
• Example: Initialize and set a secret:
| 19
Cloud Services (Production Only)
• Purpose: Used to securely store production secrets and settings in the cloud.
• Note: Requires related package(s) to work with.
| 20
appsettings.{Environment}.json
• Purpose: Used for environment-specific configurations (e.g., Development, Staging, Production) by using additional
files.
• Example:
| 21
appsettings.json (Lowest Priority)
• Purpose:
• Meant to store common configurations for all environment.
• Default application configurations that apply if no overrides exist.
• Example:
| 22
Accessing Configuration in Code
• ASP.NET Core provides IConfiguration to access settings.
• Inject the IConfiguration and use an indexer to access both simple and nested properties.
• Specify key paths using a colon (:) as the delimiter for nested properties.
| 23
Example
| 24
Example
| 25
Option Pattern
• Access configuration in a strongly typed manner.
• Bind configuration sections (from sources like appsettings.json) to Plain Old CLR Objects (POCOs).
• There are three Options interfaces:
• IOptions<T>
• Designed for Singleton lifetimes. it retrieves the configuration values at application startup and doesn't change during
the lifetime of the app (static configuration).
• IOptionsSnapshot<T>
• Designed for scoped lifetimes. It retrieves a fresh copy of configuration values on each request.
• Use when you need to pick up configuration changes without restarting the application.
• IOptionsMonitor<T>
• A singleton that provides change notifications.
• Useful for reacting to configuration changes globally.
| 26
Example > appsettings.json
| 27
Example > Define Your POCO Classes for Options
• Your configuration file should match the structure of your POCO:
| 28
Example > Register the Options in Program.cs
| 29
Example > Inject and Use in your Service
| 30
Best Practices
• Never store secrets or sensitive data in source code or configuration files.
• Avoid using production secrets in development or test environments.
• Secrets should not be deployed with the application.
• Use controlled access methods (e.g., Azure Key Vault) for production secrets.
| 31
Dependency Injection
Dependency
• A dependency is an object or resource that a class requires to function properly.
• If a class relies on another class or service to perform its operations, then it has a dependency on that class.
• Below, the NotificationService is hardcoded to depend upon EmailService (Dependency).
| 33
Problems with Hardcoded Dependencies
• Tight Coupling
• When a class directly instantiates another class (e.g., new ClassB() inside ClassA), it creates tight coupling,
making the code harder to modify and test.
• Here, EmailService is directly tied to NotificationService.
• Maintainability & Scalability:
• If a class is tightly coupled to a specific implementation, changes in one part of the code can force
modifications in multiple places.
• Loosely coupled dependencies make the system more modular and easier to extend.
• Here, If we want to use a different implementation (e.g., SmsService, PushNotificationService), we need to
change the class implementation.
• Testability Problem:
• Hardcoded dependencies make unit testing difficult because you cannot easily replace real implementations
with mock or fake objects.
| 34
Solution
1. Dependency Injection (DI):
• Design pattern that promotes Inversion of Control (IoC) by providing (injecting) required dependencies to a
class via external source instead of instantiating them manually.
• ASP.NET Core has a built-in Dependency Injection container that manages service lifetimes and dependencies.
• Instead of creating dependencies manually, they should be injected from the outside using constructor
injection, property injection (not supported), or method injection.
| 35
Example
• Following DIP, Define an Abstraction (Interface aka Contract).
• Keep Interfaces Stable, Avoid changing interfaces frequently, as it can break dependent implementations.
• Keep Interfaces Small (Interface Segregation), Don't force classes to implement methods they don’t need.
• Keep Interface focused and specific to avoid unnecessary dependencies, Design for the Consumer (Not the
Implementation)
• Avoid Leaking Implementation Details.
| 36
Example
| 37
Example
| 38
Example > Primary Constructor
| 39
Example > Dependency Registration
| 40
Example > Dependency Registration
Note: If you register multiple implementations for the same interface and then request a single instance, you will get
the last registered implementation.
| 41
Example
| 42
Service Lifetimes
• Defines when it should be created & how long an instance of a service should exist.
• Determines the scope of the service's availability and how long it will persist in memory during the application's
runtime.
• Service lifetime impacts performance, memory usage, and the behavior of your application.
• In Dependency Injection (DI) in ASP.NET, services have three main lifetimes:
• Transient
• Scoped
• Singleton
| 43
Service Lifetimes > Transient
• Lifetime: The service is created each time it is requested from the service container.
• Use case: For lightweight, short-lived, stateless services where each operation or request needs a fresh instance.
• Effect on performance: Can lead to higher memory usage if many instances are created unnecessarily.
• Example: Logger, some utility services, etc.
| 44
Service Lifetimes > Scoped
• Lifetime: For web applications, A service is created once per client request (connection).
• Use case: For services that need to maintain state during a single request but should be different across requests.
• Effect on performance: More efficient than Transient when working with resources that are expensive to create,
like database connections. The scope ensures the service isn't recreated multiple times within the same request.
• Example: Database contexts, session services, etc.
| 45
Service Lifetimes > Singleton
• Lifetime: A single instance is created and shared throughout the application's lifetime.
• Use case: For services that are expensive to create, don't need to maintain state per request, and/or for global state
that needs to be shared across requests.
• Effect on performance: Saves memory and processing time, as it avoids creating new instances of the service
repeatedly. However, it’s critical to ensure the service is thread-safe and doesn’t hold state that should vary across
requests or users.
• Example: Caching services, configuration services, etc.
| 46
Service Lifetimes Visualization
Service Lifetimes HTTP Request Class
A(O) B(O) C(O)
1st O-1 O-2 O-3
Transient
2nd O-4 O-5 O-6
1st O-1 O-1 O-1
Scoped
2nd O-2 O-2 O-2
1st O-1 O-1 O-1
Singleton
2nd O-1 O-1 O-1
• Choosing the appropriate lifetime is essential for managing resource usage, ensuring correct application behavior,
and optimizing performance.
| 47
Ways to Inject dependencies
• Constructor Injection (Most Common, Simple, & Preferred)
• Dependencies are provided through the constructor of a class.
• Method Injection
• Dependencies are injected directly into method parameters, using the [FromServices] attribute
• Useful when a dependency is only required for specific methods, not the whole class.
• Mostly used in Controller Action Methods.
• Manual Injection with IServiceProvider
• Dependencies are manually resolved from the DI container using the IServiceProvider interface.
• Useful for advanced scenarios where dependencies need to be resolved dynamically.
• You can access the IServiceProvider through the HttpContext.RequestServices property.
| 48
Example > Constructor Injection
| 49
Example > Method Injection
| 50
Example > Manual Injection with IServiceProvider
| 51
Keyed Service (.NET 8)
• Allows you to register multiple implementations of the same interface and resolve them using a key.
• Useful when you have different variations of a service that need to be selected dynamically.
• For example: You can register the IMessageService implementations (SmsService, PushNotificationService,
EmailService) using keyed service registration as follows:
| 52
Example
• For retrieving specific service instances, Use:
• Controller & Method Injection: [FromKeyedServices]
• Manual Injection:
• IKeyedServiceProvider.GetKeyedService
• IServiceProvider.GetKeyedService
• HttpContext.RequestServices.GetKeyedService
| 53
| 54