Read environment variables in ASP.NET Core - asp.net

Running my ASP.NET Core application using DNX, I was able to set environment variables from the command line and then run it like this:
set ASPNET_ENV = Production
dnx web
Using the same approach in 1.0:
set ASPNETCORE_ENVIRONMENT = Production
dotnet run
does not work - the application does not seem to be able to read environment variables.
Console.WriteLine(Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT"));
returns null
What am I missing?

Your problem is spaces around =.
This will work (attention to space before closing quote):
Console.WriteLine(Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT "));
The space after ASPNETCORE_ENVIRONMENT in this code is not a typo! The problem in the question was having extra space (in SET...), so you must use the same space in GetEnvironmentVariable().
As noted by Isantipov in a comment, an even better solution is to remove the spaces entirely from the SET command:
set ASPNETCORE_ENVIRONMENT=Production

This should really be a comment to this answer by #Dmitry (but it is too long, hence I post it as a separate answer):
You wouldn't want to use 'ASPNETCORE_ENVIRONMENT ' (with a trailing space) - there are features in ASP.NET Core which depend on the value of 'ASPNETCORE_ENVIRONMENT'(no trailing space) - e.g. resolving of appsettings.Development.json vs appsettings.Production.json. (e.g. see Working with multiple environments documentation article
And also I guess if you'd like to stay purely inside ASP.NET Core paradigm, you'd want to use IHostingEnvironment.Environment(see documentation) property instead of reading from Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") directly (although the former is of course set from the latter). E.g. in Startup.cs
public class Startup
{
//<...>
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
Console.WriteLine("HostingEnvironmentName: '{0}'", env.EnvironmentName);
//<...>
}
//<...>
}

If you create the Environment variables at runtime during development then you will get null every time. You have to restart the visual studio because VS read EV at startup only.

Related

Yaml pipeline. How to swap connstrings in Builds with Console App

I built a console app with .Net Core 3.1. I have it building using Yaml leaning heavily on the learn.microsoft.com documentation. The release is pushing to the correct box. But I have an appsettings.json file that has a conn string variable that is different between my TEST, QA and PROD regions. I knew how to do this with the xml file transforms in .NET and MVC but I can't get this to work. Any help would be great since I don't even know the term for what I am trying to do here.
How do you change the connectionstring in the appsettings.json based on a variable or do I have to create 3 branches each with settings and create 3 build and release pipelines?
Thank you.
In order to push to different environments you usually
Have seperate release pipelines that trigger from different branches.
You have one release pipeline with different stages that need pre-approval to move to the next stage TEST -> QA -> PROD.
In both cases you will make use of Stage.
There you need to add a task named "File transformation"
In the File Format select JSON
Now, any variable found in the appsettings.json file will be replaced by the variables you set in the pipeline.
Be careful because nested variables like
{
SerilogSettings: {
BatchSize: 100
}
}
need to be set with a "." instead like
SerilogSettings.BatchSize

Handling development time web.config conflicts

I am looking for a way to handle this challenge: we are a geographically dispersed dev team using ASP.NET Web API and Angular to build a web app.
The thing that causes the grief is the fact that not all team members use the same database setup for their dev work. Yes, I know - I can use web.config transforms to set the proper connection strings for test, staging and production (and I'm already doing this) - but this is not what I'm talking about.
Due to reasons beyond our control at this time, we have
some developers working on a local SQL Server instance using server=(local);database=OurDB as their connection string
other developers using a central developer SQL Server in their location, using something like server=someserver.mycorp.com;database=OurDB
and a few exotic cases with yet other settings
Now every time someone commits a change to the Git repo, and happens to also change something in the web.config, his connection string is committed to the repo. So when I then go pull that latest commit, my settings to my local DB server are overwritten by this other guy's settings.
I am looking for a way to handle this - I was hoping I might be able to
hook into the Git pull process and automagically update the web.config connection string to my local needs whenever I pull something
somehow reference a connection string (or external config file) based on e.g. my currently logged in user's name or something like that
But I can't seem to find any way of doing this. I was wondering if I need to build a VS extension to handle this - any starters for that? Has anyone done something like this before and could share his code? (or has it up on Github)
The web.config configuration system used in ASP.NET is not flexible enough to support the more advanced scenario you have described. So, why use it? You could store the configuration in files within the repository, one per developer. Or they could be stored outside the repository or otherwise ignored.
The real trick is that most older applications don't have a single root that retrieve the configuration, so you have to refactor your application to utilize a flexible configuration system. For your staging/production environments you probably still want to use the config in web.config. The following code can give you a basic idea of one way to structure it:
public class MyApplicationConfiguration
{
public string MainConnectionString { get; set; }
}
public class ConfigurationRetriever
{
public MyApplicationConfiguration GetConfiguration()
{
// You might look for the absence or presence of an environment variable to determine this
bool isLocalDevelopment = IsApplicationLocalDevelopment();
var config = new MyApplicationConfiguration();
if(isLocalDevelopment)
{
config.MainConnectionString = Environment.GetEnvironmentVariable("MyApplication_MainConnectionString");
//or get it from a JSON file or XML file or config database
}
else
{
config.MainConnectionString = ConfigurationManager.ConnectionStrings["MainConnectionString"].ConnectionString;
}
}
}
Rather than rolling your own config building logic, you might refactor your application to leverage Microsoft.Extensions.Configuration. It's not just for .NET Core. It's for .NET Standard. So you can use it even in your legacy ASP.NET applications. For reading the web.config, you could probably use Microsoft.Extensions.Configuration.Xml. Or you can write your own adapter that pulls values out of ConfigurationManager. I did a basic test, and this worked as expected.

VSTS Visual Studio Set Environment Variable Test Project

I am building Integration tests in Visual Studio (2017) for Net Core applications built on VSTS and deployed from there. My projects are test projects, and right now my connection strings to the deployed API url, and the database are hardcoded, but I want to remove them from the code and place them in a VSTS build step that adds environment variables.
Right now, my Test .cs files look something like this:
[TestClass]
public class TestFeature
{
//Set up variables
private static string _connectionString = "server=localhost;port=5432;database=databaseName;user id=postgres;password=postgres";
[TestInitialize]
public void Initialize()
{
}
//And going into my test methods
}
How do I set up my project to read Environment Variables set from the Configuration on VSTS?
What research has dug up for me so far, is to Right Click on Properties of the test project, under Debug, set up Environment Variables with a key and value there.
Then I change my test project to take the GetEnvironmentVariable()
private static string _connectionString = Environment.GetEnvironmentVariable(nameOfVariable);
On the other side in VSTS, I'm trying to find a build step that will set the variable to be the connection string there. The best step I could find is "Set Variable".
However, this is not working for me. The program will not recognize the name of the new variable set in Environment Variables, to start.
You need to add your VSTS variables in the Variables tab.
Another option is to use Powershell to set them if you want to do it in a dynamic fashion using a Powershell script task.
Write-Host "##vso[task.setvariable variable=sauce]crushed tomatoes"
https://learn.microsoft.com/en-us/vsts/build-release/concepts/definitions/release/variables?tabs=powershell
The better way is building, deploying and testing together in a build definition, after the build succeed, then the pull request can be approved.
For this way, you can use the variable in current build (can add/update variable as Marcote said)
If you must do integration test in a separate build, you can set Trigger to Manual in Build validation of Pull Request policy, then you can queue build manually and specify variables’ values (Check Settable at queue time for the variables in build definition)

VS2008 Update Web Reference creates naming conflicts in Reference.cs

I have a Web service which, when updated on one computer with VS2008 works perfectly fine, but on another computer does not. The critical difference is the contents of the auto-generated Reference.cs.
In the correctly functioning environment, the methods have fully qualified class names, refering to classes in a class library. However, in the problem environment, the class names are unqualified and partial classes are declared near the end of the file. E.g.
public string MyMethod(MyClass pr_Class)
...
public partial class MyClass {
}
versus
public string MyMethod(Class.Library.Namespace.MyClass pr_Class)
This causes errors such as:
'<ClassType>' is an ambiguous reference between <ClassLibraryNamespace>.ClassType and <WebReferenceNamespace>.ClassType
The code is under source control. Checking out to the working environment works straight away, checking out to the problem environment will only work if the reference.cs is manually edited to be like the working environments. Transferring the project manually from problem environment to the working one only requires an "Update Web Reference" to be performed to allow compilation. Both Reference.cs files claim to be auto-generated by the same tool version. The settings from the good environment have been exported to the other. No success so far...
Any ideas would be greatly appreciated!
Matt
Ok, so I have the answer. The working environment has a long forgotten extension library for importing web references. Customizing generated Web Service proxies explains the procedure. I'm new to the project and so had no idea about this!

How to prevent an ASP.NET application restarting when the web.config is modified?

I'm hosting the ASP.NET runtime via the ApplicationHost.CreateApplicationHost method. When I modify the web.config while the application is running, i see lots of first chance ThreadAbortExceptions thrown. This is right before my application comes crashing down. I'm assuming this is because the runtime has detected changes to the configuration and wants to restart.
This isn't really a supported scenario for us, so i'd prefer if I could just switch off the automatic reloading.
Does anyone know how to do this?
Actually, the first two answers are incorrect. It is possible, and quite easy, to prevent this recycling from happening, and this feature has been available since at least IIS6.
Method 1 (system wide)
Change the DWORD registry setting for HKLM\SOFTWARE\Wow6432Node\Microsoft\ASP.NET\FCNMode to the value 1, which will disable all file change notifications.
Don't be confused by the location: Wow6432Node has, in this case, no influence on the bitness of your web application.
Method 2 (.NET 4.5+)
If you are using .NET 4.5, then it is now possible to disable this on a per-site level, simply use the following in your web.config:
<httpRuntime fcnMode="Disabled"/>
Method 3 (IIS6+)
Finally, and also (at least) around since IIS6, there's a setting called DisallowRotationOnConfigChange as a setting for only the application pool (at least that is what I think the text on MSDN tries to say, but I haven't tested it). Set it to true and changes to the configuration of the application pool will not result in an immediate recycle.
This last setting can also be set from Advanced Settings of the application pool:
Method 4 (ASP.NET 1.0 and 1.1)
For (old) websites using ASP.NET 1.0 or 1.1, there is a confirmed bug that can cause rapid and repeated recycles on file changes. The workaround at the time was similar to what MartinHN suggested under the main question, namely, something like the following in your web.config:
<compilation
debug="false"
defaultLanguage="vb"
numRecompilesBeforeAppRestart="5000">
This does not disable recycling, but it does so only after 5000 recompilations have taken place. Whether this number is useful depends on the size of your application. Microsoft does not clearly say what a recompilation really is. The default, however, is 15.
As an aside: regardless of the version of .NET or Windows, we find that when the application is run from a share and used in a load-balanced environment, that the site recycles continuously. The only way to solve it was by adding that FNCMode setting to the registry (but now there are more fine-grained options).
As far as I am aware there is no way to disable this behavior, changes to the webconfig force the application to be restarted.
Update: it is actually possible, there are a number of methods, well documented, as explained in this answer*
Original answer:
There is a similar question here just for other reference. I found additional info that may be helpful.
Configuration Changes Cause a Restart
of the Application Domain
Changes to
configuration settings in Web.config
files indirectly cause the application
domain to restart. This behavior
occurs by design. You can optionally
use the configSource attribute to
reference external configuration files
that do not cause a restart when a
change is made. For more information,
see configSource in General Attributes
Inherited by Section Elements.
From This MSDN Article
* Disclaimer: I wrote the other answer and normally wouldn't make a self-reference, but find it relevant enough to link here since 8 years after this post it is really quite different: a solution is very easy by clicking through the IIS front-end, and workarounds exist since ASP.NET 1.0.
I ran in to an even bigger problem along the same lines - changes to any file or sub-folder in the AppDomain base directory cause the hosting environment to shutdown. This is a pretty big issue for our application as we're running a WPF UI in the same AppDomain and we can't restart it without being distruptive to the user.
I really wanted to avoid having to run a separate AppDomain for the web based part of the application so I did some digging with Reflector. I found that the culprit was the internal class FileChangesMonitor.
So I wrote a horrible horrible reflection hack to solve the problem. I thought I'd post it here as a potential solution for anyone else having the same problem. You just need to call HttpInternals.StopFileMonitoring() to disable shutdown on file/folder changes.
internal static class HttpInternals
{
private static readonly FieldInfo s_TheRuntime = typeof(HttpRuntime).GetField("_theRuntime", BindingFlags.NonPublic | BindingFlags.Static);
private static readonly FieldInfo s_FileChangesMonitor = typeof(HttpRuntime).GetField("_fcm", BindingFlags.NonPublic | BindingFlags.Instance);
private static readonly MethodInfo s_FileChangesMonitorStop = s_FileChangesMonitor.FieldType.GetMethod("Stop", BindingFlags.NonPublic | BindingFlags.Instance);
private static object HttpRuntime
{
get
{
return s_TheRuntime.GetValue(null);
}
}
private static object FileChangesMonitor
{
get
{
return s_FileChangesMonitor.GetValue(HttpRuntime);
}
}
public static void StopFileMonitoring()
{
s_FileChangesMonitorStop.Invoke(FileChangesMonitor, null);
}
}
A solution would be adding following element to web.config section :
<httpRuntime
waitChangeNotification="315360000"
maxWaitChangeNotification="315360000"
/>
As mentioned by jfburdet the solution is to use waitChangeNotification and maxWaitChangeNotification.
That being said, you should know they don't work on IIS 7 if ASP.NET is run in mixed mode: http://forums.iis.net/t/1149344.aspx

Resources