Runtime: More flexible Host Environment name

Created on 4 Mar 2020  路  38Comments  路  Source: dotnet/runtime

Is your feature request related to a problem? Please describe.

Our service has more environments than just Development, Staging and Production. For example, we have 2 Production environments for serving different region.
Let's call them "Production1" and "Production2"

Describe the solution you'd like

There are some extension methods like "IsDevelopment", "IsStaging" and "IsProduction" (https://github.com/dotnet/extensions/blob/master/src/Hosting/Abstractions/src/HostEnvironmentEnvExtensions.cs)
They will check if the environment is Development, Staging and Production (ignoring case).
I just wondering if it's possible that we just check the prefix of environment?
Then "Production1" and "Production2" will be treated as Production.

Describe alternatives you've considered

I can add my own extension methods like "IsMyProduction" which do whatever I want.
But it seems IsDevelopment/IsStaging/IsProduction methods has been used in different places in asp.net.

area-Extensions-Hosting documentation

Most helpful comment

Just to summarize what work needs to be done for this issue:

This issue is tracking adding XML documentation to the IsDevelopment, IsStaging and IsProduction methods that they are intended to be used by applications, and aren't intended for library authors.

All 38 comments

I can add my own extension methods like "IsMyProduction" which do whatever I want.
But it seems IsDevelopment/IsStaging/IsProduction methods has been used in different places in asp.net.

Where?

I can add my own extension methods like "IsMyProduction" which do whatever I want.
But it seems IsDevelopment/IsStaging/IsProduction methods has been used in different places in asp.net.

Where?

In my own project :-)

I'm asking where in ASP.NET assumes development vs production?

Seems like IsDevelopment wouldn鈥檛 cause any issues but IsProduction might ?

In most of those situations we are just setting defaults you can otherwise override, what's an example of a place where you want to do something different based on your own custom environment names and can't?

I don't expect us to change the built-in extension methods here. It would be massively breaking for customers who didn't expect prefix-based matching and could even cause security issues.

I think the issue in general is that it doesn't (seem to) make any sense to be able to use custom environment names when there are checks inside core/libs that check for specific strings to maybe change behaviour.
If I define "Production1" and "Production2" like in the example provided, not only those checks will not work anymore, but I will not be able to make them work in any other way whatsoever, unless I stop using custom environment names, and that would make them useless.
Did I understood correctly @iron9light ?

I've not yet encountered this specific situation in my code, but I've wondered the same before, so I'm curious about the rational behind this decision.

I think the issue in general is that it doesn't (seem to) make any sense to be able to use custom environment names when there are checks inside core/libs that check for specific strings to maybe change behaviour.

I agree. But I think it's actually a problem for IsProduction, and not IsDevelopment right?

Yes and no, let me elaborate.

If we consider the current version of core then probably yes.
If we consider even just the current version of various libs/ext out there, then realistically not.
Then, if we also consider future versions/libs/ext that will make use of the IsProduction/Development/etc ext methods because they are there in theory for a reason, then absolutely not.

I already saw various libs that work differently based on dev vs prod env using the standard ext methods, and it make sense for them to do so: but if, as soon as one uses custom environments everything falls apart, then this usage pattern become useless.

Just to be clear: I think the ability to reason differently base on dev vs prod is super useful, but I also think that no one is using those to check a very specific string, but more like just a general way to check if the env is development-y or production-y.
I say this following this reasoning: if they are not using custom env names, they are also not using the standard ext methods, and if they are not using custom env names, they are probably not considering them also.

I understand that a change like this may nonetheless create some problems, and I'm not suggesting some easy solutions like new ext methods (eg: IsDevelopmentOrSimilar()/IsProductionOrSimilar() that checks for prefixex), but at least recognizing the problem may start a conversation on what to do next.

Well, now that I think about it a solution may be to change those ext methods to also accept a bool with a default value for backward compatibility, something like:

public static bool IsProduction(this IHostingEnvironment hostingEnvironment, bool strict = true)

public static bool IsDevelopment(this IHostingEnvironment hostingEnvironment, bool strict = true)

Would that make sense?

I understand there鈥檚 a problem with libraries using these methods when there鈥檚 no extensibility but I think in practice a library explicitly depending on IsProduction is more of a problem than one that depends on IsDevelopment.

If not, I鈥檓 looking for an example of when you鈥檇 practically want to change the meaning of IsDevelopment.

@iron9light you could probably determine your specific production environment via a combination of IHostEnvironment (IsProduction) and an IOptions set from one or more custom env vars.

Mix the two behind an abstraction and you should be able to tell Prod1 from Prod2 _and_ not break the standard behaviour.

I鈥檓 not sure you could decorate the original IHostEnvironment in any way, so that鈥檚 going to be a separate service collection registration.

Just my 2c, interesting topic

I understand there鈥檚 a problem with libraries using these methods when there鈥檚 no extensibility but I think in _practice_ a library explicitly depending on IsProduction is more of a problem than one that depends on IsDevelopment.

