Skip to content

Commit 1cd156a

Browse files
Update markdown formatting. (#220)
1 parent 9036ac0 commit 1cd156a

File tree

4 files changed

+105
-286
lines changed

4 files changed

+105
-286
lines changed

docs/CustomConfigBuilders.md

+1-3
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
# Implementing More Key/Value Config Builders
22

3-
If you don't see a config builder here that suits your needs, you can write your own. Referencing the `Basic` nuget package for this project will get you the base upon which
4-
all of these builders inherit. Most of the heavy-ish lifting and consistent behavior across key/value config builders comes from this base. Take a look at the code for more
5-
detail, but in many cases implementing a custom key/value config builder in this same vein is as easy as inheriting the base, and implementing two basic methods.
3+
If you don't see a config builder here that suits your needs, you can write your own. Referencing the `Basic` nuget package for this project will get you the base upon which all of these builders inherit. Most of the heavy-ish lifting and consistent behavior across key/value config builders comes from this base. Take a look at the code for more detail, but in many cases implementing a custom key/value config builder in this same vein is as easy as inheriting the base, and implementing two basic methods.
64
```CSharp
75
using Microsoft.Configuration.ConfigurationBuilders;
86

docs/Intro.md

+15-63
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,14 @@
11
# Configuration Builders In The .Net Framework
22

3-
Configuration Builders are a new feature of the full .Net Framework, introduced in .Net 4.7.1. You can read a quick overview of
4-
the concept in [this blog post](http://jeffreyfritz.com/2017/11/modern-configuration-for-asp-net-4-7-1-with-configurationbuilders/).
5-
The concept is pretty simple - the config system instantiates an object that gets a chance to craft configuration while loading each
6-
`ConfigurationSection`. The execution of the concept can get a little more complicated though, because of the old multi-layered and
7-
strongly-typed nature of the old .Net Framework config system.
3+
Configuration Builders are a new feature of the full .Net Framework, introduced in .Net 4.7.1. You can read a quick overview of the concept in [this blog post](http://jeffreyfritz.com/2017/11/modern-configuration-for-asp-net-4-7-1-with-configurationbuilders/). The concept is pretty simple - the config system instantiates an object that gets a chance to craft configuration while loading each `ConfigurationSection`. The execution of the concept can get a little more complicated though, because of the old multi-layered and strongly-typed nature of the old .Net Framework config system.
84

9-
This document will attempt to dig a little deeper into the mechanics of `ConfigurationBuilder` in the .Net Framework, in the hopes
10-
that it will help understand how the builders in this project work together to construct a more dynamic configuration environment
11-
for legacy apps in a modern landscape. The first step is to understand the order of execution when building a `ConfigurationSection`.
5+
This document will attempt to dig a little deeper into the mechanics of `ConfigurationBuilder` in the .Net Framework, in the hopes that it will help understand how the builders in this project work together to construct a more dynamic configuration environment for legacy apps in a modern landscape. The first step is to understand the order of execution when building a `ConfigurationSection`.
126

137

148
## ConfigurationBuilders Order of Execution
15-
This section is about how the .Net Framework uses `ConfigurationBuilder`s to build configuration sections for app consumption.
16-
The mechanics of how this all works affects the order in which config builders are applied and what sources of information they have
17-
to draw on as well as the look of the underlying config section they are modifying. Indeed, even in this project, features like reading
18-
builder parameters from `AppSettings` are potentially constrained by the way the .Net config system works.
19-
20-
Its very easy to think of the .Net configuration system as simply reading the `.config` file that comes with your application. But there is much more involved
21-
in this task. To start with, the .Net Framework uses a multi-layered configuration system. Whenever code asks for a `ConfigurationSection`, wether it is directly
22-
via `GetSection()` or indirectly through `ConfigurationManager`, the .Net Framework starts by reading the machine-wide configuration in the machine.config
23-
file installed in `%WINDIR%\Microsoft.NET\Framework[64]\v4.0.30319\Config`. The process then moves to the next level config file and modifies what it
24-
read from machine.config with updates from that next level. And so on and so forth. The process iterates through a well-known chain of config files before
25-
returning the resulting accumulation of configuration values from all levels. (There are actually *two* configuration systems in one within the .Net Framework.
26-
They are roughly the same with the most noticeable difference being the layers of config files being stacked together. The client configuration system consults
27-
user config files and app.exe.config files, while the web configuration system follows a chain of web.config files.) The following diagram demonstrates the
28-
high level concept for an ASP.Net application serving a page from a sub-directory.
9+
This section is about how the .Net Framework uses `ConfigurationBuilder`s to build configuration sections for app consumption. The mechanics of how this all works affects the order in which config builders are applied and what sources of information they have to draw on as well as the look of the underlying config section they are modifying. Indeed, even in this project, features like reading builder parameters from `AppSettings` are potentially constrained by the way the .Net config system works.
10+
11+
Its very easy to think of the .Net configuration system as simply reading the `.config` file that comes with your application. But there is much more involved in this task. To start with, the .Net Framework uses a multi-layered configuration system. Whenever code asks for a `ConfigurationSection`, wether it is directly via `GetSection()` or indirectly through `ConfigurationManager`, the .Net Framework starts by reading the machine-wide configuration in the machine.config file installed in `%WINDIR%\Microsoft.NET\Framework[64]\v4.0.30319\Config`. The process then moves to the next level config file and modifies what it read from machine.config with updates from that next level. And so on and so forth. The process iterates through a well-known chain of config files before returning the resulting accumulation of configuration values from all levels. (There are actually *two* configuration systems in one within the .Net Framework. They are roughly the same with the most noticeable difference being the layers of config files being stacked together. The client configuration system consults user config files and app.exe.config files, while the web configuration system follows a chain of web.config files.) The following diagram demonstrates the high level concept for an ASP.Net application serving a page from a sub-directory.
2912

3013
```
3114
GetSection() +----------------+ +------------+ +------------+ +------------+
@@ -36,12 +19,7 @@ GetSection() +----------------+ +------------+ +-----
3619
+----------------+ +------------+ +------------+ +------------+
3720
```
3821

39-
That is a simple explanation of layered config. Even from this simple concept you can see how each layer builds on previous layers... but does not directly
40-
influence other layers. Which brings us to the first point wrt config builder execution order. <u>***ConfigurationBuilders execute only in the layer in which they
41-
are applied.***</u> This refers to the _application_ of a config builder. The _definition_ of a config builder will carry forward to every layer beyond the one in which
42-
it was defined, just like applicationSettings and connectionStrings carry forward to the next layer. But the `configBuilders` tag that is used to apply a config
43-
builder is not carried forward. It is executed at each level and then forgotten. The results of the config builder execution are carried forward in the config
44-
object passed to the next level.
22+
That is a simple explanation of layered config. Even from this simple concept you can see how each layer builds on previous layers... but does not directly influence other layers. Which brings us to the first point wrt config builder execution order. <u>***ConfigurationBuilders execute only in the layer in which they are applied.***</u> This refers to the _application_ of a config builder. The _definition_ of a config builder will carry forward to every layer beyond the one in which it was defined, just like applicationSettings and connectionStrings carry forward to the next layer. But the `configBuilders` tag that is used to apply a config builder is not carried forward. It is executed at each level and then forgotten. The results of the config builder execution are carried forward in the config object passed to the next level.
4523

4624
There is of course more detail within each layer. Let's take a look at what happens in the `(app) web.config` layer in the diagram above:
4725

@@ -61,20 +39,9 @@ There is of course more detail within each layer. Let's take a look at what happ
6139
(app) web.config +------------> { ConfigurationSection Object* }
6240
```
6341

64-
The first thing to notice is that this layer takes a `ConfigurationSection` object from the previous layer as input. All *.config* files and config builder output
65-
from previous layers are reflected in this object. From here, the first step (A:) taken at this layer is to *read and process* the raw xml for the requested configuration
66-
section. If the section is encrypted, it is decrypted here. If the section resides in a different file via the `configSource` attribute, then the raw xml is read from
67-
that source. The *last* step of retrieving/processing the raw xml content is passing it through the configuration builder chain. <u>***The builder chain executes in
68-
the order that builders appear in the `configBuilders` tag for the section.***</u> The processed xml then returns into the config system where magic is applied to
69-
combine it with the `ConfigurationSection` object from the previous layer to produce a new `ConfigurationSection` object that reflects the merged settings.
70-
Lastly, this `ConfigurationSection` object is passed through the config builder chain, which executes in the same order as before. The resulting object gets passed
71-
on to the next layer if it exists.
72-
73-
One aspect to note is that config builders are instantiated once per appearance in a `configBuilders` tag. This means that if you apply the "same" config builder to
74-
both your `<appSettings/>` and `<connectionStrings/>` sections, each section will use a separate instance for processing since each section was tagged
75-
with the builder separately. Similarly, if you define an 'Environment' config builder in machine.config and apply it to `<appSettings/>` both there and
76-
again in app.exe.config... two separate instances will be created to process configuration at each layer. However, the same instance is used for processing both
77-
the raw xml as well as the `ConfigurationSection` object within each layer/section.
42+
The first thing to notice is that this layer takes a `ConfigurationSection` object from the previous layer as input. All *.config* files and config builder output from previous layers are reflected in this object. From here, the first step (A:) taken at this layer is to *read and process* the raw xml for the requested configuration section. If the section is encrypted, it is decrypted here. If the section resides in a different file via the `configSource` attribute, then the raw xml is read from that source. The *last* step of retrieving/processing the raw xml content is passing it through the configuration builder chain. <u>***The builder chain executes in the order that builders appear in the `configBuilders` tag for the section.***</u> The processed xml then returns into the config system where magic is applied to combine it with the `ConfigurationSection` object from the previous layer to produce a new `ConfigurationSection` object that reflects the merged settings. Lastly, this `ConfigurationSection` object is passed through the config builder chain, which executes in the same order as before. The resulting object gets passed on to the next layer if it exists.
43+
44+
One aspect to note is that config builders are instantiated once per appearance in a `configBuilders` tag. This means that if you apply the "same" config builder to both your `<appSettings/>` and `<connectionStrings/>` sections, each section will use a separate instance for processing since each section was tagged with the builder separately. Similarly, if you define an 'Environment' config builder in machine.config and apply it to `<appSettings/>` both there and again in app.exe.config... two separate instances will be created to process configuration at each layer. However, the same instance is used for processing both the raw xml as well as the `ConfigurationSection` object within each layer/section.
7845

7946
Here is a brief pseudo-config example to demonstrate the order of execution:
8047

@@ -83,8 +50,7 @@ Here is a brief pseudo-config example to demonstrate the order of execution:
8350
| check 1 | check 2 | bananas |
8451
| <pre>&lt;configBuilders&gt;<br/>&nbsp;&nbsp;&lt;add name="machine1" type=MachineA,..." /&gt;<br/>&nbsp;&nbsp;&lt;add name="machine2" type=MachineB,..." /&gt;<br/>&lt;/configBuilders&gt;<br/><br/>&lt;appSettings<br/>&nbsp;&nbsp;&nbsp;configBuilders="machine2, machine1" /&gt;</pre> | <pre>&lt;configBuilders&gt;<br/>&nbsp;&nbsp;&lt;remove name="machine2" /&gt;<br/>&nbsp;&nbsp;&lt;add name="machine2" type=WebTwo,..." /&gt;<br/>&nbsp;&nbsp;&lt;add name="web1" type=WebOne,..." /&gt;<br/>&lt;/configBuilders&gt;<br/><br/>&lt;appSettings<br/>&nbsp;&nbsp;&nbsp;configBuilders="web1" /&gt;</pre> | <pre>&lt;configBuilders&gt;<br/>&nbsp;&nbsp;&lt;add name="web3" type=WebThree,..." /&gt;<br/>&lt;/configBuilders&gt;<br/><br/>&lt;appSettings<br/>&nbsp;&nbsp;&nbsp;configBuilders="web3,machine2,web1" /&gt;</pre> |
8552

86-
With the config layers above, loading the application settings for your web app would result in config builders executing in the following order. (InstanceId is a made
87-
up Id just for this table to demonstrate when instances are reused.)
53+
With the config layers above, loading the application settings for your web app would result in config builders executing in the following order. (InstanceId is a made up Id just for this table to demonstrate when instances are reused.)
8854

8955
| Row&nbsp;# | BuilderName | Type | Instance&nbsp;# | Data Format | Data Source |
9056
| ---: | ----------- | ---- | ---------: | ----------: | ----------- |
@@ -103,28 +69,14 @@ up Id just for this table to demonstrate when instances are reused.)
10369

