C# Flashcards
Inversion of Control (IoC) -Principle
Inversion of Control (IoC) is a design principle (although, some people refer to it as a pattern). As the name suggests, it is used to invert different kinds of controls in object-oriented design to achieve loose coupling. Here, controls refer to any additional responsibilities a class has, other than its main responsibility. This include control over the flow of an application, and control over the flow of an object creation or dependent object creation and binding.
IoC is all about inverting the control. To explain this in layman’s terms, suppose you drive a car to your work place. This means you control the car. The IoC principle suggests to invert the control, meaning that instead of driving the car yourself, you hire a cab, where another person will drive the car. Thus, this is called inversion of the control - from you to the cab driver. You don’t have to drive a car yourself and you can let the driver do the driving so that you can focus on your main work.
The IoC principle helps in designing loosely coupled classes which make them testable, maintainable and extensible.
Dependency Inversion Principle -DIP
DIP is one of the SOLID object-oriented principle invented by Robert Martin (a.k.a. Uncle Bob)
1: High-level modules should not depend on low-level modules. Both should depend on the abstraction.
2: Abstractions should not depend on details. Details should depend on abstractions.
A high-level module is a module which depends on other modules. In our example, CustomerBusinessLogic depends on the DataAccess class, so CustomerBusinessLogic is a high-level module and DataAccess is a low-level module. So, as per the first rule of DIP, CustomerBusinessLogic should not depend on the concrete DataAccess class, instead both classes should depend on abstraction.
In English, abstraction means something which is non-concrete. In programming terms, the above CustomerBusinessLogic and DataAccess are concrete classes, meaning we can create objects of them. So, abstraction in programming means to create an interface or an abstract class which is non-concrete. This means we cannot create an object of an interface or an abstract class. As per DIP, CustomerBusinessLogic (high-level module) should not depend on the concrete DataAccess class (low-level module). Both classes should depend on abstractions, meaning both classes should depend on an interface or an abstract class.
Dependency Injection (DI)
Dependency Injection (DI) is a design pattern which implements the IoC principle to invert the creation of dependent objects.
Inversion of Control (IoC) -Principle-Control Over the Flow of a Program
In the console app, the Main() function of the program class controls the flow of a program. It takes the user’s input for the first name and last name. It saves the data, and continues or exits the console, depending upon the user’s input. So here, the flow is controlled through the Main() function.
IoC can be applied to the above program by creating a GUI-based application such as the following windows-based application, wherein the framework will handle the flow of a program by using events.
Inversion of Control (IoC) -Principle-Control Over the Dependent Object Creation
UI-->Service Layer-->Business Logic -->DataAccess In the typical n-tier architecture, the User Interface (UI) uses Service layer to retrieve or save data. The Service layer uses the BusinessLogic class to apply business rules on the data. The BusinessLogic class depends on the DataAccess class which retrieves or saves the data to the underlying database. This is simple n-tier architecture design. Let's focus on the BusinessLogic and DataAccess classes to understand IoC.
The following is an example of BusinessLogic and DataAccess classes for a customer.
public class CustomerBusinessLogic { DataAccess _dataAccess;
public CustomerBusinessLogic() { _dataAccess = new DataAccess(); }
public string GetCustomerName(int id) { return _dataAccess.GetCustomerName(id); } }
public class DataAccess { public DataAccess() { }
public string GetCustomerName(int id) { return "Dummy Customer Name"; // get it from DB in real app } }
As you can see in the above example, the CustomerBusinessLogic class depends on the DataAccess class. It creates an object of the DataAccess class to get the customer data.
Now, let’s understand what’s wrong with the above classes.
In the above example, CustomerBusinessLogic and DataAccess are tightly coupled classes because the CustomerBusinessLogic class includes the reference of the concrete DataAccess class. It also creates an object of DataAccess class and manages the lifetime of the object.
Problems in the above example classes:
1: CustomerBusinessLogic and DataAccess classes are tightly coupled classes. So, changes in the DataAccess class will lead to changes in the CustomerBusinessLogic class. For example, if we add, remove or rename any method in the DataAccess class then we need to change the CustomerBusinessLogic class accordingly.
2: Suppose the customer data comes from different databases or web services and, in the future, we may need to create different classes, so this will lead to changes in the CustomerBusinessLogic class.
3: The CustomerBusinessLogic class creates an object of the DataAccess class using the new keyword. There may be multiple classes which use the DataAccess class and create its objects. So, if you change the name of the class, then you need to find all the places in your source code where you created objects of DataAccess and make the changes throughout the code. This is repetitive code for creating objects of the same class and maintaining their dependencies.
4: Because the CustomerBusinessLogic class creates an object of the concrete DataAccess class, it cannot be tested independently (TDD). The DataAccess class cannot be replaced with a mock class.
To solve all of the above problems and get a loosely coupled design, we can use the IoC and DIP principles together. Remember, IoC is a principle, not a pattern. It just gives high-level design guidelines but does not give implementation details. You are free to implement the IoC principle the way you want.
The following pattern (but not limited) implements the IoC principle.
IoC can be done with Service Locator, Factory,Template Method,Dependency injection,Abstract Factory,
Pattern for IOC
Let’s use the Factory pattern to implement IoC in the above example, as the first step towards attaining loosely coupled classes.
First, create a simple Factory class which returns an object of the DataAccess class as shown below.
Example: DataAccess Factory public class DataAccessFactory { public static DataAccess GetDataAccessObj() { return new DataAccess(); } } Now, use this DataAccessFactory class in the CustomerBusinessLogic class to get an object of DataAccess class.
Example: Use Factory Class to Retrieve Object public class CustomerBusinessLogic {
public CustomerBusinessLogic() { }
public string GetCustomerName(int id) { DataAccess _dataAccess = DataAccessFactory.GetDataAccessObj();
return _dataAccess.GetCustomerName(id); } } As you can see, the CustomerBusinessLogic class uses the DataAccessFactory.GetCustomerDataAccessObj() method to get an object of the DataAccess class instead of creating it using the new keyword. Thus, we have inverted the control of creating an object of a dependent class from the CustomerBusinessLogic class to the DataAccessFactory class.
This is a simple implementation of IoC and the first step towards achieving fully loose coupled design. As mentioned in the previous chapter, we will not achieve complete loosely coupled classes by only using IoC. Along with IoC, we also need to use DIP, Strategy pattern, and DI (Dependency Injection).
loosely coupled classes
In an object-oriented design, classes should be designed in a loosely coupled way. Loosely coupled means changes in one class should not force other classes to change, so the whole application can become maintainable and extensible.
Static Methods
the static methods in C# do not need an object to call them we could call them in another class by ClassName.StaticMethodName();
Abstraction
In English, abstraction means something which is non-concrete. In programming terms, the above CustomerBusinessLogic and DataAccess are concrete classes, meaning we can create objects of them. So, abstraction in programming means to create an interface or an abstract class which is non-concrete. This means we cannot create an object of an interface or an abstract class. As per DIP, CustomerBusinessLogic (high-level module) should not depend on the concrete DataAccess class (low-level module). Both classes should depend on abstractions, meaning both classes should depend on an interface or an abstract class.
Dependency Inversion Principle -DIP - Example
public interface ICustomerDataAccess { string GetCustomerName(int id); }
public class CustomerDataAccess: ICustomerDataAccess { public CustomerDataAccess() { }
public string GetCustomerName(int id) { return "Dummy Customer Name"; } }
public class DataAccessFactory { public static ICustomerDataAccess GetCustomerDataAccessObj() { return new CustomerDataAccess(); } }
public class CustomerBusinessLogic { ICustomerDataAccess _custDataAccess;
public CustomerBusinessLogic() { _custDataAccess = DataAccessFactory.GetCustomerDataAccessObj(); }
public string GetCustomerName(int id) { return _custDataAccess.GetCustomerName(id); } }
The advantages of implementing DIP in the above example is that the CustomerBusinessLogic and CustomerDataAccess classes are loosely coupled classes because CustomerBusinessLogic does not depend on the concrete DataAccess class, instead it includes a reference of the ICustomerDataAccess interface. So now, we can easily use another class which implements ICustomerDataAccess with a different implementation.
Still, we have not achieved fully loosely coupled classes because the CustomerBusinessLogic class includes a factory class to get the reference of ICustomerDataAccess. This is where the Dependency Injection pattern helps us.
Dependency Injection
Dependency Injection (DI) is a design pattern used to implement IoC. It allows the creation of dependent objects outside of a class and provides those objects to a class through different ways. Using DI, we move the creation and binding of the dependent objects outside of the class that depends on them.
The Dependency Injection pattern involves 3 types of classes.
Client Class: The client class (dependent class) is a class which depends on the service class Service Class: The service class (dependency) is a class that provides service to the client class. Injector Class: The injector class injects the service class object into the client class.
As you can see, the injector class creates an object of the service class, and injects that object to a client object. In this way, the DI pattern separates the responsibility of creating an object of the service class out of the client class.
Constructor Dependency Injection example
Example: Constructor Injection public class CustomerBusinessLogic { ICustomerDataAccess _dataAccess;
public CustomerBusinessLogic(ICustomerDataAccess custDataAccess) { _dataAccess = custDataAccess; }
public CustomerBusinessLogic() { _dataAccess = new CustomerDataAccess(); }
public string ProcessCustomerData(int id) { return _dataAccess.GetCustomerName(id); } }
public interface ICustomerDataAccess { string GetCustomerData(int id); }
public class CustomerDataAccess: ICustomerDataAccess { public CustomerDataAccess() { }
public string GetCustomerName(int id) { //get the customer name from the db in real application return "Dummy Customer Name"; } } In the above example, CustomerBusinessLogic includes the constructor with one parameter of type ICustomerDataAccess. Now, the calling class must inject an object of ICustomerDataAccess.
Example: Inject Dependency
public class CustomerService
{
CustomerBusinessLogic _customerBL;
public CustomerService() { _customerBL = new CustomerBusinessLogic(new CustomerDataAccess()); }
public string GetCustomerName(int id) { return _customerBL.GetCustomerName(id); } } As you can see in the above example, the CustomerService class creates and injects the CustomerDataAccess object into the CustomerBusinessLogic class. Thus, the CustomerBusinessLogic class doesn't need to create an object of CustomerDataAccess using the new keyword or using factory class. The calling class (CustomerService) creates and sets the appropriate DataAccess class to the CustomerBusinessLogic class. In this way, the CustomerBusinessLogic and CustomerDataAccess classes become "more" loosely coupled classes.
Defining the services for Dependency injection
The Startup.ConfigureServices method is responsible for defining the services that the app uses, including platform features, such as Entity Framework Core and ASP.NET Core MVC. Initially, the IServiceCollection provided to ConfigureServices has services defined by the framework depending on how the host was configured. It’s not uncommon for an app based on an ASP.NET Core template to have hundreds of services registered by the framework. A small sample of framework-registered services is listed in the following table.
Extension methods
https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/extension-methods
Constructor Dependency injection into controllers in ASP.NET Core
Constructor Injection
Services are added as a constructor parameter, and the runtime resolves the service from the service container. Services are typically defined using interfaces. For example, consider an app that requires the current time. The following interface exposes the IDateTime service:
C#
Copy public interface IDateTime { DateTime Now { get; } } The following code implements the IDateTime interface:
C#
Copy public class SystemDateTime : IDateTime { public DateTime Now { get { return DateTime.Now; } } } Add the service to the service container:
C#
Copy
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton();
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2); } For more information on AddSingleton, see DI service lifetimes.
The following code displays a greeting to the user based on the time of day:
C#
Copy public class HomeController : Controller { private readonly IDateTime _dateTime;
public HomeController(IDateTime dateTime) { _dateTime = dateTime; }
public IActionResult Index() { var serverTime = _dateTime.Now; if (serverTime.Hour < 12) { ViewData["Message"] = "It's morning here - Good Morning!"; } else if (serverTime.Hour < 17) { ViewData["Message"] = "It's afternoon here - Good Afternoon!"; } else { ViewData["Message"] = "It's evening here - Good Evening!"; } return View(); } Run the app and a message is displayed based on the time.
Action injection with FromServices with out using Constructor Dependency Injection
Action injection with FromServices
The FromServicesAttribute enables injecting a service directly into an action method without using constructor injection:
C#
Copy
public IActionResult About([FromServices] IDateTime dateTime)
{
ViewData[“Message”] = $”Current server time: {dateTime.Now}”;
return View(); }
Multiple Startup classes for different environments
https://docs.microsoft.com/en-us/aspnet/core/fundamentals/startup?view=aspnetcore-3.1
When the app defines separate Startup classes for different environments (for example, StartupDevelopment), the appropriate Startup class is selected at runtime. The class whose name suffix matches the current environment is prioritized. If the app is run in the Development environment and includes both a Startup class and a StartupDevelopment class, the StartupDevelopment class is used. For more information, see Use multiple environments.
App startup in ASP.NET Core
The Startup class configures services and the app’s request pipeline.
The Startup class ASP.NET Core apps use a Startup class, which is named Startup by convention. The Startup class:
Optionally includes a ConfigureServices method to configure the app’s services. A service is a reusable component that provides app functionality. Services are registered in ConfigureServices and consumed across the app via dependency injection (DI) or ApplicationServices.
Includes a Configure method to create the app’s request processing pipeline.
ConfigureServices and Configure are called by the ASP.NET Core runtime when the app starts:
IHostBuilder in program.cs
The Startup class is specified when the app’s host is built. The Startup class is typically specified by calling the WebHostBuilderExtensions.UseStartup method on the host builder:
C#
Copy
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup(); }); } The host provides services that are available to the Startup class constructor. The app adds additional services via ConfigureServices. Both the host and app services are available in Configure and throughout the app.
Only the following service types can be injected into the Startup constructor when using the Generic Host (IHostBuilder):
IWebHostEnvironment
IHostEnvironment
IConfiguration
C#
Copy public class Startup { private readonly IWebHostEnvironment _env;
public Startup(IConfiguration configuration, IWebHostEnvironment env) { Configuration = configuration; _env = env; }
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services) { if (_env.IsDevelopment()) { } else { } } } Most services are not available until the Configure method is called.
ConfigureServices method in startup
The ConfigureServices method is:
Optional.
Called by the host before the Configure method to configure the app’s services.
Where configuration options are set by convention.
The host may configure some services before Startup methods are called. For more information, see The host.
For features that require substantial setup, there are Add{Service} extension methods on IServiceCollection. For example, AddDbContext, AddDefaultIdentity, AddEntityFrameworkStores, and AddRazorPages:
https://docs.microsoft.com/en-us/aspnet/core/fundamentals/?view=aspnetcore-3.1&tabs=windows#host
public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; }
public IConfiguration Configuration { get; } public void ConfigureServices(IServiceCollection services) { services.AddDbContext(options => options.UseSqlServer( Configuration.GetConnectionString("DefaultConnection"))); services.AddDefaultIdentity( options => options.SignIn.RequireConfirmedAccount = true) .AddEntityFrameworkStores(); services.AddRazorPages(); } Adding services to the service container makes them available within the app and in the Configure method. The services are resolved via dependency injection or from ApplicationServices.
ASP.NET Core Middleware
Middleware is software that’s assembled into an app pipeline to handle requests and responses. Each component:
Chooses whether to pass the request to the next component in the pipeline.
Can perform work before and after the next component in the pipeline.
The Configure method in startup class
The Configure method is used to specify how the app responds to HTTP requests. The request pipeline is configured by adding middleware components to an IApplicationBuilder instance. IApplicationBuilder is available to the Configure method, but it isn’t registered in the service container. Hosting creates an IApplicationBuilder and passes it directly to Configure.
The ASP.NET Core templates configure the pipeline with support for:
Developer Exception Page Exception handler HTTP Strict Transport Security (HSTS) HTTPS redirection Static files ASP.NET Core MVC and Razor Pages C#
Copy public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; }
public IConfiguration Configuration { get; } public void ConfigureServices(IServiceCollection services) { services.AddRazorPages(); } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { app.UseExceptionHandler("/Error"); app.UseHsts(); } app. UseHttpsRedirection(); app. UseStaticFiles(); app. UseRouting(); app. UseAuthorization();
app.UseEndpoints(endpoints => { endpoints.MapRazorPages(); }); } } The preceding sample is for Razor Pages; the MVC version is similar.
Each Use extension method adds one or more middleware components to the request pipeline. For instance, UseStaticFiles configures middleware to serve static files.
Each middleware component in the request pipeline is responsible for invoking the next component in the pipeline or short-circuiting the chain, if appropriate.
Additional services, such as IWebHostEnvironment, ILoggerFactory, or anything defined in ConfigureServices, can be specified in the Configure method signature. These services are injected if they’re available.
For more information on how to use IApplicationBuilder and the order of middleware processing, see ASP.NET Core Middleware.
Configure services without Startup
To configure services and the request processing pipeline without using a Startup class, call ConfigureServices and Configure convenience methods on the host builder. Multiple calls to ConfigureServices append to one another. If multiple Configure method calls exist, the last Configure call is used.
C#
Copy
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureAppConfiguration((hostingContext, config) => { }) .ConfigureWebHostDefaults(webBuilder => { webBuilder.ConfigureServices(services => { services.AddControllersWithViews(); }) .Configure(app => { var loggerFactory = app.ApplicationServices .GetRequiredService(); var logger = loggerFactory.CreateLogger(); var env = app.ApplicationServices.GetRequiredService(); var config = app.ApplicationServices.GetRequiredService(); logger.LogInformation("Logged in Configure"); if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { app.UseExceptionHandler("/Home/Error"); app.UseHsts(); }
var configValue = config["MyConfigKey"]; }); }); }); }
launchSettings.json
he environment for local machine development can be set in the Properties\launchSettings.json file of the project. Environment values set in launchSettings.json override values set in the system environment.
The following JSON shows three profiles from a launchSettings.json file:
JSON
Copy { "iisSettings": { "windowsAuthentication": false, "anonymousAuthentication": true, "iisExpress": { "applicationUrl": "http://localhost:54339/", "sslPort": 0 } }, "profiles": { "IIS Express": { "commandName": "IISExpress", "launchBrowser": true, "environmentVariables": { "ASPNETCORE_My_Environment": "1", "ASPNETCORE_DETAILEDERRORS": "1", "ASPNETCORE_ENVIRONMENT": "Staging" } }, "EnvironmentsSample": { "commandName": "Project", "launchBrowser": true, "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Staging" }, "applicationUrl": "http://localhost:54340/" }, "Kestrel Staging": { "commandName": "Project", "launchBrowser": true, "environmentVariables": { "ASPNETCORE_My_Environment": "1", "ASPNETCORE_DETAILEDERRORS": "1", "ASPNETCORE_ENVIRONMENT": "Staging" }, "applicationUrl": "http://localhost:51997/" } } }
Environment in asp.net core
ASP.NET Core configures app behavior based on the runtime environment using an environment variable.
Environments
ASP.NET Core reads the environment variable ASPNETCORE_ENVIRONMENT at app startup and stores the value in IWebHostEnvironment.EnvironmentName. ASPNETCORE_ENVIRONMENT can be set to any value, but three values are provided by the framework:
Development
Staging
Production (default)
C#
Copy public void Configure(IApplicationBuilder app, IHostingEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); }
if (env.IsProduction() || env.IsStaging() || env.IsEnvironment("Staging_2")) { app.UseExceptionHandler("/Error"); } app.UseStaticFiles(); app.UseMvc(); } The preceding code:
Calls UseDeveloperExceptionPage when ASPNETCORE_ENVIRONMENT is set to Development.
Calls UseExceptionHandler when the value of ASPNETCORE_ENVIRONMENT is set one of the following:
Staging
Production
Staging_2
The Environment Tag Helper uses the value of IHostingEnvironment.EnvironmentName to include or exclude markup in the element:
CSHTML
Copy
<div><environment include="Development"></div> <div><environment exclude="Development"></div> <div> <environment include="Staging,Development,Staging_2"> </div>
On Windows and macOS, environment variables and values aren’t case sensitive. Linux environment variables and values are case sensitive by default.
setting environmental variable in launchsettings.json
The applicationUrl property in launchSettings.json can specify a list of server URLs. Use a semicolon between the URLs in the list:
JSON
Copy
“EnvironmentsSample”: {
“commandName”: “Project”,
“launchBrowser”: true,
“applicationUrl”: “https://localhost:5001;http://localhost:5000”,
“environmentVariables”: {
“ASPNETCORE_ENVIRONMENT”: “Development”
}
}
When the app is launched with dotnet run, the first profile with “commandName”: “Project” is used. The value of commandName specifies the web server to launch. commandName can be any one of the following:
IISExpress
IIS
Project (which launches Kestrel)
When an app is launched with dotnet run:
launchSettings.json is read if available. environmentVariables settings in launchSettings.json override environment variables.
The hosting environment is displayed.
The following output shows an app started with dotnet run:
PS C:\Websites\EnvironmentsSample> dotnet run
Using launch settings from C:\Websites\EnvironmentsSample\Properties\launchSettings.json…
Hosting environment: Staging
Content root path: C:\Websites\EnvironmentsSample
Now listening on: http://localhost:54340
Application started. Press Ctrl+C to shut down.
Visual studio to set environmental variables
Project –> Properties –>Profiles –>select profile and update the variables
These variables override the system environmental variables
Changes made to project profiles may not take effect until the web server is restarted. Kestrel must be restarted before it can detect changes made to its environment.
Secrets in asp.net core
launchSettings.json shouldn’t store secrets. The Secret Manager tool can be used to store secrets for local development.
Visual studio code Environmental variables
When using Visual Studio Code, environment variables can be set in the .vscode/launch.json file. The following example sets the environment to Development:
JSON
Copy { "version": "0.2.0", "configurations": [ { "name": ".NET Core Launch (web)",
... additional VS Code configuration settings ... "env": { "ASPNETCORE_ENVIRONMENT": "Development" } } ] } A .vscode/launch.json file in the project isn't read when starting the app with dotnet run in the same way as Properties/launchSettings.json. When launching an app in development that doesn't have a launchSettings.json file, either set the environment with an environment variable or a command-line argument to the dotnet run command.
Advantages of setting production environment variables
Production
The production environment should be configured to maximize security, performance, and app robustness. Some common settings that differ from development include:
Caching.
Client-side resources are bundled, minified, and potentially served from a CDN.
Diagnostic error pages disabled.
Friendly error pages enabled.
Production logging and monitoring enabled. For example, Application Insights.
Set the environment
It’s often useful to set a specific environment for testing with an environment variable or platform setting. If the environment isn’t set, it defaults to Production, which disables most debugging features. The method for setting the environment depends on the operating system.
When the host is built, the last environment setting read by the app determines the app’s environment. The app’s environment can’t be changed while the app is running.
Set Environmental variable for current session
To set the ASPNETCORE_ENVIRONMENT for the current session when the app is started using dotnet run, the following commands are used:
Command prompt
console
Copy
set ASPNETCORE_ENVIRONMENT=Development
PowerShell
PowerShell
Copy
$Env:ASPNETCORE_ENVIRONMENT = “Development”
These commands only take effect for the current window. When the window is closed, the ASPNETCORE_ENVIRONMENT setting reverts to the default setting or machine value.
Set Environmental variable globally
To set the value globally in Windows, use either of the following approaches:
Open the Control Panel > System > Advanced system settings and add or edit the ASPNETCORE_ENVIRONMENT value:
Set Environmental variable globally
To set the value globally in Windows, use either of the following approaches:
Open the Control Panel > System > Advanced system settings and add or edit the ASPNETCORE_ENVIRONMENT value:
Open an administrative command prompt and use the setx command or open an administrative PowerShell command prompt and use [Environment]::SetEnvironmentVariable:
Command prompt
console
Copy
setx ASPNETCORE_ENVIRONMENT Development /M
The /M switch indicates to set the environment variable at the system level. If the /M switch isn’t used, the environment variable is set for the user account.
The Machine option value indicates to set the environment variable at the system level. If the option value is changed to User, the environment variable is set for the user account.
When the ASPNETCORE_ENVIRONMENT environment variable is set globally, it takes effect for dotnet run in any command window opened after the value is set.
Environment variable in web.config
To set the ASPNETCORE_ENVIRONMENT environment variable with web.config, see the Setting environment variables section of ASP.NET Core Module.
Project file or publish profile
For Windows IIS deployments: Include the property in the publish profile (.pubxml) or project file. This approach sets the environment in web.config when the project is published:
XML
Copy
Development
Environment variable per IIS Application Pool
Per IIS Application Pool
To set the ASPNETCORE_ENVIRONMENT environment variable for an app running in an isolated Application Pool (supported on IIS 10.0 or later), see the AppCmd.exe command section of the Environment Variables topic. When the ASPNETCORE_ENVIRONMENT environment variable is set for an app pool, its value overrides a setting at the system level.
Important
When hosting an app in IIS and adding or changing the ASPNETCORE_ENVIRONMENT environment variable, use any one of the following approaches to have the new value picked up by apps:
Execute net stop was /y followed by net start w3svc from a command prompt.
Restart the server.
Configuration by Environment; appsettings.{Environment}.json
Set the environment in code
Call UseEnvironment when building the host. See .NET Generic Host.
Configuration by environment
To load configuration by environment, we recommend:
appsettings files (appsettings.{Environment}.json). See Configuration in ASP.NET Core. Environment variables (set on each system where the app is hosted). See .NET Generic Host and Safe storage of app secrets in development in ASP.NET Core. Secret Manager (in the Development environment only). See Safe storage of app secrets in development in ASP.NET Core.
Environment-based Startup class for fewer changes
Inject IWebHostEnvironment into Startup.Configure
Inject IWebHostEnvironment into Startup.Configure. This approach is useful when the app only requires adjusting Startup.Configure for a few environments with minimal code differences per environment.
C#
Copy public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { // Development environment code } else { // Code for all other environments } } Inject IWebHostEnvironment into the Startup class Inject IWebHostEnvironment into the Startup constructor. This approach is useful when the app requires configuring Startup for only a few environments with minimal code differences per environment.
In the following example:
The environment is held in the _env field.
_env is used in ConfigureServices and Configure to apply startup configuration based on the app’s environment.
C#
Copy public class Startup { private readonly IWebHostEnvironment _env;
public Startup(IWebHostEnvironment env) { _env = env; }
public void ConfigureServices(IServiceCollection services) { if (_env.IsDevelopment()) { // Development environment code } else if (_env.IsStaging()) { // Staging environment code } else { // Code for all other environments } }
public void Configure(IApplicationBuilder app) { if (_env.IsDevelopment()) { // Development environment code } else { // Code for all other environments } } }
Environment-based Startup class for many changes
To implement environment-based Startup classes, create a Startup{EnvironmentName} class for each environment in use and a fallback Startup class:
C#
Copy // Startup class to use in the Development environment public class StartupDevelopment { public void ConfigureServices(IServiceCollection services) { }
public void Configure(IApplicationBuilder app, IHostingEnvironment env) { } }
// Startup class to use in the Production environment public class StartupProduction { public void ConfigureServices(IServiceCollection services) { }
public void Configure(IApplicationBuilder app, IHostingEnvironment env) { } }
// Fallback Startup class // Selected if the environment doesn't match a Startup{EnvironmentName} class public class Startup { public void ConfigureServices(IServiceCollection services) { }
public void Configure(IApplicationBuilder app, IHostingEnvironment env) { } } Use the UseStartup(IWebHostBuilder, String) overload that accepts an assembly name:
C#
Copy public static void Main(string[] args) { CreateWebHostBuilder(args).Build().Run(); }
public static IWebHostBuilder CreateWebHostBuilder(string[] args) { var assemblyName = typeof(Startup).GetTypeInfo().Assembly.FullName;
return WebHost.CreateDefaultBuilder(args) .UseStartup(assemblyName); }
Environment-based Startup methods for many changes
Startup method conventions
Configure and ConfigureServices support environment-specific versions of the form Configure and ConfigureServices. This approach is useful when the app requires configuring startup for several environments with many code differences per environment.
C#
Copy public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; }
public IConfiguration Configuration { get; } public void ConfigureServices(IServiceCollection services) { StartupConfigureServices(services); } public void ConfigureStagingServices(IServiceCollection services) { StartupConfigureServices(services); } private void StartupConfigureServices(IServiceCollection services) { services.AddMvc() .SetCompatibilityVersion(CompatibilityVersion.Version_2_2); } public void Configure(IApplicationBuilder app, IHostingEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } if (env.IsProduction() || env.IsStaging() || env.IsEnvironment("Staging_2")) { app.UseExceptionHandler("/Error"); } app.UseStaticFiles(); app.UseMvc(); }
public void ConfigureStaging(IApplicationBuilder app, IHostingEnvironment env) { if (!env.IsStaging()) { throw new Exception("Not staging."); }
app.UseExceptionHandler("/Error"); app.UseStaticFiles(); app.UseMvc(); } }
Startup class conventions
When an ASP.NET Core app starts, the Startup class bootstraps the app. The app can define separate Startup classes for different environments (for example, StartupDevelopment). The appropriate Startup class is selected at runtime. The class whose name suffix matches the current environment is prioritized. If a matching Startup{EnvironmentName} class isn’t found, the Startup class is used. This approach is useful when the app requires configuring startup for several environments with many code differences per environment.
To implement environment-based Startup classes, create a Startup{EnvironmentName} class for each environment in use and a fallback Startup class:
Safe storage of app secrets in development in ASP.NET Core
This document explains techniques for storing and retrieving sensitive data during development of an ASP.NET Core app on a development machine. Never store passwords or other sensitive data in source code. Production secrets shouldn’t be used for development or test. Secrets shouldn’t be deployed with the app. Instead, secrets should be made available in the production environment through a controlled means like environment variables, Azure Key Vault, etc. You can store and protect Azure test and production secrets with the Azure Key Vault configuration provider.
Environment variables for secrets- not recommended
Environment variables are generally stored in plain, unencrypted text. If the machine or process is compromised, environment variables can be accessed by untrusted parties. Additional measures to prevent disclosure of user secrets may be required.
Secret Manager tool
The Secret Manager tool abstracts away the implementation details, such as where and how the values are stored. You can use the tool without knowing these implementation details. The values are stored in a JSON configuration file in a system-protected user profile folder on the local machine:
Windows
Linux / macOS
File system path:
%APPDATA%\Microsoft\UserSecrets\secrets.json
In the preceding file paths, replace with the UserSecretsId value specified in the .csproj file.
Don’t write code that depends on the location or format of data saved with the Secret Manager tool. These implementation details may change. For example, the secret values aren’t encrypted, but could be in the future.
Access a Secret
Access a secret
The ASP.NET Core Configuration API provides access to Secret Manager secrets.
In ASP.NET Core 2.0 or later, the user secrets configuration source is automatically added in development mode when the project calls CreateDefaultBuilder to initialize a new instance of the host with preconfigured defaults. CreateDefaultBuilder calls AddUserSecrets when the EnvironmentName is Development:
C#
Copy
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup();
});
When CreateDefaultBuilder isn’t called, add the user secrets configuration source explicitly by calling AddUserSecrets in the Startup constructor. Call AddUserSecrets only when the app runs in the Development environment, as shown in the following example:
C#
Copy
public Startup(IWebHostEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile(“appsettings.json”,
optional: false,
reloadOnChange: true)
.AddEnvironmentVariables();
if (env.IsDevelopment()) { builder.AddUserSecrets(); } Configuration = builder.Build(); } User secrets can be retrieved via the Configuration API:
C#
Copy public class Startup { private string _moviesApiKey = null;
public Startup(IConfiguration configuration) { Configuration = configuration; }
public IConfiguration Configuration { get; } public void ConfigureServices(IServiceCollection services) { _moviesApiKey = Configuration["Movies:ServiceApiKey"]; }
public void Configure(IApplicationBuilder app) { app.Run(async (context) => { var result = string.IsNullOrEmpty(_moviesApiKey) ? "Null" : "Not Null"; await context.Response.WriteAsync($"Secret is {result}"); }); } }
POCO
POCO (Plain old CLR Objects)
String replacement with secrets
Storing passwords in plain text is insecure. For example, a database connection string stored in appsettings.json may include a password for the specified user:
JSON
Copy { "ConnectionStrings": { "Movies": "Server=(localdb)\\mssqllocaldb;Database=Movie-1;User Id=johndoe;Password=pass123;MultipleActiveResultSets=true" } }
A more secure approach is to store the password as a secret. For example:
.NET Core CLI
Copy
dotnet user-secrets set “DbPassword” “pass123”
Remove the Password key-value pair from the connection string in appsettings.json. For example:
JSON
Copy { "ConnectionStrings": { "Movies": "Server=(localdb)\\mssqllocaldb;Database=Movie-1;User Id=johndoe;MultipleActiveResultSets=true" } } ------ The secret's value can be set on a SqlConnectionStringBuilder object's Password property to complete the connection string:
C#
Copy public class Startup { private string _connection = null;
public Startup(IConfiguration configuration) { Configuration = configuration; }
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services) { var builder = new SqlConnectionStringBuilder( Configuration.GetConnectionString("Movies")); builder.Password = Configuration["DbPassword"]; _connection = builder.ConnectionString; }
public void Configure(IApplicationBuilder app) { app.Run(async (context) => { await context.Response.WriteAsync($"DB Connection: {_connection}"); }); } }
Task asynchronous programming model (TAP)
using System;
using System.Threading.Tasks;
using System.Windows;
// Add a using directive and a reference for System.Net.Http; using System.Net.Http;
namespace AsyncFirstExample { public partial class MainWindow : Window { // Mark the event handler with async so you can use await in it. private async void StartButton_Click(object sender, RoutedEventArgs e) { // Call and await separately. //Task getLengthTask = AccessTheWebAsync(); //// You can do independent work here. //int contentLength = await getLengthTask;
int contentLength = await AccessTheWebAsync(); resultsTextBox.Text += $"\r\nLength of the downloaded string: {contentLength}.\r\n"; }
// Three things to note in the signature: // - The method has an async modifier. // - The return type is Task or Task. (See "Return Types" section.) // Here, it is Task because the return statement returns an integer. // - The method name ends in "Async." async Task AccessTheWebAsync() { // You need to add a reference to System.Net.Http to declare client. var client = new HttpClient();
// GetStringAsync returns a Task. That means that when you await the // task you'll get a string (urlContents). Task getStringTask = client.GetStringAsync("https://docs.microsoft.com/dotnet");
// You can do work here that doesn't rely on the string from GetStringAsync. DoIndependentWork();
// The await operator suspends AccessTheWebAsync. // - AccessTheWebAsync can't continue until getStringTask is complete. // - Meanwhile, control returns to the caller of AccessTheWebAsync. // - Control resumes here when getStringTask is complete. // - The await operator then retrieves the string result from getStringTask. string urlContents = await getStringTask;
// The return statement specifies an integer result. // Any methods that are awaiting AccessTheWebAsync retrieve the length value. return urlContents.Length; }
void DoIndependentWork() { resultsTextBox.Text += "\r\nWorking . . . . . . .\r\n"; } } }
// Sample Output:
// Working . . . . . . .
// Length of the downloaded string: 41564.
TAP Naming conventions
By convention, methods that return commonly awaitable types (e.g. Task, Task, ValueTask, ValueTask) should have names that end with “Async”. Methods that start an asynchronous operation but do not return an awaitable type should not have names that end with “Async”, but may start with “Begin”, “Start”, or some other verb to suggest this method does not return or throw the result of the operation.
You can ignore the convention where an event, base class, or interface contract suggests a different name. For example, you shouldn’t rename common event handlers, such as Button1_Click.
async and await common rules
1:Avoid Using Async Void; instead use async Task whenever possible, where T is the return type of your method.
2:DoSomeStuff(); // synchronous method
await DoSomeLengthyStuffAsync(); // long-running asynchronous method
DoSomeMoreStuff(); // another synchronous method
This allows the compiler to split the calling method at the point of the await keyword. The first part ends with the asynchronous method call; the second part starts with using its result if any, and continues from there on.
3:This allows the compiler to split the calling method at the point of the await keyword. The first part ends with the asynchronous method call; the second part starts with using its result if any, and continues from there on.
4:In order to use the await keyword on a method; its return type must be Task. This allows the compiler to trigger the continuation of our method, once the task completes. In other words, this will work as long as the asynchronous method’s signature is async Task. Had the signature been async void instead, we would have to call it without the await keyword:
DoSomeStuff(); // synchronous method
DoSomeLengthyStuffAsync(); // long-running asynchronous method
DoSomeMoreStuff(); // another synchronous method
5:Execution difference of with wait and without wait for an async method call
DoSomeStuff(); // synchronous method
DoSomeLengthyStuffAsync(); // long-running asynchronous method
DoSomeMoreStuff(); // another synchronous method
The compiler would not complain though. Depending on the side effects of DoSomeLengthyStuffAsync, the code might even work correctly. However, there is one important difference between the two examples. In the first one, DoSomeMoreStuff will only be invoked after DoSomeLengthyStuffAsync completes. In the second one, DoSomeMoreStuff will be invoked immediately after DoSomeLengthyStuffAsync starts. Since in the latter case DoSomeLengthyStuffAsync and DoSomeMoreStuff run in parallel, race conditions might occur. If DoSomeMoreStuff depends on any of DoSomeLengthyStuffAsync’s side effects, these might or might not yet be available when DoSomeMoreStuff wants to use them. Such a bug can be difficult to fix, because it cannot be reproduced reliably. It can also occur only in production environment, where I/O operations are usually slower than in development environment.
6:Always use async task
To avoid such issues altogether, always use async Task as the signature for methods you intend to call from your code. Restrict the usage of async void signature to event handlers, which are not allowed to return anything, and make sure you never call them yourself. If you need to reuse the code in an event handler, refactor it into a separate method returning Task, and call that new method from both the event handler and your method, using await.
7:Beware of Deadlocks
In a way, asynchronous methods behave contagiously. To call an asynchronous method with await, you must make the calling method asynchronous as well, even if it was not async before. Now, all methods calling this newly asynchronous method must also become asynchronous. This pattern repeats itself up the call stack until it finally reaches the entry points, e.g. event handlers.
When one of the methods on this path to the entry points cannot be asynchronous, this poses a problem. For example, constructors. They cannot be asynchronous, therefore you cannot use await in their body. As discussed in the previous section, you could break the asynchronous requirement early by giving a method async void signature, but this prevents you from waiting for its execution to end, which makes it a bad idea in most cases.
Alternatively, you could try synchronously waiting for the asynchronous method to complete, by calling Wait on the returned Task, or reading its Result property. Of course, this synchronous code will temporarily stop your application from processing the message queue, which we wanted to avoid in the first place. Even worse, in some cases you could cause a deadlock in your application with some very innocent looking code:
private async void MyEventHandler(object sender, RoutedEventArgs e) { var instance = new InnocentLookingClass(); // further code } Any synchronously called asynchronous code in InnocentLookingClass constructor is enough to cause a deadlock:
public class InnocentLookingClass() { public InnocentLookingClass() { DoSomeLengthyStuffAsync().Wait(); // do some more stuff }
private async Task DoSomeLengthyStuffAsync() { await SomeOtherLengthyStuffAsync(); }
// other class members } Let us dissect what is happening in this code.
MyEventHandler synchronously calls InnocentLookingClass constructor, which invokes DoSomeLengthyStuffAsync, which in turn asynchronously invokes SomeOtherLengthyStuffAsync. The execution of the latter method starts; at the same time the main thread blocks at Wait until DoSomeLengthyStuffAsync completes without giving control back to the main message loop.
Eventually SomeOtherLengthyStuffAsync completes and posts a message to the message queue implying that the execution of DoSomeLengthyStuffAsync can continue. Unfortunately, the main thread is waiting for that method to complete instead of processing the messages, and will therefore never trigger it to continue, hence waiting indefinitely.
As you can see, synchronously invoking asynchronous methods can quickly have undesired consequences. Avoid it at all costs; unless you are sure what you are doing, i.e. you are not blocking the main message loop.
Using statement ; using translates into try,catch and finally
Provides a convenient syntax that ensures the correct use of IDisposable objects.
The following example shows how to use the using statement.
C#
Copy using (var font1 = new Font("Arial", 10.0f)) { byte charset = font1.GdiCharSet; } Beginning with C# 8.0, you can use the following alternative syntax for the using statement that doesn't require braces:
C#
Copy using var font1 = new Font("Arial", 10.0f); byte charset = font1.GdiCharSet;
File and Font are examples of managed types that access unmanaged resources (in this case file handles and device contexts). There are many other kinds of unmanaged resources and class library types that encapsulate them. All such types must implement the IDisposable interface.
When the lifetime of an IDisposable object is limited to a single method, you should declare and instantiate it in the using statement. The using statement calls the Dispose method on the object in the correct way, and (when you use it as shown earlier) it also causes the object itself to go out of scope as soon as Dispose is called. Within the using block, the object is read-only and cannot be modified or reassigned.
using statement and exception handling
The using statement ensures that Dispose is called even if an exception occurs within the using block. You can achieve the same result by putting the object inside a try block and then calling Dispose in a finally block; in fact, this is how the using statement is translated by the compiler. The code example earlier expands to the following code at compile time (note the extra curly braces to create the limited scope for the object):
C#
Copy { var font1 = new Font("Arial", 10.0f); try { byte charset = font1.GdiCharSet; } finally { if (font1 != null) ((IDisposable)font1).Dispose(); } } The newer using statement syntax translates to very similar code. The try block opens where the variable is declared. The finally block is added at the close of the enclosing block, typically at the end of a method.
Finally calls the IDisposable.Dispose() method
using statement with multiple objects of same type
Multiple instances of a type can be declared in the using statement, as shown in the following example:
C#
Copy using (Font font3 = new Font("Arial", 10.0f), font4 = new Font("Arial", 10.0f)) { // Use font3 and font4. } You can combine multiple declarations of the same type using the new syntax introduced with C# 8 as well. This is shown in the following example:
C#
Copy using Font font3 = new Font("Arial", 10.0f), font4 = new Font("Arial", 10.0f); // Use font3 and font4. You can instantiate the resource object and then pass the variable to the using statement, but this is not a best practice. In this case, after control leaves the using block, the object remains in scope but probably has no access to its unmanaged resources. In other words, it's not fully initialized anymore. If you try to use the object outside the using block, you risk causing an exception to be thrown. For this reason, it's generally better to instantiate the object in the using statement and limit its scope to the using block.
C#
Copy var font2 = new Font("Arial", 10.0f); using (font2) // not recommended { // use font2 } // font2 is still in scope // but the method call throws an exception float f = font2.GetHeight();
Cleaning Up Unmanaged Resources
For the majority of the objects that your app creates, you can rely on .NET’s garbage collector to handle memory management. However, when you create objects that include unmanaged resources, you must explicitly release those resources when you finish using them in your app. The most common types of unmanaged resource are objects that wrap operating system resources, such as files, windows, network connections, or database connections. Although the garbage collector is able to track the lifetime of an object that encapsulates an unmanaged resource, it doesn’t know how to release and clean up the unmanaged resource.
If your types use unmanaged resources, you should do the following:
Implement the dispose pattern. This requires that you provide an IDisposable.Dispose implementation to enable the deterministic release of unmanaged resources. A consumer of your type calls Dispose when the object (and the resources it uses) is no longer needed. The Dispose method immediately releases the unmanaged resources.
Provide for your unmanaged resources to be released in the event that a consumer of your type forgets to call Dispose. There are two ways to do this:
Use a safe handle to wrap your unmanaged resource. This is the recommended technique. Safe handles are derived from the System.Runtime.InteropServices.SafeHandle class and include a robust Finalize method. When you use a safe handle, you simply implement the IDisposable interface and call your safe handle’s Dispose method in your IDisposable.Dispose implementation. The safe handle’s finalizer is called automatically by the garbage collector if its Dispose method is not called.
—or—
Override the Object.Finalize method. Finalization enables the non-deterministic release of unmanaged resources when the consumer of a type fails to call IDisposable.Dispose to dispose of them deterministically. However, because object finalization can be a complex and error-prone operation, we recommend that you use a safe handle instead of providing your own finalizer.
Consumers of your type can then call your IDisposable.Dispose implementation directly to free memory used by unmanaged resources. When you properly implement a Dispose method, either your safe handle’s Finalize method or your own override of the Object.Finalize method becomes a safeguard to clean up resources in the event that the Dispose method is not called.
async and await
async- specifies a method is an asychrnous method
await - it specifies a suspension point, it says that async method can not pass that point untill the await task is complete.In the mean time control passes to the caller of the async method
you might have multiple await in an async method
async with out return (Task) example - Might not work
using System; using System.IO; using System.Threading; using System.Threading.Tasks; namespace TAPModel { class Program { static void Main(string[] args) {
Console.WriteLine("From Main before calling Async");
//The below async method is called CallReadFromFile();
//Control is returned when the async method wait is encountered and the below command is executed Console.WriteLine($"From Main after calling Async");
//If the readline does not exist the code ends with errors; as the async method //has not yet returned the results //but with readline the console is open and waits for the async output to show up Console.ReadLine();
}
public static int ReadFromFile() { using (StreamReader streamReader = new StreamReader("TestData.txt")) { Thread.Sleep(10000); string content= streamReader.ReadToEnd(); return content.Length; } } public async static Task CallReadFromFile() { //Creating a task for the method that takes time to execute Task task = new Task(ReadFromFile); //starting the task task.Start();
//waiting on the task response; here the control goes back to calling method int count = await task;
//once the count value is available the control returns to here and the below statements get executed Console.WriteLine(count); Console.WriteLine("The above is the result From async method"); //after this the control returns back to the calling method } } }
async method with Task returning to the main method - might not work
using System; using System.IO; using System.Threading; using System.Threading.Tasks; namespace TAPModel { class Program { static void Main(string[] args) {
Console.WriteLine("From Main before calling Async");
//The below async method is called; to convert Task to int = we use .Result property int output= CallReadFromFile().Result;
//it waits for the async method to complete; all its operations and then the control is returned to here Console.WriteLine($"From Main after calling Async, the output is : {output}");
Console.WriteLine("After the async method is being called and there is no dependency on waiting method");
//No need of below line, as the code is automatically for the asyc function to complete the task // Console.ReadLine();
}
public static int ReadFromFile() { using (StreamReader streamReader = new StreamReader("TestData.txt")) { Thread.Sleep(10000); string content= streamReader.ReadToEnd(); return content.Length; } } public async static Task CallReadFromFile() { //Creating a task for the method that takes time to execute Task task = new Task(ReadFromFile); //starting the task task.Start();
//waiting on the task response; here the control goes back to calling method int count = await task;
Console.WriteLine("After the await is being called");
//once the count value is available the control returns to here and the below statements get executed return count;
//after this the control returns back to the calling method } } }
async return type Task
for an async method that returns a value.
using System;
using System.Linq;
using System.Threading.Tasks;
public class Example
{
public static void Main()
{
Console.WriteLine(“Inside the Main method, before calling Result property on async method”);
Console.WriteLine(ShowTodaysInfo().Result);
Console.WriteLine(“Inside the Main method, after calling Result property on async method”);
}
private static async Task ShowTodaysInfo() { Console.WriteLine("Inside the ShowTodaysInfo method, before await"); string ret = $"Today is {DateTime.Today:D}\n" + "Today's hours of leisure: " + $"{await GetLeisureHours()}"; Console.WriteLine("Inside the ShowTodaysInfo method, after await"); return ret; } static async Task GetLeisureHours() { Console.WriteLine("Inside the GetLeisureHours method, before await"); // Task.FromResult is a placeholder for actual work that returns a string. var today = await Task.FromResult(DateTime.Now.DayOfWeek.ToString()); Console.WriteLine("Inside the GetLeisureHours method, after await"); // The method then can process the result in some way. int leisureHours; if (today.First() == 'S') leisureHours = 16; else leisureHours = 5; return leisureHours; } }
/* OUTPUT
Inside the Main method, before calling Result property on async method
Inside the ShowTodaysInfo method, before await
Inside the GetLeisureHours method, before await
Inside the GetLeisureHours method, after await
Inside the ShowTodaysInfo method, after await
Today is Saturday, February 15, 2020
Today’s hours of leisure: 16
Inside the Main method, after calling Result property on async method
The before wait calls are executed in Top to Down apporach; but the after await calls are exeuted in bottom to Top approach; so it means the lower level async method completes first and then the chains up */
Task Return Type
The Task return type is used for an async method that contains a return (C#) statement in which the operand has type TResult.
In the following example, the GetLeisureHours async method contains a return statement that returns an integer. Therefore, the method declaration must specify a return type of Task. The FromResult async method is a placeholder for an operation that returns a string.
When GetLeisureHours is called from within an await expression in the ShowTodaysInfo method, the await expression retrieves the integer value (the value of leisureHours) that’s stored in the task returned by the GetLeisureHours method. For more information about await expressions, see await.
You can better understand how this happens by separating the call to GetLeisureHours from the application of await, as the following code shows. A call to method GetLeisureHours that isn’t immediately awaited returns a Task, as you would expect from the declaration of the method. The task is assigned to the integerTask variable in the example. Because integerTask is a Task, it contains a Result property of type TResult. In this case, TResult represents an integer type. When await is applied to integerTask, the await expression evaluates to the contents of the Result property of integerTask. The value is assigned to the ret variable.
Important
The Result property is a blocking property. If you try to access it before its task is finished, the thread that’s currently active is blocked until the task completes and the value is available. In most cases, you should access the value by using await instead of accessing the property directly.
The previous example retrieved the value of the Result property to block the main thread so that the ShowTodaysInfo method could finish execution before the application ended.
C#
Copy var integerTask = GetLeisureHours();
// You can do other work that does not rely on integerTask before awaiting.
string ret = $”Today is {DateTime.Today:D}\n” +
“Today’s hours of leisure: “ +
$”{await integerTask}”;
async return type Task
for an async method that performs an operation but returns no value.
Async methods that don’t contain a return statement or that contain a return statement that doesn’t return an operand usually have a return type of Task. Such methods return void if they run synchronously. If you use a Task return type for an async method, a calling method can use an await operator to suspend the caller’s completion until the called async method has finished.
In the following example, the WaitAndApologize async method doesn’t contain a return statement, so the method returns a Task object. This enables WaitAndApologize to be awaited. Note that the Task type doesn’t include a Result property because it has no return value.
using System;
using System.Threading.Tasks;
public class Example { public static void Main() { Console.WriteLine("Inside the Main method, before calling async method");
//This wait() method stops the program execution till we obtain result from async method //it works synchronously DisplayCurrentInfo().Wait();
Console.WriteLine("Inside the Main method, after calling async method"); } static async Task DisplayCurrentInfo() { Console.WriteLine("Inside the DisplayCurrentInfo method, before calling await method"); await WaitAndApologize(); Console.WriteLine("Inside the DisplayCurrentInfo method, after calling await method"); Console.WriteLine($"Today is {DateTime.Now:D}"); Console.WriteLine($"The current time is {DateTime.Now.TimeOfDay:t}"); Console.WriteLine("The current temperature is 76 degrees."); } static async Task WaitAndApologize() {
Console.WriteLine("Inside the WaitAndApologize method, before calling await method for delay"); // Task.Delay is a placeholder for actual work. await Task.Delay(2000);
Console.WriteLine("Inside the WaitAndApologize method, after calling await method for delay"); // Task.Delay delays the following line by two seconds. Console.WriteLine("\nSorry for the delay. . . .\n"); } } // The example displays the following output: // Sorry for the delay. . . . // // Today is Wednesday, May 24, 2017 // The current time is 15:25:16.2935649 // The current temperature is 76 degrees.
/*
* WITH OUT THE WAIT() IN THE MAIN- THE program does not wait for the async method to complete execution
*
Inside the Main method, before calling async method
Inside the DisplayCurrentInfo method, before calling await method
Inside the WaitAndApologize method, before calling await method for delay
Inside the Main method, after calling async method
*/
/*
* WITH THE WAIT() IN THE MAIN- THE program waits for the async method to complete execution
*
Inside the Main method, before calling async method
Inside the DisplayCurrentInfo method, before calling await method
Inside the WaitAndApologize method, before calling await method for delay
Inside the WaitAndApologize method, after calling await method for delay
Sorry for the delay. . . .
Inside the DisplayCurrentInfo method, after calling await method
Today is Sunday, February 16, 2020
The current time is 08:27:21.8715125
The current temperature is 76 degrees.
Inside the Main method, after calling async method
*/
WaitAndApologize is awaited by using an await statement instead of an await expression, similar to the calling statement for a synchronous void-returning method. The application of an await operator in this case doesn’t produce a value.
As in the previous Task example, you can separate the call to WaitAndApologize from the application of an await operator, as the following code shows. However, remember that a Task doesn’t have a Result property, and that no value is produced when an await operator is applied to a Task.
The following code separates calling the WaitAndApologize method from awaiting the task that the method returns.
C#
Copy
Task wait = WaitAndApologize();
string output = $”Today is {DateTime.Now:D}\n” +
$”The current time is {DateTime.Now.TimeOfDay:t}\n” +
$”The current temperature is 76 degrees.\n”;
await wait;
Console.WriteLine(output);
async return type Void
for event Handler
You use the void return type in asynchronous event handlers, which require a void return type. For methods other than event handlers that don’t return a value, you should return a Task instead, because an async method that returns void can’t be awaited. Any caller of such a method must be able to continue to completion without waiting for the called async method to finish, and the caller must be independent of any values or exceptions that the async method generates.
The caller of a void-returning async method can’t catch exceptions that are thrown from the method, and such unhandled exceptions are likely to cause your application to fail. If an exception occurs in an async method that returns a Task or Task, the exception is stored in the returned task and is rethrown when the task is awaited. Therefore, make sure that any async method that can produce an exception has a return type of Task or Task and that calls to the method are awaited.
For more information about how to catch exceptions in async methods, see the Exceptions in Async Methods section of the try-catch topic.
The following example shows the behavior of an async event handler. Note that in the example code, an async event handler must let the main thread know when it finishes. Then the main thread can wait for an async event handler to complete before exiting the program.
using System;
using System.Threading.Tasks;
public class NaiveButton { public event EventHandler Clicked;
public void Click() { Console.WriteLine("Somebody has clicked a button. Let's raise the event..."); Clicked?.Invoke(this, EventArgs.Empty); Console.WriteLine("All listeners are notified."); } }
public class AsyncVoidExample { static TaskCompletionSource tcs;
static async Task Main() { tcs = new TaskCompletionSource(); var secondHandlerFinished = tcs.Task;
var button = new NaiveButton(); button. Clicked += Button_Clicked_1; button. Clicked += Button_Clicked_2_Async; button. Clicked += Button_Clicked_3; Console.WriteLine("About to click a button..."); button.Click(); Console.WriteLine("Button's Click method returned."); await secondHandlerFinished; } private static void Button_Clicked_1(object sender, EventArgs e) { Console.WriteLine(" Handler 1 is starting..."); Task.Delay(100).Wait(); Console.WriteLine(" Handler 1 is done."); } private static async void Button_Clicked_2_Async(object sender, EventArgs e) { Console.WriteLine(" Handler 2 is starting..."); Task.Delay(100).Wait(); Console.WriteLine(" Handler 2 is about to go async..."); await Task.Delay(500); Console.WriteLine(" Handler 2 is done."); tcs.SetResult(true); } private static void Button_Clicked_3(object sender, EventArgs e) { Console.WriteLine(" Handler 3 is starting..."); Task.Delay(100).Wait(); Console.WriteLine(" Handler 3 is done."); } }
// Expected output: // About to click a button... // Somebody has clicked a button. Let's raise the event... // Handler 1 is starting... // Handler 1 is done. // Handler 2 is starting... // Handler 2 is about to go async... // Handler 3 is starting... // Handler 3 is done. // All listeners are notified. // Button's Click method returned. // Handler 2 is done.
await
this stops the exection of the program and returns to the calling function -This also converts Task into Tresult like Task to int -it gets the values of Result property but it is non-blocking which means other codes could be running await GetLeisureHours()
Result property of Task
it converts the Task to TResult but unlike await it is blocking property; so your code will stop for the result to be available.
we could use this in the Main () method ; so that we get the value from the async method before the program ends
Task.FromResult(TResult) Method
Creates a Task that’s completed successfully with the specified result.
This method creates a Task object whose Task.Result property is result and whose Status property is RanToCompletion. The method is commonly used when the return value of a task is immediately known without executing a longer code path. The example provides an illustration.
// Task.FromResult is a placeholder for actual work that returns a string. var today = await Task.FromResult(DateTime.Now.DayOfWeek.ToString());
Tasks could be a list of Tasks
List> tasks = new List>();
Task Methods
Task.WaitAll(tasks.ToArray());
Task.Run()
Task.Add()
Task.Run()-Queues the specified work to run on the ThreadPool and returns a task or Task handle for that work.
Task Properties
Task.Status
The current TaskStatus of this task instance.
Retrieving the value of the Task.Status property does not block the calling thread until the task has completed.
Wait method vs await operand - while similar conceptually - are actually completely different.
Wait will synchronously block until the task completes. So the current thread is literally blocked waiting for the task to complete. As a general rule, you should use “async all the way down”; that is, don’t block on async code. On my blog, I go into the details of how blocking in asynchronous code causes deadlock.
await will asynchronously wait until the task completes. This means the current method is “paused” (its state is captured) and the method returns an incomplete task to its caller. Later, when the await expression completes, the remainder of the method is scheduled as a continuation.
Task.Wait Method
Waits for the Task to complete execution.
Wait(TimeSpan) is a synchronization method that causes the calling thread to wait for the current task instance to complete until one of the following occurs:
The task completes successfully.
The task itself is canceled or throws an exception. In this case, you handle an AggregateException exception. The AggregateException.InnerExceptions property contains details about the exception or exceptions.
The interval defined by timeout elapses. In this case, the current thread resumes execution and the method returns false.
Creating a task for async method to await is optional, you could directly await on the async method in the calling function
static async Task DisplayCurrentInfo() { await WaitAndApologize(); Console.WriteLine($"Today is {DateTime.Now:D}"); Console.WriteLine($"The current time is {DateTime.Now.TimeOfDay:t}"); Console.WriteLine("The current temperature is 76 degrees."); }
static async Task WaitAndApologize() { // Task.Delay is a placeholder for actual work. await Task.Delay(2000); // Task.Delay delays the following line by two seconds. Console.WriteLine("\nSorry for the delay. . . .\n"); }
Task wait = WaitAndApologize();
string output = $”Today is {DateTime.Now:D}\n” +
$”The current time is {DateTime.Now.TimeOfDay:t}\n” +
$”The current temperature is 76 degrees.\n”;
await wait;
Console.WriteLine(output);
Generalized async return types and ValueTask
to use use value type instead of Reference types
Generalized async return types and ValueTask
Starting with C# 7.0, an async method can return any type that has an accessible GetAwaiter method.
Because Task and Task are reference types, memory allocation in performance-critical paths, particularly when allocations occur in tight loops, can adversely affect performance. Support for generalized return types means that you can return a lightweight value type instead of a reference type to avoid additional memory allocations.
.NET provides the System.Threading.Tasks.ValueTask structure as a lightweight implementation of a generalized task-returning value. To use the System.Threading.Tasks.ValueTask type, you must add the System.Threading.Tasks.Extensions NuGet package to your project. The following example uses the ValueTask structure to retrieve the value of two dice rolls.
using System;
using System.Threading.Tasks;
class Program { static Random rnd;
static void Main() { Console.WriteLine("Inside the Main method, before calling async method"); Console.WriteLine($"You rolled {GetDiceRoll().Result}"); Console.WriteLine("Inside the Main method, after calling async method"); } private static async ValueTask GetDiceRoll() { Console.WriteLine("...Shaking the dice..."); Console.WriteLine("Inside the GetDiceRoll method, before calling await method"); int roll1 = await Roll(); Console.WriteLine("Inside the GetDiceRoll method, after the first calling to await"); int roll2 = await Roll(); Console.WriteLine("Inside the GetDiceRoll method, after the second calling to await"); return roll1 + roll2; } private static async ValueTask Roll() { if (rnd == null) rnd = new Random(); Console.WriteLine("Inside the Roll method, before the calling to await"); await Task.Delay(500);
Console.WriteLine("Inside the Roll method, after the calling to await"); int diceRoll = rnd.Next(1, 7); return diceRoll; } } // The example displays output like the following: // ...Shaking the dice... // You rolled 8
/*
* Inside the Main method, before calling async method
…Shaking the dice…
Inside the GetDiceRoll method, before calling await method
Inside the Roll method, before the calling to await
Inside the Roll method, after the calling to await
Inside the GetDiceRoll method, after the first calling to await
Inside the Roll method, before the calling to await
Inside the Roll method, after the calling to await
Inside the GetDiceRoll method, after the second calling to await
You rolled 10
Inside the Main method, after calling async method
*/
Random class
Random rnd = new Random(); int diceRoll = rnd.Next(1, 7); Randomly selects a value between 1 and 7
For the task to wait and await on it
await Task.Delay(500);
dataset vs datable to get data from database
Use a DataTable.
A DataSet is an in-memory database while DataTable is an in-memory table.
DataSets are more complicated and heavier-weight; they can contain multiple DataTables and relations between DataTables
you can better use DataTable(uses less memory).
or you can try with user created value objects or DTO
DataReader, DataSet, DataAdapter and DataTable are four major components of ADO.NET
These are different objects to read data from database
DataReader - ADO.NET
DataReader is used to read the data from database and it is a read and forward only connection oriented architecture during fetch the data from database. DataReader will fetch the data very fast when compared with dataset. Generally we will use ExecuteReader object to bind data to datareader.
To bind DataReader data to GridView we need to write the code like as shown below:
Protected void BindGridview() {
using(SqlConnection conn = new SqlConnection(“Data Source=abc;Integrated Security=true;Initial Catalog=Test”)) {
con.Open();
SqlCommand cmd = new SqlCommand(“Select UserName, First Name,LastName,Location FROM Users”, conn);
SqlDataReader sdr = cmd.ExecuteReader();
gvUserInfo.DataSource = sdr;
gvUserInfo.DataBind();
conn.Close();
}
}
Holds the connection open until you are finished (don’t forget to close it!).
Can typically only be iterated over once
Is not as useful for updating back to the database