Oh ok, maybe I misunderstood: if you are specifically wondering about IsDevelopment then yes, I agree with you that it would be way more strange.
Probably the thinking was just more general, that is instead of IsDevelopment checking for a specific string and IsProduction/Staging checking for prefixes, the suggestion was to change them all togheter.

But again yes, prod/staging are probably the ones that make most sense or that, at least, have a more evident need to be extensible.

Btw: what do you think about the additional bool strict = true proposal? Would it make any sense?

@rcollina yes, that would probably be an option. But it would also mean that custom env names as they are today are useless, no?

I don鈥檛 want to solve the general problem if there鈥檚 no need to solve it. We could add a bool but it wouldn鈥檛 fix the situation (libraries need to reference a new version of this binary and then use it). I鈥檓 looking for concrete problems we can solve without having to re-design the world or add APIs (if it can be avoided).

I don鈥檛 know of any libs that check for staging and it seems like the above ones check IsProduction in one of them (to print out a message).

What鈥檚 blocking you today

@njy I just think the problem at hand can be solved and I鈥檓 not sure I have the complete picture to rule out custom env names.

Anyhow, I鈥檇 tend to agree - still, some people get clever when dealing with constraints. It鈥檚 not always bad per se :)

@iron9light you could probably determine your specific production environment via a combination of IHostEnvironment (IsProduction) and an IOptions set from one or more custom env vars.

Mix the two behind an abstraction and you should be able to tell Prod1 from Prod2 _and_ not break the standard behaviour.

I鈥檓 not sure you could decorate the original IHostEnvironment in any way, so that鈥檚 going to be a separate service collection registration.

Just my 2c, interesting topic

So I have to at least write my own Host.CreateDefaultBuilder for loading config from the right file.

@iron9light can you clarify and specify what specific problems you are having right now? Things check for IsDevelopment, but nothing checks for IsStaging and we have one IsProduction that doesn't do much. What are you having problems with?

  1. I've more environments in my project, but not just Development, Staging and Production.
  2. Based on the guideline, I will name some of my environments as "Production1", "Production2", "Development1", "Development2" with some config files like appsettings.Production1.json, appsettings.Production2.json, appsettings.Development1.json, appsettings.Development2.json
  3. I wish my "Production1", "Production2" environment will be treated as Production by "Microsoft.Extensions.Xxx" and ASP.NET Core. and "Development1", "Development2" will be treated as Development.
    If the "Microsoft.Extensions.*" and ASP.NET Core use the extension methods like "IsDevelopment", "IsStaging" and "IsProduction" to check if it's Development/Staging/Production environment, then my customized environment name will not be treated as what I want and there's no easy way for it except I do not use any customized environment name.
  4. This is not a bug report, but a feature request.
  5. Change "IsDevelopment", "IsStaging" and "IsProduction" to just check the prefix is just a suggestion but not my purpose.

OK so you haven't actually encountered any issues, you're asking for a new feature to support a new scenario where you want IsProduction to work without having to write your own extension method right?

I don't see the value in doing that over you having your own method.

@davidfowl I'm not sure if there's any issue so far. Will report a bug if it has.
And for me, it seems a risk that if I use my own extension methods but asp.net use the built-in one.

There're two concepts here: 1) Environment name, 2) 3 environment types.
I wish Microsoft.Extensions and APS.NET Core can provide a way (in the future version maybe), to handle customized environment name and 3 environment types better.

As @njy said, I think we can discuss this topic here.

I鈥檓 not convinced it鈥檚 necessary but would love to be convinced otherwise 馃槵.

@davidfowl I think I understood what you mean only IsProduction is a problem.
Assume Microsoft.Extensions.Xxx and Asp.Net Core will only use IsDevelopment in code. Then to solve my concern, there should be only one "Development" environment (it makes sense for me). And if I want to use something like "Production1" and "Production2", I will use my own method to check if it's "Production" in my code.
Am I right?
@njy what do you think?

@njy what do you think?

I think having your own ex method would solve the problem for your own code, sure.