10470
The `ConfigurationSection` object that returns from row 12 is the end result of config building.
10571

106-
<a name="recursion"/>
72+
<a name="recursion"></a>
10773
## Potential For Recursion/Overflow
10874

109-
The discussion above all centers on the loading of a single `ConfigurationSection.` This simple conceptual model of how sections
110-
are loaded has served the framework well over the years. But one potential pitfall that arises from the extensibility introduced by *ConfigurationBuilders*
111-
is that config sections may not remain neatly separated anymore. In fact, the *KeyValueConfigBuilders* provided in this project offer
112-
a feature that explicitly entangles the creation of different config sections by allowing builders to draw upon `appSettings` for
113-
input parameters.
75+
The discussion above all centers on the loading of a single `ConfigurationSection.` This simple conceptual model of how sections are loaded has served the framework well over the years. But one potential pitfall that arises from the extensibility introduced by *ConfigurationBuilders* is that config sections may not remain neatly separated anymore. In fact, the *KeyValueConfigBuilders* provided in this project offer a feature that explicitly entangles the creation of different config sections by allowing builders to draw upon `appSettings` for input parameters.
11476

11577

116-
When sections are conceptually siloed, there are no concerns about deadlocks or overflows because there are no cycles created when
117-
sections remain completely untangled. Traditionally, code that defines a `ConfigurationSection` is encouraged to not use other configuration
118-
sections while creating thier own. Creating dependencies across sections can create complications when the order in which the sections
119-
get loaded is not determinate. Crossing streams is bad.
78+
When sections are conceptually siloed, there are no concerns about deadlocks or overflows because there are no cycles created when sections remain completely untangled. Traditionally, code that defines a `ConfigurationSection` is encouraged to not use other configuration sections while creating thier own. Creating dependencies across sections can create complications when the order in which the sections get loaded is not determinate. Crossing streams is bad.
12079

