I have an asp.net core 3.1 application and I'm trying to inject the framework service IHostEnvironment in my ConfigureServices so I can get the environment in my application service and the application is throwing an error.
Startup.cs:
private IHostEnvironment _env;
public Startup(IConfiguration configuration, IHostEnvironment hostEnvironment)
{
Configuration = configuration;
_env = hostEnvironment;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
//add framework services
services.AddSingleton<IHostEnvironment>(_env);
//add application services
services.AddSingleton<IMySvc, MySvc>();
}
MySvc.cs
public class MySvc : IMySvc
{
private IConfigurationRoot _config;
//private IHostingEnvironment _env;
private IHostEnvironment _env;
public string Env{
get{
if(_env.IsDevelopment()){return _config["MyConfiguration: MyProperty"];}
}
}
public HttpSvc(IConfigurationRoot config, IHostEnvironment env)
{
_config = config;
_env = env;
}
}
The application fails to run complaining about some services not being able to be constructed.
As mentioned in the migration guide from 2.2 to 3.0 IWebHostEnvironment should be injected in the Startup class instead of IHostingEnvironment. Also I would suggest to consider injecting IConfiguration into service instead of IConfigurationRoot or even better consider to bind/register relevant config parts to some class and inject it (see Configuration in ASP.NET Core and Options pattern in ASP.NET Core
)
I'm trying to register a singleton class, providing the constructor parameters in Startup.ConfigureServices method.
After several tries, I'm still not able to make the dbContext injection working
public void ConfigureServices(IServiceCollection services)
{
services.AddGrpc();
services.AddDbContext<EFContext>();
services.AddSingleton<OPCClient>(x =>
{
string endpointURL = "opc.tcp://xxx.yyy.zzz.nnn:12345";
bool autoAccept = false;
int stopTimeout = Timeout.Infinite;
var efContext = x.GetService<EFContext>();
OPCClient client = new OPCClient(endpointURL, autoAccept, stopTimeout, efContext);
client.Run();
return client;
});
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
// warmup
app.ApplicationServices.GetService<OPCClient>();
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapGrpcService<OPCService>();
endpoints.MapGet("/", async context =>
{
await context.Response.WriteAsync("Communication with gRPC endpoints must be made through a gRPC client. To learn how to create a client, visit: https://go.microsoft.com/fwlink/?linkid=2086909");
});
});
When var efContext = x.GetService<EFContext>(); is executed, I'm getting the exception
System.InvalidOperationException: 'Cannot resolve scoped service 'EFContext' from root provider.'
Thanks for any help in injecting the DbContext in OPCClient class
It is not a good choice to use a scoped service (the EFContext) inside a singleton.
The DI container creates a new instance of a scoped service for every request, while it creates a singleton only once and this can lead to inconsistent states for your objects. Documentation here
I suggest to change the lifetime of OPCClient to scoped - using services.AddScoped instead of services.AddSingleton. If you cannot do this, pass a reference of IServiceProvider rather than EFContext and resolve that service from the container each time you need to use it:
public class OPCClient
{
private IServicePrivder _serviceProvider;
public OPCClient (IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public void DoSomething() {
EfContext efContext = _serviceProvider.GetRequiredService<EfContext>();
}
}
I'm trying to setup a .net core 2.0 web application to send an email when user registers and also to recover password. I have followed this tutorial: https://learn.microsoft.com/en-us/aspnet/core/security/app-secrets?tabs=visual-studio.
However, upon reading the comments sections, it seems that the tutorial is not updated for Core 2.0. My question is, when I get to part "Add the user secrets configuration source to the Startup method", I cannot figure out how the startup file should look like since my startup file is different from the one showed there. Can anyone help me by showing me how the startup file should look like? Thanks.
This is my current startup file:
public class Startup
{
string _testSecret = null;
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
_testSecret = Configuration["MySecret"];
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddIdentity<ApplicationUser, IdentityRole>(config =>
{
config.SignIn.RequireConfirmedEmail = true;
})
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
// Add application services.
services.AddTransient<IEmailSender, EmailSender>();
services.AddMvc();
services.Configure<AuthMessageSenderOptions>(Configuration);
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
var builder = new ConfigurationBuilder();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseBrowserLink();
app.UseDatabaseErrorPage();
builder.AddUserSecrets<Startup>();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
app.UseAuthentication();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
}
In your code I didn't find invoking of build() function of ConfigurationBuilder class. Here is the sample code.
var builder = new ConfigurationBuilder();
builder.AddUserSecrets<Startup>();
var config builder.Build(); //This line is missing from your code
string mySecret = config ['EmailAccount'];
Refernce: https://learn.microsoft.com/en-us/aspnet/core/security/app-secrets?view=aspnetcore-2.1&tabs=windows#access-a-secret
I'm having trouble with Autofac resolve. I can't use this with mvc 6 beta7.
Using dependences:
"Autofac": "4.0.0-beta7-130",
"Microsoft.AspNet.Mvc": "6.0.0-beta7",
My Startup.cs
public IContainer Container { get; set; }
// For more information on how to configure your application, visit http://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
// Create the autofac container
var builder = new ContainerBuilder();
// Create the container and use the default application services as a fallback
//AutofacRegistration.Populate(builder, services);
// Add any Autofac modules or registrations.
builder.RegisterModule(new AutofacModule());
Container = builder.Build();
}
public void Configure(IApplicationBuilder app)
{
//app.Run(async (context) =>
//{
// await context.Response.WriteAsync("Hello World!");
//});
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
// Uncomment the following line to add a route for porting Web API 2 controllers.
// routes.MapWebApiRoute("DefaultApi", "api/{controller}/{id?}");
});
app.ApplicationServices = Container.Resolve<IServiceProvider>();
}
Take this exception
An exception of type 'Autofac.Core.Registration.ComponentNotRegisteredException' occurred in Autofac.dll but was not handled in user code
Additional information: The requested service 'System.IServiceProvider' has not been registered. To avoid this exception, either register a component to provide the service, check for service registration using IsRegistered(), or use the ResolveOptional() method to resolve an optional dependency.
How to use autofac with MVC 6 beta 7?
You can replace the ConfigureServices method with the following to get autofac to work.
public IServiceProvider ConfigureServices(IServiceCollection services)
{
// Add MVC services to the services container.
services.AddMvc();
// Create the autofac container
var builder = new ContainerBuilder();
// Create the container and use the default application services as a fallback
//AutofacRegistration.Populate(builder, services);
// Populate the services.
builder.Populate(services);
// Add any Autofac modules or registrations.
builder.RegisterModule(new AutofacModule());
Container = builder.Build();
// Resolve and return the service provider.
return Container.Resolve<IServiceProvider>();
}
How do I get the Development/Staging/production Hosting Environment in the ConfigureServices method in Startup?
public void ConfigureServices(IServiceCollection services)
{
// Which environment are we running under?
}
The ConfigureServices method only takes a single IServiceCollection parameter.
You can easily access it in ConfigureServices, just persist it to a property during Startup method which is called first and gets it passed in, then you can access the property from ConfigureServices.
public Startup(IWebHostEnvironment env, IApplicationEnvironment appEnv)
{
...your code here...
CurrentEnvironment = env;
}
private IWebHostEnvironment CurrentEnvironment{ get; set; }
public void ConfigureServices(IServiceCollection services)
{
string envName = CurrentEnvironment.EnvironmentName;
... your code here...
}
If you need to test this somewhere in your codebase that doesn't have easy access to the IHostingEnvironment, another easy way to do it is like this:
bool isDevelopment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") == "Development";
TL;DR
Set an environment variable called ASPNETCORE_ENVIRONMENT with the name of the environment (e.g. Production). Then do one of two things:
Inject IHostingEnvironment into Startup.cs, then use that (env here) to check: env.IsEnvironment("Production"). Do not check using env.EnvironmentName == "Production"!
Use either separate Startup classes or individual Configure/ConfigureServices functions. If a class or the functions match these formats, they will be used instead of the standard options on that environment.
Startup{EnvironmentName}() (entire class) || example: StartupProduction()
Configure{EnvironmentName}() || example: ConfigureProduction()
Configure{EnvironmentName}Services() || example: ConfigureProductionServices()
Full explanation
The .NET Core docs describe how to accomplish this. Use an environment variable called ASPNETCORE_ENVIRONMENT that's set to the environment you want, then you have two choices.
Check environment name
From the docs:
The IHostingEnvironment service provides the core abstraction for working with environments. This service is provided by the ASP.NET hosting layer, and can be injected into your startup logic via Dependency Injection. The ASP.NET Core web site template in Visual Studio uses this approach to load environment-specific configuration files (if present) and to customize the app’s error handling settings. In both cases, this behavior is achieved by referring to the currently specified environment by calling EnvironmentName or IsEnvironment on the instance of IHostingEnvironment passed into the appropriate method.
NOTE: Checking the actual value of env.EnvironmentName is not recommended!
If you need to check whether the application is running in a particular environment, use env.IsEnvironment("environmentname") since it will correctly ignore case (instead of checking if env.EnvironmentName == "Development" for example).
Use separate classes
From the docs:
When an ASP.NET Core application starts, the Startup class is used to bootstrap the application, load its configuration settings, etc. (learn more about ASP.NET startup). However, if a class exists named Startup{EnvironmentName} (for example StartupDevelopment), and the ASPNETCORE_ENVIRONMENT environment variable matches that name, then that Startup class is used instead. Thus, you could configure Startup for development, but have a separate StartupProduction that would be used when the app is run in production. Or vice versa.
In addition to using an entirely separate Startup class based on the current environment, you can also make adjustments to how the application is configured within a Startup class. The Configure() and ConfigureServices() methods support environment-specific versions similar to the Startup class itself, of the form Configure{EnvironmentName}() and Configure{EnvironmentName}Services(). If you define a method ConfigureDevelopment() it will be called instead of Configure() when the environment is set to development. Likewise, ConfigureDevelopmentServices() would be called instead of ConfigureServices() in the same environment.
In .NET Core 2.0 MVC app / Microsoft.AspNetCore.All v2.0.0, you can have environmental specific startup class as described by #vaindil but I don't like that approach.
You can also inject IHostingEnvironment into StartUp constructor. You don't need to store the environment variable in Program class.
public class Startup
{
private readonly IHostingEnvironment _currentEnvironment;
public IConfiguration Configuration { get; private set; }
public Startup(IConfiguration configuration, IHostingEnvironment env)
{
_currentEnvironment = env;
Configuration = configuration;
}
public void ConfigureServices(IServiceCollection services)
{
......
services.AddMvc(config =>
{
// Requiring authenticated users on the site globally
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
config.Filters.Add(new AuthorizeFilter(policy));
// Validate anti-forgery token globally
config.Filters.Add(new AutoValidateAntiforgeryTokenAttribute());
// If it's Production, enable HTTPS
if (_currentEnvironment.IsProduction()) // <------
{
config.Filters.Add(new RequireHttpsAttribute());
}
});
......
}
}
This can be accomplished without any extra properties or method parameters, like so:
public void ConfigureServices(IServiceCollection services)
{
IServiceProvider serviceProvider = services.BuildServiceProvider();
IHostingEnvironment env = serviceProvider.GetService<IHostingEnvironment>();
if (env.IsProduction()) DoSomethingDifferentHere();
}
Starting from ASP.NET Core 3.0, it is much simpler to access the environment variable from both ConfigureServices and Configure.
Simply inject IWebHostEnvironment into the Startup constructor itself. Like so...
public class Startup
{
public Startup(IConfiguration configuration, IWebHostEnvironment env)
{
Configuration = configuration;
_env = env;
}
public IConfiguration Configuration { get; }
private readonly IWebHostEnvironment _env;
public void ConfigureServices(IServiceCollection services)
{
if (_env.IsDevelopment())
{
//development
}
}
public void Configure(IApplicationBuilder app)
{
if (_env.IsDevelopment())
{
//development
}
}
}
Reference: https://learn.microsoft.com/en-us/aspnet/core/fundamentals/environments?view=aspnetcore-3.0#inject-iwebhostenvironment-into-the-startup-class
from the docs
Configure and ConfigureServices support environment specific versions
of the form Configure{EnvironmentName} and Configure{EnvironmentName}Services:
You can do something like this...
public void ConfigureProductionServices(IServiceCollection services)
{
ConfigureCommonServices(services);
//Services only for production
services.Configure();
}
public void ConfigureDevelopmentServices(IServiceCollection services)
{
ConfigureCommonServices(services);
//Services only for development
services.Configure();
}
public void ConfigureStagingServices(IServiceCollection services)
{
ConfigureCommonServices(services);
//Services only for staging
services.Configure();
}
private void ConfigureCommonServices(IServiceCollection services)
{
//Services common to each environment
}
I wanted to get the environment in one of my services. It is really easy to do! I just inject it to the constructor like this:
private readonly IHostingEnvironment _hostingEnvironment;
public MyEmailService(IHostingEnvironment hostingEnvironment)
{
_hostingEnvironment = hostingEnvironment;
}
Now later on in the code I can do this:
if (_hostingEnvironment.IsProduction()) {
// really send the email.
}
else {
// send the email to the test queue.
}
EDIT:
Code above is for .NET Core 2. For version 3 you will want to use IWebHostEnvironment.
In NET6, Startup and Program have merged into a single file, there is no ConfigureServices method in Startup anymore. Now you can simply use
builder.Environment.IsProduction()
builder.Environment.IsStaging()
builder.Environment.IsDevelopment()
right after the first line
var builder = WebApplication.CreateBuilder(args);
Since there is no full copy & paste solution yet, based on Joe Audette's answer:
public IWebHostEnvironment Environment { get; }
public Startup(IWebHostEnvironment environment, IConfiguration configuration)
{
Environment = environment;
...
}
public void ConfigureServices(IServiceCollection services)
{
if (Environment.IsDevelopment())
{
// Do something
}else{
// Do something
}
...
}
The hosting environment comes from the ASPNET_ENV environment variable, which is available during Startup using the IHostingEnvironment.IsEnvironment extension method, or one of the corresponding convenience methods of IsDevelopment or IsProduction. Either save what you need in Startup(), or in ConfigureServices call:
var foo = Environment.GetEnvironmentVariable("ASPNET_ENV");
Just in case someone is looking to this too. In .net core 3+ most of this is obsolete. The update way is:
public void Configure(
IApplicationBuilder app,
IWebHostEnvironment env,
ILogger<Startup> logger)
{
if (env.EnvironmentName == Environments.Development)
{
// logger.LogInformation("In Development environment");
}
}
In Dotnet Core 2.0 the Startup-constructor only expects a IConfiguration-parameter.
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
How to read hosting environment there?
I store it in Program-class during ConfigureAppConfiguration
(use full BuildWebHost instead of WebHost.CreateDefaultBuilder):
public class Program
{
public static IHostingEnvironment HostingEnvironment { get; set; }
public static void Main(string[] args)
{
// Build web host
var host = BuildWebHost(args);
host.Run();
}
public static IWebHost BuildWebHost(string[] args)
{
return new WebHostBuilder()
.UseConfiguration(new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("hosting.json", optional: true)
.Build()
)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.ConfigureAppConfiguration((hostingContext, config) =>
{
var env = hostingContext.HostingEnvironment;
// Assigning the environment for use in ConfigureServices
HostingEnvironment = env; // <---
config
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);
if (env.IsDevelopment())
{
var appAssembly = Assembly.Load(new AssemblyName(env.ApplicationName));
if (appAssembly != null)
{
config.AddUserSecrets(appAssembly, optional: true);
}
}
config.AddEnvironmentVariables();
if (args != null)
{
config.AddCommandLine(args);
}
})
.ConfigureLogging((hostingContext, builder) =>
{
builder.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
builder.AddConsole();
builder.AddDebug();
})
.UseIISIntegration()
.UseDefaultServiceProvider((context, options) =>
{
options.ValidateScopes = context.HostingEnvironment.IsDevelopment();
})
.UseStartup<Startup>()
.Build();
}
Ant then reads it in ConfigureServices like this:
public IServiceProvider ConfigureServices(IServiceCollection services)
{
var isDevelopment = Program.HostingEnvironment.IsDevelopment();
}
Another way is to read the environment name directly from the configuration with Configuration["ASPNETCORE_ENVIRONMENT"]. This works everywhere where you can access configuration.
public IConfiguration Configuration { get; }
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public void ConfigureServices(IServiceCollection services)
{
Console.WriteLine(Configuration["ASPNETCORE_ENVIRONMENT"]);
}
Prerequisite is that the host was created with Host.CreateDefaultBuilder() in Programm.cs which is the default for ASP.NET Core 3.0 (and 5.0) web apps. If another builder is used the envars can be added with AddEnvironmentVariables() in Programm.cs.
In .NET6.0 Use the following:
var env = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
This will say "Development" if it is in development mode.
The variable "DEV_ENVIRONMENT" also will be set to 1 in dev environment.
For a blazor server app I did this: Startup.cs directly under the namespace declaration add
namespace myProjectName.Server
public static class myGlobals
{
public static bool isDevelopment = true;
}
Startup.cs find the Configure method and the existing check for env.IsDevelopment and set the static isDeveloment declared above to true or false.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
myGlobals.isDevelopment = true;
}
else false
In ApplicationUser when you are initializing the database connection or anywhere else
if (myGlobals.isDevelopment)
{