I also think about any lib/pkg out there which changes its behaviour based on the env (like any of these on GitHub https://github.com/search?l=C%23&q=IsProduction&type=Code for example), and they still won't work like intended, because honestly the usage of IsProduction() in all of those cases is reasonably intended as _"is the env a production environment?"_ and not _"is the name of the environment exactly equal to the string 'production' ?"_.

Otherwise as I said custom env names would only be useful for non dev/stag/prod environments - what kind of env would remain? - simply because any piece of code testing for the "type of env" is in reality testing for a very specific env name, which is useful as a coat for a tardigrade.

Just my 2 cents.

Well, you mean the open source community has used IsProduction here and there. No one can force the authors of 3rd party lib to just use IsDevelopment only :-(

I'm absolutely willing to look a this more if it's actually a problem. That code search isn't really proof, lots of those are applications not libraries (albeit I haven't looked at everything). We can even update the recommendation to avoid taking a dependency on those in libraries (it's why ASP.NET tries to avoid it as well).

Same usecase/requiremen/feature request on my side:

using different (sub) environments for development, configured via multiple appsettings.json.files:

appsettings.development.azure.json
appsettings.development.aws.json
appsettings.development.google.json

PLUS: a launchSettings.json with one profile for each appsetting.

problem/missing feature: existing code ,specially "IsDevelopment()" is returning false.

Many ways to solve this....
one idea:
allow customizing the results for Isxxx() extionsions
implement a (static) filter action/delegate on the extension calls that is called for each (Isxxx() check.....

regards Werner

Triage: Closing this issue as we don't plan to make changes here. The IsNNN methods are primarily designed for application use. We have some limited use in ASP.NET Core, and if those are causing problems please feel free to file issues for them. In general, libraries should not be depending on specific environment names, they are an app-specific concept. So:

  • In places where the IsNNN methods are used by libraries, we recommend following up with those library authors to provide an alternative.
  • In your own apps, if the IsNNN methods are insufficient, creating extension methods based on your app's patterns is the recommended approach.

Creating a model for environment "types" certainly has value but isn't something we'll be able to prioritize doing.

@anurse I think it's ok not changing code.
"In general, libraries should not be depending on specific environment names, they are an app-specific concept." <- For this, it will be great if there's a "Note" in doc for 3rd party lib owner.

If this is what has been decided, I can only go with the flow 馃憤

Having said that, I still don't understand why those ext methods exist in the framework in the first place, if they are only app related and not framework/libs related, imho it doesn't make any sense.

Convenience for applications

Yeah, exactly. There are plenty of parts of ASP.NET Core that are more designed for application use than library use. We could probably do a better job clarifying that though. I'll see if I can put some XML doc comments in (that seems most useful since it'll show in Intellisense).

@ericstj Now that this has moved to runtime, where should I make XML doc changes? For dotnet/extensions we use the doc comments directly but I think I remember dotnet/runtime does not do that.

@maryamariyan and @carlossanlop were porting the existing source docs to https://github.com/dotnet/dotnet-api-docs. Once up to date, that's where the API doc authoring should happen directly.

Basically i can understand the decision, but what about the following findings:

https://github.com/dotnet/aspnetcore/blob/3b4be4f51a39cd008cfc5c2eb0ef9854f9bcd649/src/DefaultBuilder/src/WebHost.cs#L175

https://github.com/dotnet/aspnetcore/blob/d4a2fa1ceb3b9c10054da8a7c6a4bd61801aea18/src/Hosting/Hosting/src/GenericHost/GenericWebHostedService.cs#L154

https://github.com/dotnet/aspnetcore/blob/d4a2fa1ceb3b9c10054da8a7c6a4bd61801aea18/src/Hosting/Hosting/src/Internal/WebHost.cs#L251

All of them are using "IsDevelopment" and are located inside the core of Asp.Net!

Basically i must rewrite half of the WebHost infrastructure to "inject" my own behavior!?

For consistency reasons i must suggest to move out the IsInXXX usage from aspnet Core (WebHosts) and allow another type of customizing for this....

regards
Werner

@WernerMairl yep, that was my point also.

I understand not wanting to change the internal implementaton (or at least the default behaviour) of the IsXXX ext methods, but we have 2 different path to make this reasonable considering the usage in the framework and the usefulness for applications and libraries, all in one fell swoop and with really minimum effort regarding implementation.

1) change the current methods by adding a strict param that defaults to true (current behaviour):

public static bool IsProduction(this IHostingEnvironment hostingEnvironment, bool strict = true)
public static bool IsStaging(this IHostingEnvironment hostingEnvironment, bool strict = true)
public static bool IsDevelopment(this IHostingEnvironment hostingEnvironment, bool strict = true)

2) add 1 new ext method per each IsXXX with a Like suffix:

public static bool IsProductionLike(this IHostingEnvironment hostingEnvironment)
public static bool IsStagingLike(this IHostingEnvironment hostingEnvironment)
public static bool IsDevelopmentLike(this IHostingEnvironment hostingEnvironment)

that would do a StartsWith or a Contains, whatever is preferred (and document it).

In both cases apps and libs can make a better use of them while not changing the currently used calls in core and everywhere (backward compat).

Also, if app/lib authors want the more reasonable behaviour (the less strict, that would catch a more broad dev/stag/prod env names) they can use that with a simple change, and just like the IsXXX methods are already there in core and not re-developed per each lib/app, the same would be true for the less strict version, already in the framework for anyone to use.

I personally don't see a problem in adding those, and the .net core parts that are using the current behaviour (useless with custom env names) may drop the call completely or switch to the new, less strict ones.

Overall on the implementation side they are just 3 one-liner methods + the current behaviour would not change + new possibilities would be available for anybody to use, both in core, libs and apps.

Just my 2 cents.

Just to summarize what work needs to be done for this issue:

This issue is tracking adding XML documentation to the IsDevelopment, IsStaging and IsProduction methods that they are intended to be used by applications, and aren't intended for library authors.

Closing as this issue is covered already by PR https://github.com/dotnet/dotnet-api-docs/pull/4204.

Was this page helpful?
0 / 5 - 0 ratings