121-
But the 'load from appSettings' feature in this project does just that. We also encourage folks who extend this package to use the coding skills
122-
they know to bring in information from external sources - and the classes/methods for doing so could also indirectly load another configuration
123-
section... or maybe even try to reload the section that is currently being loaded. The potential for deadlock and/or stack overflow is there if
124-
developers are not careful about what their config builders are doing.
80+
But the 'load from appSettings' feature in this project does just that. We also encourage folks who extend this package to use the coding skills they know to bring in information from external sources - and the classes/methods for doing so could also indirectly load another configuration section... or maybe even try to reload the section that is currently being loaded. The potential for deadlock and/or stack overflow is there if developers are not careful about what their config builders are doing.
12581

126-
Unfortunately, there is not much we can do to prevent or neatly handle this situation. We have [seen it crop up in the past](#61) and it
127-
can be difficult to diagnose. Users of config builders should be aware of the possibility they might end up in a recursive loop of
128-
sections loading sections. In version 3 of this project there is a new [RecursionGuard](#TODO) feature that is enabled by default. It
129-
does not 'fix' or stop the recursion, but throws an exception to explain what's happening. It can optionally be configured to stop the
130-
recursion and continue... but using this option could yield non-deterministic results.
82+
Unfortunately, there is not much we can do to prevent or neatly handle this situation. We have [seen it crop up in the past](#61) and it can be difficult to diagnose. Users of config builders should be aware of the possibility they might end up in a recursive loop of sections loading sections. In version 3 of this project there is a new [RecursionGuard](#TODO) feature that is enabled by default. It does not 'fix' or stop the recursion, but throws an exception to explain what's happening. It can optionally be configured to stop the recursion and continue... but using this option could yield non-deterministic results.

0 commit comments

Comments
 (0)