├── .github
├── PULL_REQUEST_TEMPLATE.md
└── workflows
│ ├── build-and-publish.yml
│ ├── build-and-test.yml
│ └── snyk-test.yml
├── .gitignore
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── Examples
├── Example.AppConfig.DotNetFramework
│ ├── App.config
│ ├── Example.AppConfig.DotNetFramework.csproj
│ └── Program.cs
├── Example.EnvironmentVariableConfig.DotNetCore
│ ├── Example.EnvironmentVariableConfig.DotNetCore.csproj
│ ├── Program.cs
│ └── appsettings.json
├── Example.FileConfig.DotNetCore
│ ├── Example.FileConfig.DotNetCore.csproj
│ ├── Program.cs
│ └── appsettings.json
└── Example.MemoryLeakTest
│ ├── Example.MemoryLeakTest.csproj
│ └── Program.cs
├── LICENSE.md
├── README.md
├── RockLib.Configuration.All.sln
├── RockLib.Configuration.AspNetCore
├── .editorconfig
├── AspNetExtensions.cs
├── CHANGELOG.md
├── Directory.Build.props
├── RockLib.Configuration.AspNetCore.csproj
└── RockLib.Configuration.AspNetCore.sln
├── RockLib.Configuration.MessagingProvider
├── .editorconfig
├── AssemblyInformation.cs
├── BlocklistSettingFilter.cs
├── CHANGELOG.md
├── Directory.Build.props
├── ISettingFilter.cs
├── ListSettingFilterExtensions.cs
├── MessagingConfigurationProvider.cs
├── MessagingConfigurationSource.cs
├── NullSettingFilter.cs
├── README.md
├── RockLib.Configuration.MessagingProvider.csproj
├── RockLib.Configuration.MessagingProvider.sln
├── RockLibMessagingProviderExtensions.cs
└── SafelistSettingFilter.cs
├── RockLib.Configuration.ObjectFactory
├── .editorconfig
├── AlternateNameAttribute.cs
├── AssemblyInformation.cs
├── CHANGELOG.md
├── ConfigReloadingProxy.cs
├── ConfigReloadingProxyFactory.cs
├── ConfigReloadingProxyReferenceModel.cs
├── ConfigSectionAttribute.cs
├── ConfigurationObjectFactory.cs
├── ConstructorOrderInfo.cs
├── ConvertMethodAttribute.cs
├── DefaultTypeAttribute.cs
├── DefaultTypes.cs
├── Directory.Build.props
├── Exceptions.cs
├── IResolver.cs
├── InternalsVisibleTo.cs
├── Members.cs
├── NullableAttributes.cs
├── README.md
├── Resolver.cs
├── RockLib.Configuration.ObjectFactory.csproj
├── RockLib.Configuration.ObjectFactory.sln
└── ValueConverters.cs
├── RockLib.Configuration.ProxyFactory
├── .editorconfig
├── CHANGELOG.md
├── ConfigurationProxyFactory.cs
├── Directory.Build.props
├── Exceptions.cs
├── InternalsVisibleTo.cs
├── README.md
├── RockLib.Configuration.ProxyFactory.csproj
└── RockLib.Configuration.ProxyFactory.sln
├── RockLib.Configuration
├── .editorconfig
├── AppSettings.cs
├── CHANGELOG.md
├── CompositeConfigurationExtensions.cs
├── CompositeConfigurationSection.cs
├── Config.cs
├── Directory.Build.props
├── README.md
├── RockLib.Configuration.csproj
├── RockLib.Configuration.sln
├── RockLibConfigurationBuilderExtensions.cs
├── Semimutable.cs
└── SoftLock.cs
├── Tests
├── RockLib.Configuration.AppSettingsConfigTests
│ ├── .editorconfig
│ ├── AssemblySettings.cs
│ ├── ConfigTests.cs
│ ├── Directory.Build.props
│ ├── RockLib.Configuration.AppSettingsConfigTests.csproj
│ ├── appsettings.development.json
│ ├── appsettings.json
│ └── appsettings.staging.json
├── RockLib.Configuration.AspNetCore.Tests
│ ├── .editorconfig
│ ├── AspNetExtensionsTests.cs
│ ├── Directory.Build.props
│ └── RockLib.Configuration.AspNetCore.Tests.csproj
├── RockLib.Configuration.CustomConfigTests
│ ├── .editorconfig
│ ├── AssemblySettings.cs
│ ├── ConfigTests.cs
│ ├── Directory.Build.props
│ └── RockLib.Configuration.CustomConfigTests.csproj
├── RockLib.Configuration.MessagingProvider.Tests
│ ├── .editorconfig
│ ├── BlocklistSettingFilterTests.cs
│ ├── Directory.Build.props
│ ├── FakeReceiver.cs
│ ├── MessagingConfigurationProviderTests.cs
│ ├── MessagingConfigurationSourceTests.cs
│ ├── NullSettingFilterTests.cs
│ ├── RockLib.Configuration.MessagingProvider.Tests.csproj
│ ├── RockLibMessagingProviderExtensionsTests.cs
│ ├── SafelistSettingFilterTests.cs
│ └── appsettings.json
├── RockLib.Configuration.ObjectFactory.Tests
│ ├── .editorconfig
│ ├── AssemblySettings.cs
│ ├── ConfigReloadingProxyFactoryTests.cs
│ ├── ConfigReloadingProxyReferenceModelTests.cs
│ ├── ConfigurationObjectFactorySadPathTests.cs
│ ├── ConfigurationObjectFactoryTests.cs
│ ├── ConstructorOrderInfoTests.cs
│ ├── DefaultTypesTests.cs
│ ├── Directory.Build.props
│ ├── ResolverTests.cs
│ ├── RockLib.Configuration.ObjectFactory.Tests.csproj
│ └── ValueConvertersTests.cs
├── RockLib.Configuration.ProxyFactory.Tests
│ ├── .editorconfig
│ ├── Directory.Build.props
│ ├── ProxyFactoryTests.cs
│ └── RockLib.Configuration.ProxyFactory.Tests.csproj
└── RockLib.Configuration.Tests
│ ├── .editorconfig
│ ├── AssemblySettings.cs
│ ├── CompositeSectionTests.cs
│ ├── Directory.Build.props
│ └── RockLib.Configuration.Tests.csproj
└── icon.png
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | ## Description
2 |
3 |
12 |
13 | ## Type of change:
14 |
15 | 1. Non-functional change (e.g. documentation changes, removing unused `using` directives, renaming local variables, etc)
16 | 2. Bug fix (non-breaking change that fixes an issue)
17 | 3. New feature (non-breaking change that adds functionality)
18 | 4. Breaking change (fix or feature that could cause existing functionality to not work as expected)
19 |
20 | ## Checklist:
21 |
22 | - Have you reviewed your own code? Do you understand every change?
23 | - Are you following the [contributing guidelines](../blob/main/CONTRIBUTING.md)?
24 | - Have you added tests that prove your fix is effective or that this feature works?
25 | - New and existing unit tests pass locally with these changes?
26 | - Have you made corresponding changes to the documentation?
27 | - Will this change require an update to an example project? (if so, create an issue and link to it)
28 |
29 | ---
30 |
31 | _[Reviewer guidelines](../blob/main/CONTRIBUTING.md#reviewing-changes)_
32 |
--------------------------------------------------------------------------------
/.github/workflows/build-and-publish.yml:
--------------------------------------------------------------------------------
1 | name: Build and Publish
2 |
3 | ####################################################################################################
4 | ## WORKFLOW TRIGGER
5 | ####################################################################################################
6 | on:
7 | # Workflow will run when a release is published.
8 | release:
9 | types: [ released, prereleased ]
10 |
11 | ####################################################################################################
12 | ## WORKFLOW JOBS
13 | ####################################################################################################
14 | jobs:
15 | # Calls the shared build-and-publish workflow.
16 | call_build_and_publish:
17 | name: Call build-and-publish workflow
18 | uses: RockLib/RockLib.Workflows/.github/workflows/build-and-publish.yml@main
19 | secrets: inherit
20 |
--------------------------------------------------------------------------------
/.github/workflows/build-and-test.yml:
--------------------------------------------------------------------------------
1 | name: Run Unit Test
2 |
3 | ####################################################################################################
4 | ## WORKFLOW TRIGGER
5 | ####################################################################################################
6 | on:
7 | # Workflow will run on pull requests to the main branch.
8 | pull_request:
9 | branches: [ main ]
10 |
11 | ####################################################################################################
12 | ## WORKFLOW JOBS
13 | ####################################################################################################
14 | jobs:
15 | # Calls the shared unit-test workflow.
16 | call_unit_test:
17 | name: Call unit-test workflow
18 | uses: RockLib/RockLib.Workflows/.github/workflows/unit-test.yml@main
19 | secrets: inherit
20 |
--------------------------------------------------------------------------------
/.github/workflows/snyk-test.yml:
--------------------------------------------------------------------------------
1 | name: Run Snyk Test
2 |
3 | ####################################################################################################
4 | ## WORKFLOW TRIGGER
5 | ####################################################################################################
6 | on:
7 | # Workflow will run after unit-test is completed.
8 | workflow_run:
9 | workflows: [ Run Unit Test ]
10 | types: [ completed ]
11 |
12 | ####################################################################################################
13 | ## WORKFLOW JOBS
14 | ####################################################################################################
15 | jobs:
16 | # Calls the shared snyk-test workflow.
17 | call_snyk_test:
18 | name: Call snyk-test workflow
19 | uses: RockLib/RockLib.Workflows/.github/workflows/snyk-test.yml@main
20 | secrets: inherit
21 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as
6 | contributors and maintainers pledge to making participation in our project and
7 | our community a harassment-free experience for everyone, regardless of age, body
8 | size, disability, ethnicity, sex characteristics, gender identity and expression,
9 | level of experience, education, socio-economic status, nationality, personal
10 | appearance, race, religion, or sexual identity and orientation.
11 |
12 | ## Our Standards
13 |
14 | Examples of behavior that contributes to creating a positive environment
15 | include:
16 |
17 | * Using welcoming and inclusive language
18 | * Being respectful of differing viewpoints and experiences
19 | * Gracefully accepting constructive criticism
20 | * Focusing on what is best for the community
21 | * Showing empathy towards other community members
22 |
23 | Examples of unacceptable behavior by participants include:
24 |
25 | * The use of sexualized language or imagery and unwelcome sexual attention or
26 | advances
27 | * Trolling, insulting/derogatory comments, and personal or political attacks
28 | * Public or private harassment
29 | * Publishing others' private information, such as a physical or electronic
30 | address, without explicit permission
31 | * Other conduct which could reasonably be considered inappropriate in a
32 | professional setting
33 |
34 | ## Our Responsibilities
35 |
36 | Project maintainers are responsible for clarifying the standards of acceptable
37 | behavior and are expected to take appropriate and fair corrective action in
38 | response to any instances of unacceptable behavior.
39 |
40 | Project maintainers have the right and responsibility to remove, edit, or
41 | reject comments, commits, code, wiki edits, issues, and other contributions
42 | that are not aligned to this Code of Conduct, or to ban temporarily or
43 | permanently any contributor for other behaviors that they deem inappropriate,
44 | threatening, offensive, or harmful.
45 |
46 | ## Scope
47 |
48 | This Code of Conduct applies both within project spaces and in public spaces
49 | when an individual is representing the project or its community. Examples of
50 | representing a project or community include using an official project e-mail
51 | address, posting via an official social media account, or acting as an appointed
52 | representative at an online or offline event. Representation of a project may be
53 | further defined and clarified by project maintainers.
54 |
55 | ## Enforcement
56 |
57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
58 | reported by contacting the project team at RockLibSupport@quickenloans.com. All
59 | complaints will be reviewed and investigated and will result in a response that
60 | is deemed necessary and appropriate to the circumstances. The project team is
61 | obligated to maintain confidentiality with regard to the reporter of an incident.
62 | Further details of specific enforcement policies may be posted separately.
63 |
64 | Project maintainers who do not follow or enforce the Code of Conduct in good
65 | faith may face temporary or permanent repercussions as determined by other
66 | members of the project's leadership.
67 |
68 | ## Attribution
69 |
70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
72 |
73 | [homepage]: https://www.contributor-covenant.org
74 |
75 | For answers to common questions about this code of conduct, see
76 | https://www.contributor-covenant.org/faq
77 |
--------------------------------------------------------------------------------
/Examples/Example.AppConfig.DotNetFramework/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/Examples/Example.AppConfig.DotNetFramework/Example.AppConfig.DotNetFramework.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | Exe
4 | net8.0
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | Always
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/Examples/Example.AppConfig.DotNetFramework/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using RockLib.Configuration;
3 |
4 | namespace Example.AppConfig.DotNetFramework
5 | {
6 | class Program
7 | {
8 | static void Main(string[] args)
9 | {
10 | Console.WriteLine("App.Config File Example Harness");
11 |
12 | var key100 = Config.AppSettings["Key100"];
13 |
14 | Console.WriteLine($"Key100: {key100}");
15 |
16 | Console.ReadLine();
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/Examples/Example.EnvironmentVariableConfig.DotNetCore/Example.EnvironmentVariableConfig.DotNetCore.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | Exe
4 | net8.0
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | Always
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/Examples/Example.EnvironmentVariableConfig.DotNetCore/Program.cs:
--------------------------------------------------------------------------------
1 | using Newtonsoft.Json;
2 | using RockLib.Configuration;
3 | using System;
4 | using Microsoft.Extensions.Configuration;
5 | using RockLib.Configuration.ObjectFactory;
6 |
7 | namespace Example.EnvironmentVariableConfig.DotNetCore
8 | {
9 | class Program
10 | {
11 | static void Main(string[] args)
12 | {
13 | Console.WriteLine("RockLib.Configuration Environment Variable Configuration Example");
14 |
15 | // Note that the current version (0.0.1-alpha02) requires that the rocklib.config.json file exists.
16 | // In order to successfully load, this file must a valid JSON object. A config file with an empty
17 | // JSON object (open & close curly braces) is the current work-around for this.
18 |
19 | SetEnvironmentVariables();
20 |
21 | var testKey1 = Config.AppSettings["test_key1"];
22 | var testKey2 = Config.AppSettings["test_key2"];
23 |
24 | IConfigurationSection fooConfigSection = Config.Root.GetSection("foo_section");
25 | Foo foo = fooConfigSection.Create();
26 |
27 | IConfigurationSection quxConfigSection = Config.Root.GetSection("qux_section");
28 | Qux qux = quxConfigSection.Create();
29 |
30 | Console.WriteLine($"testKey1: {testKey1}");
31 | Console.WriteLine($"testKey2: {testKey2}");
32 | Console.WriteLine($"foo: {JsonConvert.SerializeObject(foo)}");
33 | Console.WriteLine($"qux: {JsonConvert.SerializeObject(qux)}");
34 |
35 | Console.ReadLine();
36 | }
37 |
38 | private static void SetEnvironmentVariables()
39 | {
40 | // Note: these environment variables could be set up on any level (machine, user, process) with
41 | // the same values, and this example would still work. Setting environment variables programmatically
42 | // means that they are set on the process level.
43 |
44 | Environment.SetEnvironmentVariable("AppSettings:test_key1", "123");
45 | Environment.SetEnvironmentVariable("AppSettings:test_key2", "789");
46 |
47 | Environment.SetEnvironmentVariable("foo_section:Bar", "123.45");
48 | Environment.SetEnvironmentVariable("foo_section:Baz", "abc");
49 |
50 | Environment.SetEnvironmentVariable("qux_section:Garply", "Fred");
51 | Environment.SetEnvironmentVariable("qux_section:Foos:0:Bar", "1.1");
52 | Environment.SetEnvironmentVariable("qux_section:Foos:0:Baz", "aaa");
53 | Environment.SetEnvironmentVariable("qux_section:Foos:1:Bar", "2.2");
54 | Environment.SetEnvironmentVariable("qux_section:Foos:1:Baz", "bbb");
55 | Environment.SetEnvironmentVariable("qux_section:Foos:2:Bar", "3.3");
56 | Environment.SetEnvironmentVariable("qux_section:Foos:2:Baz", "ccc");
57 | }
58 | }
59 |
60 | public class Foo
61 | {
62 | public double Bar { get; set; }
63 | public string Baz { get; set; }
64 | }
65 |
66 | public class Qux
67 | {
68 | public Garply Garply { get; set; }
69 |
70 | public Foo[] Foos { get; set; }
71 | }
72 |
73 | public enum Garply
74 | {
75 | Grault,
76 | Thud,
77 | Fred
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/Examples/Example.EnvironmentVariableConfig.DotNetCore/appsettings.json:
--------------------------------------------------------------------------------
1 | {}
--------------------------------------------------------------------------------
/Examples/Example.FileConfig.DotNetCore/Example.FileConfig.DotNetCore.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | Exe
4 | net8.0
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | Always
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/Examples/Example.FileConfig.DotNetCore/Program.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.Configuration;
2 | using Newtonsoft.Json;
3 | using RockLib.Configuration;
4 | using System;
5 | using RockLib.Configuration.ObjectFactory;
6 |
7 | namespace Example.FileConfig.DotNetCore
8 | {
9 | class Program
10 | {
11 | static void Main(string[] args)
12 | {
13 | Console.WriteLine("Configuration Manager Example Harness");
14 |
15 | try
16 | {
17 | var key1 = Config.AppSettings["Key1"];
18 | string defaultConnectionString = Config.Root.GetConnectionString("Default");
19 |
20 | IConfigurationSection fooConfigSection = Config.Root.GetSection("foo_section");
21 | Foo foo = fooConfigSection.Create();
22 |
23 | IConfigurationSection foo2ConfigSection = Config.Root.GetSection("foo_section");
24 | Foo foo2 = foo2ConfigSection.Create();
25 |
26 | Console.WriteLine($"key1: {key1}");
27 | Console.WriteLine($"defaultConnectionString: {defaultConnectionString}");
28 | Console.WriteLine($"foo: {JsonConvert.SerializeObject(foo)}");
29 | Console.WriteLine($"foo is same instance as foo2: {ReferenceEquals(foo, foo2)}");
30 |
31 | var notFound = Config.AppSettings["notFound"];
32 | }
33 | catch (Exception e)
34 | {
35 | Console.WriteLine(e);
36 | }
37 |
38 | Console.ReadLine();
39 |
40 | }
41 |
42 | public class Foo
43 | {
44 | public int Bar { get; set; }
45 | public string Baz { get; set; }
46 | public bool Qux { get; set; }
47 | }
48 | }
49 | }
--------------------------------------------------------------------------------
/Examples/Example.FileConfig.DotNetCore/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "AppSettings": {
3 | "Key1": "Key1",
4 | "Key2": "Key2"
5 | },
6 | "ConnectionStrings": {
7 | "Default": "schema://path"
8 | },
9 | "Foo": {
10 | "Bar": 123,
11 | "Baz": "abc",
12 | "Qux": true
13 | }
14 | }
--------------------------------------------------------------------------------
/Examples/Example.MemoryLeakTest/Example.MemoryLeakTest.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | Exe
4 | net6.0
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/Examples/Example.MemoryLeakTest/Program.cs:
--------------------------------------------------------------------------------
1 | using RockLib.Configuration;
2 | using RockLib.Configuration.ObjectFactory;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Diagnostics;
6 | using System.Threading;
7 | using System.Threading.Tasks;
8 |
9 | var baseWorkingSet = Process.GetCurrentProcess().WorkingSet64;
10 |
11 | while (true)
12 | {
13 | await new Tester().Test();
14 |
15 | var tasks = new List();
16 |
17 | for (var i = 0; i < 8; i++)
18 | {
19 | tasks.Add(Task.Run(async () => await new Tester().Test()));
20 | }
21 |
22 | await Task.WhenAll(tasks);
23 |
24 | var currentWorkingSet = Process.GetCurrentProcess().WorkingSet64;
25 | //Console.WriteLine($"Current count: {Tester._total}");
26 | Console.WriteLine($"Current working set: {currentWorkingSet} - diff is {((currentWorkingSet - baseWorkingSet) / (double)baseWorkingSet) * 100}");
27 | Console.ReadLine();
28 | }
29 |
30 | public class Tester
31 | {
32 | private static int _counter = 0;
33 | public static int _total = 0;
34 |
35 | public async Task Test()
36 | {
37 | for (int i = 0; i < 10000; i++)
38 | {
39 | var count = i;
40 | Log(count);
41 | }
42 |
43 | await Task.CompletedTask;
44 | }
45 |
46 | public interface ILogger : IDisposable { }
47 |
48 | public class Logger : ILogger
49 | {
50 | public Logger() { }
51 |
52 | public void Dispose() { }
53 | }
54 |
55 | public void Log(int i)
56 | {
57 | Interlocked.Increment(ref _counter);
58 |
59 | var config = Config.Root.GetCompositeSection("RockLib_Logging", "RockLib.Logging");
60 | var defaultTypes = new DefaultTypes();
61 | if (!defaultTypes.TryGet(typeof(ILogger), out var dummy))
62 | defaultTypes.Add(typeof(ILogger), typeof(Logger));
63 |
64 | ILogger logger = null;
65 | try
66 | {
67 | logger = config.CreateReloadingProxy(defaultTypes, null, null);
68 | }
69 | finally
70 | {
71 | //logger.Dispose();
72 | }
73 | Interlocked.Decrement(ref _counter);
74 | Interlocked.Increment(ref _total);
75 |
76 | int count = _total;
77 |
78 | //if (count % 1000 == 0)
79 | // Console.WriteLine(count);
80 | }
81 | }
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | Copyright (C) 2017-2021 Rocket Mortgage
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4 |
5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6 |
7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
8 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | This repository contains the source code for four nuget packages:
2 |
3 | ### [RockLib.Configuration](RockLib.Configuration/README.md)
4 |
5 | Defines a static `Config` class as a general replacement for the old .NET Framework `ConfigurationManager` class.
6 |
7 | ### [RockLib.Configuration.ObjectFactory](RockLib.Configuration.ObjectFactory/README.md)
8 |
9 | An alternative to `Microsoft.Extensions.Configuration.Binder` that supports non-default constructors and other features commonly found in JSON and XML serializers.
10 |
11 | ### [RockLib.Configuration.ProxyFactory](RockLib.Configuration.ProxyFactory/README.md)
12 |
13 | A factory that creates instances of property-only interfaces, defined at run-time, and populated with values defined in an instance of `IConfiguration`.
14 |
15 | ### [RockLib.Configuration.MessagingProvider](RockLib.Configuration.MessagingProvider/README.md)
16 |
17 | A configuration provider that reloads when it receives a message containing configuration changes from a `RockLib.Messaging.IReceiver`.
18 |
--------------------------------------------------------------------------------
/RockLib.Configuration.AspNetCore/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | end_of_line = lf
5 |
6 | [*.cs]
7 | # Styling
8 | indent_style = space
9 | indent_size = 4
10 | csharp_indent_case_contents = true
11 | csharp_indent_switch_labels = true
12 | csharp_new_line_before_catch = true
13 | csharp_new_line_before_else = true
14 | csharp_new_line_before_finally = true
15 | csharp_new_line_before_members_in_anonymous_types = false
16 | csharp_new_line_before_members_in_object_initializers = false
17 | csharp_new_line_before_open_brace = methods, control_blocks, types, properties, lambdas, accessors, object_collection_array_initializers
18 | csharp_new_line_between_query_expression_clauses = true
19 | csharp_prefer_braces = false:suggestion
20 | csharp_prefer_simple_default_expression = true:suggestion
21 | csharp_preferred_modifier_order = public,private,internal,protected,static,readonly,async,override,sealed:suggestion
22 | csharp_preserve_single_line_blocks = true
23 | csharp_preserve_single_line_statements = true
24 | csharp_space_after_cast = false
25 | csharp_space_after_colon_in_inheritance_clause = true
26 | csharp_space_after_keywords_in_control_flow_statements = true
27 | csharp_space_before_colon_in_inheritance_clause = true
28 | csharp_space_between_method_call_empty_parameter_list_parentheses = false
29 | csharp_space_between_method_call_name_and_opening_parenthesis = false
30 | csharp_space_between_method_call_parameter_list_parentheses = false
31 | csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
32 | csharp_space_between_method_declaration_parameter_list_parentheses = false
33 | csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
34 | csharp_style_expression_bodied_accessors = true:suggestion
35 | csharp_style_expression_bodied_constructors = false:suggestion
36 | csharp_style_expression_bodied_methods = false:suggestion
37 | csharp_style_expression_bodied_properties = true:suggestion
38 | csharp_style_inlined_variable_declaration = true:suggestion
39 | csharp_style_var_elsewhere = true:suggestion
40 | csharp_style_var_for_built_in_types = true:suggestion
41 | csharp_style_var_when_type_is_apparent = true:suggestion
42 | dotnet_sort_system_directives_first = false
43 | dotnet_style_explicit_tuple_names = true:suggestion
44 | dotnet_style_object_initializer = true:suggestion
45 | csharp_style_pattern_local_over_anonymous_function = false:suggestion
46 | dotnet_style_predefined_type_for_member_access = true:suggestion
47 | dotnet_style_prefer_inferred_anonymous_type_member_names = false:suggestion
48 | dotnet_style_prefer_inferred_tuple_names = true:suggestion
49 | dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
50 | dotnet_style_require_accessibility_modifiers = for_non_interface_members:suggestion
51 | dotnet_style_qualification_for_field = false:suggestion
52 | dotnet_style_qualification_for_method = false:suggestion
53 | dotnet_style_qualification_for_property = false:suggestion
54 |
55 |
56 | # Analyzer Configuration
57 | # These are rules we want to either ignore or have set as suggestion or info
58 |
59 | # CA1014: Mark assemblies with CLSCompliant
60 | # https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1014
61 | dotnet_diagnostic.CA1014.severity = none
62 |
63 | # CA1725: Parameter names should match base declaration
64 | # https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1725
65 | dotnet_diagnostic.CA1725.severity = suggestion
66 |
67 | # CA2227: Collection properties should be read only
68 | # https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca2227
69 | dotnet_diagnostic.CA2227.severity = suggestion
70 |
--------------------------------------------------------------------------------
/RockLib.Configuration.AspNetCore/AspNetExtensions.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Hosting;
2 | using Microsoft.Extensions.Configuration;
3 | using System;
4 |
5 | namespace RockLib.Configuration.AspNetCore
6 | {
7 | ///
8 | /// Extension methods for ASP.NET Core.
9 | ///
10 | public static class AspNetExtensions
11 | {
12 | ///
13 | /// Sets the value of the property to the
14 | /// containing the merged configuration of the application and the .
15 | ///
16 | /// The to configure.
17 | /// The .
18 | public static IWebHostBuilder SetConfigRoot(this IWebHostBuilder builder)
19 | {
20 | #if NET6_0_OR_GREATER
21 | ArgumentNullException.ThrowIfNull(builder);
22 | #else
23 | if (builder is null)
24 | {
25 | throw new ArgumentNullException(nameof(builder));
26 | }
27 | #endif
28 | builder.ConfigureServices((context, services) =>
29 | {
30 | if (!Config.IsLocked && Config.IsDefault)
31 | Config.SetRoot(context.Configuration);
32 | });
33 | return builder;
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/RockLib.Configuration.AspNetCore/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # RockLib.Configuration.AspNetCore Changelog
2 |
3 | All notable changes to this project will be documented in this file.
4 |
5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7 |
8 | ## 4.0.0 - 2025-01-29
9 |
10 | #### Changed
11 | - Finalized the 4.0.0 version
12 |
13 | ## 4.0.0-alpha.1 - 2025-01-29
14 |
15 | #### Changed
16 | - Removed .NET 6 as a target framework
17 | - Updated RockLib.Configuration to 5.0.0
18 |
19 | ## 3.0.1 - 2024-07-15
20 |
21 | #### Changed
22 | - Updated RockLib.Configuration to 4.0.1 for vulnerability fix.
23 |
24 | ## 3.0.0 - 2024-02-14
25 |
26 | #### Changed
27 | - Finalized the 3.0.0 version
28 |
29 | ## 3.0.0-alpha.1 - 2023-12-14
30 |
31 | #### Changed
32 | - Removed .NET Core 3.1 as a TFM and added .NET 8. Supported targets are now .NET 4.8, .NET 6, and .NET 8.
33 |
34 | ## 2.0.0 - 2022-02-15
35 |
36 | #### Added
37 |
38 | - Added .editorconfig and Directory.Build.props files to ensure consistency.
39 |
40 | #### Changed
41 |
42 | - Supported targets: net6.0, netcoreapp3.1, and net48.
43 | - As the package now uses nullable reference types, some method parameters now specify if they can accept nullable values.
44 |
45 | ## 1.0.5 - 2021-08-11
46 |
47 | #### Changed
48 |
49 | - Changes "Quicken Loans" to "Rocket Mortgage".
50 | - Updates RockLib.Configuration to latest version, [2.5.3](https://github.com/RockLib/RockLib.Configuration/blob/main/RockLib.Configuration/CHANGELOG.md#253---2021-08-11).
51 |
52 | ## 1.0.4 - 2021-05-06
53 |
54 | #### Added
55 |
56 | - Adds SourceLink to nuget package.
57 |
58 | #### Changed
59 |
60 | - Updates RockLib.Configuration package to latest version, which includes SourceLink.
61 | - Updates Microsoft.AspNetCore.Hosting.Abstractions package to latest version.
62 | - For net5.0, use framework reference instead of package reference.
63 |
64 | ----
65 |
66 | **Note:** Release notes in the above format are not available for earlier versions of
67 | RockLib.Configuration.AspNetCore. What follows below are the original release notes.
68 |
69 | ----
70 |
71 | ## 1.0.3
72 |
73 | Adds net5.0 target.
74 |
75 | ## 1.0.2
76 |
77 | Adds icon to project and nuget package.
78 |
79 | ## 1.0.1
80 |
81 | Updates to align with Nuget conventions.
82 |
83 | ## 1.0.0
84 |
85 | Adds `SetConfigRoot` extension method for `IWebHostBuilder`.
86 |
--------------------------------------------------------------------------------
/RockLib.Configuration.AspNetCore/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 | true
4 |
5 |
6 | all
7 | Rocket Mortgage
8 | Copyright 2025 (c) Rocket Mortgage. All rights reserved.
9 | latest
10 | enable
11 | net48;net8.0
12 | NU1603,NU1701
13 | true
14 |
15 |
16 |
17 | all
18 | runtime; build; native; contentfiles; analyzers; buildtransitive
19 |
20 |
21 |
--------------------------------------------------------------------------------
/RockLib.Configuration.AspNetCore/RockLib.Configuration.AspNetCore.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | RockLib.Configuration.AspNetCore
4 | 4.0.0
5 | Extension methods for RockLib.Configuration and ASP.NET Core.
6 | bin\$(Configuration)\$(TargetFramework)\$(PackageId).xml
7 | false
8 | A changelog is available at https://github.com/RockLib/RockLib.Configuration/blob/main/RockLib.Configuration.AspNetCore/CHANGELOG.md.
9 | https://github.com/RockLib/RockLib.Configuration/tree/master/RockLib.Configuration.AspNetCore
10 | LICENSE.md
11 | icon.png
12 | RockLib Configuration AspNetCore Extensions
13 | 4.0.0
14 | True
15 | True
16 | True
17 | Embedded
18 |
19 |
20 | true
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/RockLib.Configuration.AspNetCore/RockLib.Configuration.AspNetCore.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 15
4 | VisualStudioVersion = 15.0.27004.2009
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RockLib.Configuration.AspNetCore", "RockLib.Configuration.AspNetCore.csproj", "{CFD500EF-CB3D-4381-8F0F-D62B4FE36080}"
7 | EndProject
8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RockLib.Configuration.AspNetCore.Tests", "..\Tests\RockLib.Configuration.AspNetCore.Tests\RockLib.Configuration.AspNetCore.Tests.csproj", "{E94CEE70-6820-46C5-B2A7-9FBF8D406000}"
9 | EndProject
10 | Global
11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
12 | Debug|Any CPU = Debug|Any CPU
13 | Release|Any CPU = Release|Any CPU
14 | EndGlobalSection
15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
16 | {CFD500EF-CB3D-4381-8F0F-D62B4FE36080}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
17 | {CFD500EF-CB3D-4381-8F0F-D62B4FE36080}.Debug|Any CPU.Build.0 = Debug|Any CPU
18 | {CFD500EF-CB3D-4381-8F0F-D62B4FE36080}.Release|Any CPU.ActiveCfg = Release|Any CPU
19 | {CFD500EF-CB3D-4381-8F0F-D62B4FE36080}.Release|Any CPU.Build.0 = Release|Any CPU
20 | {E94CEE70-6820-46C5-B2A7-9FBF8D406000}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
21 | {E94CEE70-6820-46C5-B2A7-9FBF8D406000}.Debug|Any CPU.Build.0 = Debug|Any CPU
22 | {E94CEE70-6820-46C5-B2A7-9FBF8D406000}.Release|Any CPU.ActiveCfg = Release|Any CPU
23 | {E94CEE70-6820-46C5-B2A7-9FBF8D406000}.Release|Any CPU.Build.0 = Release|Any CPU
24 | EndGlobalSection
25 | GlobalSection(SolutionProperties) = preSolution
26 | HideSolutionNode = FALSE
27 | EndGlobalSection
28 | GlobalSection(ExtensibilityGlobals) = postSolution
29 | SolutionGuid = {DF36CA15-3280-487B-AA81-A9F8C00A4CBB}
30 | EndGlobalSection
31 | EndGlobal
32 |
--------------------------------------------------------------------------------
/RockLib.Configuration.MessagingProvider/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | end_of_line = lf
5 |
6 | [*.cs]
7 | # Styling
8 | indent_style = space
9 | indent_size = 4
10 | csharp_indent_case_contents = true
11 | csharp_indent_switch_labels = true
12 | csharp_new_line_before_catch = true
13 | csharp_new_line_before_else = true
14 | csharp_new_line_before_finally = true
15 | csharp_new_line_before_members_in_anonymous_types = false
16 | csharp_new_line_before_members_in_object_initializers = false
17 | csharp_new_line_before_open_brace = methods, control_blocks, types, properties, lambdas, accessors, object_collection_array_initializers
18 | csharp_new_line_between_query_expression_clauses = true
19 | csharp_prefer_braces = false:suggestion
20 | csharp_prefer_simple_default_expression = true:suggestion
21 | csharp_preferred_modifier_order = public,private,internal,protected,static,readonly,async,override,sealed:suggestion
22 | csharp_preserve_single_line_blocks = true
23 | csharp_preserve_single_line_statements = true
24 | csharp_space_after_cast = false
25 | csharp_space_after_colon_in_inheritance_clause = true
26 | csharp_space_after_keywords_in_control_flow_statements = true
27 | csharp_space_before_colon_in_inheritance_clause = true
28 | csharp_space_between_method_call_empty_parameter_list_parentheses = false
29 | csharp_space_between_method_call_name_and_opening_parenthesis = false
30 | csharp_space_between_method_call_parameter_list_parentheses = false
31 | csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
32 | csharp_space_between_method_declaration_parameter_list_parentheses = false
33 | csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
34 | csharp_style_expression_bodied_accessors = true:suggestion
35 | csharp_style_expression_bodied_constructors = false:suggestion
36 | csharp_style_expression_bodied_methods = false:suggestion
37 | csharp_style_expression_bodied_properties = true:suggestion
38 | csharp_style_inlined_variable_declaration = true:suggestion
39 | csharp_style_var_elsewhere = true:suggestion
40 | csharp_style_var_for_built_in_types = true:suggestion
41 | csharp_style_var_when_type_is_apparent = true:suggestion
42 | dotnet_sort_system_directives_first = false
43 | dotnet_style_explicit_tuple_names = true:suggestion
44 | dotnet_style_object_initializer = true:suggestion
45 | csharp_style_pattern_local_over_anonymous_function = false:suggestion
46 | dotnet_style_predefined_type_for_member_access = true:suggestion
47 | dotnet_style_prefer_inferred_anonymous_type_member_names = false:suggestion
48 | dotnet_style_prefer_inferred_tuple_names = true:suggestion
49 | dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
50 | dotnet_style_require_accessibility_modifiers = for_non_interface_members:suggestion
51 | dotnet_style_qualification_for_field = false:suggestion
52 | dotnet_style_qualification_for_method = false:suggestion
53 | dotnet_style_qualification_for_property = false:suggestion
54 |
55 |
56 | # Analyzer Configuration
57 | # These are rules we want to either ignore or have set as suggestion or info
58 |
59 | # CA1014: Mark assemblies with CLSCompliant
60 | # https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1014
61 | dotnet_diagnostic.CA1014.severity = none
62 |
63 | # CA1725: Parameter names should match base declaration
64 | # https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1725
65 | dotnet_diagnostic.CA1725.severity = suggestion
66 |
67 | # CA2227: Collection properties should be read only
68 | # https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca2227
69 | dotnet_diagnostic.CA2227.severity = suggestion
70 |
--------------------------------------------------------------------------------
/RockLib.Configuration.MessagingProvider/AssemblyInformation.cs:
--------------------------------------------------------------------------------
1 | using System.Runtime.CompilerServices;
2 |
3 | [assembly: InternalsVisibleTo("RockLib.Configuration.MessagingProvider.Tests")]
--------------------------------------------------------------------------------
/RockLib.Configuration.MessagingProvider/BlocklistSettingFilter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 |
4 | namespace RockLib.Configuration.MessagingProvider
5 | {
6 | ///
7 | /// An implementation of that does not allow changes to
8 | /// settings that are members of a blocklist.
9 | ///
10 | public sealed class BlocklistSettingFilter : ISettingFilter
11 | {
12 | private readonly HashSet _blockedSettings;
13 |
14 | ///
15 | /// Initializes a new instance of the class.
16 | ///
17 | ///
18 | /// The collection of settings (and their child settings) that will be blocked.
19 | ///
20 | ///
21 | /// An optional setting filter that is applied if a setting is not a
22 | /// member of .
23 | ///
24 | public BlocklistSettingFilter(IEnumerable blockedSettings, ISettingFilter? innerFilter = null)
25 | {
26 | #if NET6_0_OR_GREATER
27 | ArgumentNullException.ThrowIfNull(blockedSettings);
28 | #else
29 | if (blockedSettings is null)
30 | {
31 | throw new ArgumentNullException(nameof(blockedSettings));
32 | }
33 | #endif
34 | _blockedSettings = new HashSet(blockedSettings, StringComparer.OrdinalIgnoreCase);
35 | InnerFilter = innerFilter ?? NullSettingFilter.Instance;
36 | }
37 |
38 | ///
39 | /// Gets the collection of settings (and their child settings) that will be blocked.
40 | ///
41 | public IEnumerable BlockedSettings => _blockedSettings;
42 |
43 | ///
44 | /// Gets the inner setting filter that is applied if a setting is not a
45 | /// member of .
46 | ///
47 | public ISettingFilter InnerFilter { get; }
48 |
49 | ///
50 | /// Returns whether the specified setting should be allowed to be changed.
51 | ///
52 | /// The setting to potentially change.
53 | ///
54 | /// The headers of the message that was received that has initiated the change.
55 | ///
56 | ///
57 | /// if the setting is allowed to be changed; otherwise
58 | /// if the setting is not allowed to be changed.
59 | ///
60 | public bool ShouldProcessSettingChange(string setting, IReadOnlyDictionary receivedMessageHeaders) =>
61 | !_blockedSettings.HasSetting(setting) && InnerFilter.ShouldProcessSettingChange(setting, receivedMessageHeaders);
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/RockLib.Configuration.MessagingProvider/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # RockLib.Configuration.MessagingProvider Changelog
2 |
3 | All notable changes to this project will be documented in this file.
4 |
5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7 |
8 | ## 4.0.0 - 2025-01-29
9 |
10 | #### Removed
11 | - Removed support for .NET 6.
12 |
13 | ## 3.1.1 - 2024-07-19
14 |
15 | #### Changed
16 | - RockLib.Messaging.4.0.0 -> RockLib.Messaging.4.0.1 for vulnerability fix.
17 |
18 | ## 3.1.0 - 2024-03-12
19 |
20 | #### Changed
21 | - Finalized 3.1.0 version.
22 |
23 | ## 3.1.0-alpha.1 - 2024-03-11
24 |
25 | #### Changed
26 | - Updated RockLib.Messaging package reference to 4.0.0.
27 |
28 | ## 3.0.0 - 2024-02-14
29 |
30 | #### Changed
31 | - Finalized 3.0.0 version.
32 |
33 | ## 3.0.0-alpha.1 - 2023-12-14
34 |
35 | #### Changed
36 | - Removed .NET Core 3.1 as a TFM and added .NET 8. Supported targets are now .NET 4.8, .NET 6, and .NET 8.
37 |
38 | ## 2.0.0 - 2022-03-14
39 |
40 | #### Added
41 | - Added `.editorconfig` and `Directory.Build.props` files to ensure consistency.
42 |
43 | #### Changed
44 | - Supported targets: net6.0, netcoreapp3.1, and net48.
45 | - As the package now uses nullable reference types, some method parameters now specify if they can accept nullable values.
46 | - Changes "Quicken Loans" to "Rocket Mortgage".
47 |
48 | ## 1.0.5 - 2021-05-06
49 |
50 | #### Added
51 |
52 | - Adds SourceLink to nuget package.
53 |
54 | #### Changed
55 |
56 | - Updates RockLib.Messaging package to latest version, which includes SourceLink.
57 | - Updates Newtonsoft.Json package to latest version.
58 |
59 | ----
60 |
61 | **Note:** Release notes in the above format are not available for earlier versions of
62 | RockLib.Configuration.MessagingProvider. What follows below are the original release notes.
63 |
64 | ----
65 |
66 | ## 1.0.4
67 |
68 | Adds net5.0 target.
69 |
70 | ## 1.0.3
71 |
72 | Adds icon to project and nuget package.
73 |
74 | ## 1.0.2
75 |
76 | Update dependency package.
77 |
78 | ## 1.0.1
79 |
80 | Updates to align with Nuget conventions.
81 |
82 | ## 1.0.0
83 |
84 | Initial release.
85 |
86 | ## 1.0.0-alpha01
87 |
88 | Initial prerelease.
89 |
--------------------------------------------------------------------------------
/RockLib.Configuration.MessagingProvider/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 | true
4 |
5 |
6 | all
7 | Rocket Mortgage
8 | Copyright 2025 (c) Rocket Mortgage. All rights reserved.
9 | latest
10 | enable
11 | net48;net8.0
12 | NU1603,NU1701
13 | true
14 |
15 |
16 |
17 | all
18 | runtime; build; native; contentfiles; analyzers; buildtransitive
19 |
20 |
21 |
--------------------------------------------------------------------------------
/RockLib.Configuration.MessagingProvider/ISettingFilter.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace RockLib.Configuration.MessagingProvider
4 | {
5 | ///
6 | /// Defines an object that can determine if a specific setting from a received message
7 | /// is allowed to be changed.
8 | ///
9 | public interface ISettingFilter
10 | {
11 | ///
12 | /// Returns whether the specified setting should be allowed to be changed.
13 | ///
14 | /// The setting to potentially change.
15 | ///
16 | /// The headers of the message that was received that has initiated the change.
17 | ///
18 | ///
19 | /// if the setting is allowed to be changed; otherwise
20 | /// if the setting is not allowed to be changed.
21 | ///
22 | bool ShouldProcessSettingChange(string setting, IReadOnlyDictionary receivedMessageHeaders);
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/RockLib.Configuration.MessagingProvider/ListSettingFilterExtensions.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace RockLib.Configuration.MessagingProvider
4 | {
5 | internal static class ListSettingFilterExtensions
6 | {
7 | internal static bool HasSetting(this HashSet settings, string setting)
8 | {
9 | foreach (var key in SelfAndAncestors(setting))
10 | {
11 | if (settings.Contains(key))
12 | {
13 | return true;
14 | }
15 | }
16 | return false;
17 | }
18 |
19 | private static IEnumerable SelfAndAncestors(string setting)
20 | {
21 | yield return setting;
22 | var index = setting.LastIndexOf(':');
23 | if (index != -1)
24 | {
25 | #if NET48
26 | foreach (var ancestor in SelfAndAncestors(setting.Substring(0, index)))
27 | #else
28 | foreach (var ancestor in SelfAndAncestors(setting[..index]))
29 | #endif
30 | {
31 | yield return ancestor;
32 | }
33 | }
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/RockLib.Configuration.MessagingProvider/MessagingConfigurationProvider.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.Configuration;
2 | using Newtonsoft.Json;
3 | using RockLib.Messaging;
4 | using System;
5 | using System.Collections.Generic;
6 | using System.Threading.Tasks;
7 |
8 | namespace RockLib.Configuration.MessagingProvider
9 | {
10 | ///
11 | /// An that reloads when it receives
12 | /// a message containing configuration changes.
13 | ///
14 | public sealed class MessagingConfigurationProvider : ConfigurationProvider
15 | {
16 | internal MessagingConfigurationProvider(IReceiver receiver, ISettingFilter? settingFilter)
17 | {
18 | Receiver = receiver;
19 | SettingFilter = settingFilter ?? NullSettingFilter.Instance;
20 | Receiver.Start(OnMessageReceivedAsync);
21 | }
22 |
23 | ///
24 | /// Gets the that receives messages for changing
25 | /// configuration values.
26 | ///
27 | public IReceiver Receiver { get; }
28 |
29 | ///
30 | /// Gets the that is applied to each setting of each
31 | /// received message.
32 | ///
33 | public ISettingFilter SettingFilter { get; }
34 |
35 | private async Task OnMessageReceivedAsync(IReceiverMessage message)
36 | {
37 | var newSettings = new Dictionary(StringComparer.OrdinalIgnoreCase);
38 |
39 | try
40 | {
41 | JsonConvert.PopulateObject(message.StringPayload, newSettings);
42 | }
43 | catch (Exception ex) when (ex is JsonReaderException || ex is JsonSerializationException)
44 | {
45 | await message.RejectAsync().ConfigureAwait(false);
46 | return;
47 | }
48 |
49 | if (IsChanged(newSettings, message.Headers))
50 | {
51 | Data = newSettings;
52 | OnReload();
53 | }
54 |
55 | await message.AcknowledgeAsync().ConfigureAwait(false);
56 | }
57 |
58 | private bool IsChanged(Dictionary newSettings, HeaderDictionary headers)
59 | {
60 | foreach (var newSetting in newSettings)
61 | {
62 | #if NET8_0_OR_GREATER
63 | if (Data.TryGetValue(newSetting.Key, out var value))
64 | {
65 | if (value != newSetting.Value)
66 | {
67 | return true;
68 | }
69 | }
70 | #else
71 | if (Data.ContainsKey(newSetting.Key))
72 | {
73 | if (Data[newSetting.Key] != newSetting.Value)
74 | {
75 | return true;
76 | }
77 | }
78 | #endif
79 | else if (SettingFilter.ShouldProcessSettingChange(newSetting.Key, headers))
80 | {
81 | return true;
82 | }
83 | }
84 |
85 | foreach (var oldSetting in Data)
86 | {
87 | if (!newSettings.ContainsKey(oldSetting.Key))
88 | {
89 | return true;
90 | }
91 | }
92 |
93 | return false;
94 | }
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/RockLib.Configuration.MessagingProvider/MessagingConfigurationSource.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.Configuration;
2 | using RockLib.Messaging;
3 | using System;
4 | using System.Runtime.CompilerServices;
5 |
6 | namespace RockLib.Configuration.MessagingProvider
7 | {
8 | ///
9 | /// An that creates an
10 | /// that reloads when it receives a message containing configuration changes.
11 | ///
12 | public sealed class MessagingConfigurationSource : IConfigurationSource
13 | {
14 | private static readonly ConditionalWeakTable _validationCache = new ConditionalWeakTable();
15 |
16 | private readonly Lazy _cachedProvider;
17 |
18 | ///
19 | /// Initializes a new instance of the class.
20 | ///
21 | ///
22 | /// The that will receive messages in order to change
23 | /// configuration values.
24 | ///
25 | ///
26 | /// The that is applied to each setting of each
27 | /// received message.
28 | ///
29 | public MessagingConfigurationSource(IReceiver receiver, ISettingFilter? settingFilter = null)
30 | {
31 | #if NET6_0_OR_GREATER
32 | ArgumentNullException.ThrowIfNull(receiver);
33 | #else
34 | if (receiver is null)
35 | {
36 | throw new ArgumentNullException(nameof(receiver));
37 | }
38 | #endif
39 | if (!ReferenceEquals(this, _validationCache.GetValue(receiver, r => this)))
40 | {
41 | throw new ArgumentException("The same instance of IReceiver cannot be used to create multiple instances of MessagingConfigurationSource.", nameof(receiver));
42 | }
43 | if (receiver.MessageHandler is not null)
44 | {
45 | throw new ArgumentException("The receiver is already started.", nameof(receiver));
46 | }
47 |
48 | Receiver = receiver;
49 | SettingFilter = settingFilter;
50 | _cachedProvider = new Lazy(() => new MessagingConfigurationProvider(Receiver, SettingFilter));
51 | }
52 |
53 | ///
54 | /// Gets the that will receive messages in order to change
55 | /// configuration values.
56 | ///
57 | public IReceiver Receiver { get; }
58 |
59 | ///
60 | /// Gets the that is applied to each setting of each
61 | /// received message.
62 | ///
63 | public ISettingFilter? SettingFilter { get; }
64 |
65 | ///
66 | /// Builds the for this source.
67 | ///
68 | /// The .
69 | /// A .
70 | public IConfigurationProvider Build(IConfigurationBuilder builder) => _cachedProvider.Value;
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/RockLib.Configuration.MessagingProvider/NullSettingFilter.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace RockLib.Configuration.MessagingProvider
4 | {
5 | ///
6 | /// Singleton implementation of the interface
7 | /// that always returns .
8 | ///
9 | public sealed class NullSettingFilter : ISettingFilter
10 | {
11 | private NullSettingFilter() {}
12 |
13 | ///
14 | /// Gets the instance of .
15 | ///
16 | public static NullSettingFilter Instance { get; } = new NullSettingFilter();
17 |
18 | ///
19 | /// Always returns true.
20 | ///
21 | /// Ignored.
22 | /// Ignored.
23 | /// .
24 | public bool ShouldProcessSettingChange(string setting, IReadOnlyDictionary messageHeaders) => true;
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/RockLib.Configuration.MessagingProvider/README.md:
--------------------------------------------------------------------------------
1 | # :warning: Deprecation Warning :warning:
2 |
3 | This library has been deprecated and will no longer receive updates.
4 |
5 | ---
6 |
7 | RockLib has been a cornerstone of our open source efforts here at Rocket Mortgage, and it's played a significant role in our journey to drive innovation and collaboration within our organization and the open source community. It's been amazing to witness the collective creativity and hard work that you all have poured into this project.
8 |
9 | However, as technology relentlessly evolves, so must we. The decision to deprecate this library is rooted in our commitment to staying at the cutting edge of technological advancements. While this chapter is ending, it opens the door to exciting new opportunities on the horizon.
10 |
11 | We want to express our heartfelt thanks to all the contributors and users who have been a part of this incredible journey. Your contributions, feedback, and enthusiasm have been invaluable, and we are genuinely grateful for your dedication. 🚀
12 |
13 | ---
14 |
15 | # RockLib.Configuration.MessagingProvider
16 |
17 | A configuration provider that reloads when it receives a message containing configuration changes from a `RockLib.Messaging.IReceiver`.
18 |
19 | > [!WARNING]
20 | > The 4.0.0 release of this library will be the final version with upgrades and changes. Bug fixes will continue to be released as needed. We strongly encourage developers to use standard .NET configuration libraries directly like `Microsoft.Extensions.Configuration` in place of `RockLib.Configuration`.
21 |
22 | ## Setup
23 |
24 | Add the messaging provider to an existing configuration builder with the `AddRockLibMessagingProvider` extension (in the `RockLib.Configuration.MessagingProvider` namespace). Note that the messaging configuration provider does not add any values to the configuration initially.
25 |
26 | There are two overloads for the extension method. The first one uses an existing instance of `IReceiver`:
27 |
28 | ```csharp
29 | IReceiver receiver = new NamedPipeReceiver(name: "example_receiver", pipeName: "example-pipe-name");
30 |
31 | ConfigurationBuilder builder = new ConfigurationBuilder()
32 | .AddJsonFile("appsettings.json")
33 | .AddRockLibMessagingProvider(receiver); // Add the messaging provider last
34 | ```
35 |
36 | The other overload creates a new instance of `IReceiver` by name from the configuration under construction:
37 |
38 | ```json
39 | {
40 | "RockLib.Messaging": {
41 | "type": "RockLib.Messaging.NamedPipes.NamedPipeReceiver, RockLib.Messaging.NamedPipes",
42 | "value": {
43 | "name": "example_receiver",
44 | "pipeName": "example-pipe-name"
45 | }
46 | }
47 | }
48 | ```
49 |
50 | ```csharp
51 | ConfigurationBuilder builder = new ConfigurationBuilder()
52 | .AddJsonFile("appsettings.json") // Assume appsettings.json contains the above JSON
53 | .AddRockLibMessagingProvider("example_receiver"); // Add the messaging provider last
54 | ```
55 |
56 | Each of these example adds an equivalent messaging provider to the configuration builder. All `IConfigurationRoot` objects built by the configuration builder will listen to the same `IReceiver` for configuration changes.
57 |
58 | ## Making a configuration change
59 |
60 | This section assumes there is an `appsettings.json` file and a configuration built as follows:
61 |
62 | ```json
63 | {
64 | "AppSettings": {
65 | "Foo": "abc",
66 | "Bar": 123
67 | }
68 | }
69 | ```
70 |
71 | ```csharp
72 | IReceiver receiver = new NamedPipeReceiver(name: "example_receiver", pipeName: "example-pipe-name");
73 |
74 | ConfigurationBuilder builder = new ConfigurationBuilder()
75 | .AddJsonFile("appsettings.json")
76 | .AddRockLibMessagingProvider(receiver);
77 |
78 | IConfigurationRoot configuration = builder.Build();
79 | ```
80 |
81 | To make a configuration change, send a message to the receiver with a JSON payload that describes the configuration changes. In this example, we want to change the values of `AppSettings:Foo` and `AppSettings:Bar` to `"xyz"` and `456` respectively. Note that the JSON is flattened into key/value pairs.
82 |
83 | ```csharp
84 | string configChange = @"{
85 | ""AppSettings:Foo"": ""xyz"",
86 | ""AppSettings:Bar"": 456
87 | }";
88 |
89 | using (ISender sender = new NamedPipeSender(name: "example_receiver", pipeName: "example-pipe-name"))
90 | {
91 | await sender.SendAsync(configChange);
92 | }
93 | ```
94 |
95 | After the message is received, the `configuration` object will have the new values for "AppSettings:Foo" and "AppSettings:Bar".
96 |
97 | ## Filters
98 |
99 | The messaging configuration provider can be protected by passing a instance of the `ISettingFilter` instance to the extension methods. For reference, this is the definition of that interface:
100 |
101 | ```csharp
102 | public interface ISettingFilter
103 | {
104 | bool ShouldProcessSettingChange(string setting, IReadOnlyDictionary receivedMessageHeaders);
105 | }
106 | ```
107 |
108 | When a message is received, each setting is passed to the `ShouldProcessSettingChange` method along with the `Headers` property of the received message. If the method returns true, the setting is changed. Otherwise, the setting is not changed.
109 |
110 | RockLib.Configuration.MessagingProvider has three implementations of the `ISettingFilter` interface:
111 |
112 | - `BlocklistSettingFilter`
113 | - Blocks specified settings, including child settings.
114 | - Has an optional inner filter.
115 | - `SafelistSettingFilter`
116 | - Blocks any settings, including child settings, that are *not* specified.
117 | - Has an optional inner filter.
118 | - `NullSettingFilter`
119 | - Doesn't block any settings.
120 |
--------------------------------------------------------------------------------
/RockLib.Configuration.MessagingProvider/RockLib.Configuration.MessagingProvider.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | Embedded
4 | A configuration provider that reloads when it receives a message containing configuration changes from a RockLib.Messaging IReceiver.
5 | True
6 | True
7 | icon.png
8 | RockLib.Configuration.MessagingProvider
9 | LICENSE.md
10 | https://github.com/RockLib/RockLib.Configuration/tree/master/RockLib.Configuration.MessagingProvider
11 | A changelog is available at https://github.com/RockLib/RockLib.Configuration/blob/main/RockLib.Configuration.MessagingProvider/CHANGELOG.md.
12 | false
13 | Configuration Messaging
14 | 4.0.0
15 | True
16 | 4.0.0
17 |
18 |
19 | bin\$(Configuration)\$(TargetFramework)\$(PackageId).xml
20 |
21 |
22 | true
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/RockLib.Configuration.MessagingProvider/RockLib.Configuration.MessagingProvider.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 16
4 | VisualStudioVersion = 16.0.30804.86
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RockLib.Configuration.MessagingProvider", "RockLib.Configuration.MessagingProvider.csproj", "{592A0564-0266-4601-ACF2-73EA0265F971}"
7 | EndProject
8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RockLib.Configuration.MessagingProvider.Tests", "..\Tests\RockLib.Configuration.MessagingProvider.Tests\RockLib.Configuration.MessagingProvider.Tests.csproj", "{96A46744-4FE4-4E77-A3E5-FAB104BD5B66}"
9 | EndProject
10 | Global
11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
12 | Debug|Any CPU = Debug|Any CPU
13 | Release|Any CPU = Release|Any CPU
14 | EndGlobalSection
15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
16 | {592A0564-0266-4601-ACF2-73EA0265F971}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
17 | {592A0564-0266-4601-ACF2-73EA0265F971}.Debug|Any CPU.Build.0 = Debug|Any CPU
18 | {592A0564-0266-4601-ACF2-73EA0265F971}.Release|Any CPU.ActiveCfg = Release|Any CPU
19 | {592A0564-0266-4601-ACF2-73EA0265F971}.Release|Any CPU.Build.0 = Release|Any CPU
20 | {96A46744-4FE4-4E77-A3E5-FAB104BD5B66}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
21 | {96A46744-4FE4-4E77-A3E5-FAB104BD5B66}.Debug|Any CPU.Build.0 = Debug|Any CPU
22 | {96A46744-4FE4-4E77-A3E5-FAB104BD5B66}.Release|Any CPU.ActiveCfg = Release|Any CPU
23 | {96A46744-4FE4-4E77-A3E5-FAB104BD5B66}.Release|Any CPU.Build.0 = Release|Any CPU
24 | EndGlobalSection
25 | GlobalSection(SolutionProperties) = preSolution
26 | HideSolutionNode = FALSE
27 | EndGlobalSection
28 | GlobalSection(ExtensibilityGlobals) = postSolution
29 | SolutionGuid = {16E62408-B5B7-4BD0-A503-C730909CAE3A}
30 | EndGlobalSection
31 | EndGlobal
32 |
--------------------------------------------------------------------------------
/RockLib.Configuration.MessagingProvider/RockLibMessagingProviderExtensions.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.Configuration;
2 | using RockLib.Messaging;
3 | using System;
4 |
5 | namespace RockLib.Configuration.MessagingProvider
6 | {
7 | ///
8 | /// Extension methods for registering
9 | /// with .
10 | ///
11 | public static class RockLibMessagingProviderExtensions
12 | {
13 | ///
14 | /// Adds an that reloads with changes
15 | /// specified in messages received from a new with the
16 | /// specified name created from the built builder.
17 | ///
18 | /// The to add to.
19 | /// The name of the receiver.
20 | ///
21 | /// The that is applied to each setting of each
22 | /// received message.
23 | ///
24 | /// The .
25 | ///
26 | /// This is how the is created:
27 | ///
28 | /// builder.Build().GetSection("RockLib.Messaging").CreateReceiver(receiverName)
29 | ///
30 | ///
31 | public static IConfigurationBuilder AddRockLibMessagingProvider(this IConfigurationBuilder builder, string receiverName, ISettingFilter? settingFilter = null)
32 | {
33 | #if NET6_0_OR_GREATER
34 | ArgumentNullException.ThrowIfNull(builder);
35 | #else
36 | if (builder is null)
37 | {
38 | throw new ArgumentNullException(nameof(builder));
39 | }
40 | #endif
41 | return builder.AddRockLibMessagingProvider(builder.Build().GetSection("RockLib.Messaging").CreateReceiver(receiverName), settingFilter);
42 | }
43 |
44 | ///
45 | /// Adds an that reloads with changes
46 | /// specified in messages received from the .
47 | ///
48 | /// The to add to.
49 | ///
50 | /// The object that listens for messages that update configuration values.
51 | ///
52 | ///
53 | /// The that is applied to each setting of each
54 | /// received message.
55 | ///
56 | /// The .
57 | public static IConfigurationBuilder AddRockLibMessagingProvider(this IConfigurationBuilder builder, IReceiver receiver, ISettingFilter? settingFilter = null)
58 | {
59 | #if NET6_0_OR_GREATER
60 | ArgumentNullException.ThrowIfNull(builder);
61 | #else
62 | if (builder is null)
63 | {
64 | throw new ArgumentNullException(nameof(builder));
65 | }
66 | #endif
67 | return builder.Add(new MessagingConfigurationSource(receiver, settingFilter));
68 | }
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/RockLib.Configuration.MessagingProvider/SafelistSettingFilter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 |
4 | namespace RockLib.Configuration.MessagingProvider
5 | {
6 | ///
7 | /// An implementation of that only allows changes to
8 | /// settings that are members of a safelist - all other settings are blocked.
9 | ///
10 | public sealed class SafelistSettingFilter : ISettingFilter
11 | {
12 | private readonly HashSet _safeSettings;
13 |
14 | ///
15 | /// Initializes a new instance of the class.
16 | ///
17 | ///
18 | /// The collection of settings (and their child settings) that are safe - all
19 | /// other settings will be blocked.
20 | ///
21 | ///
22 | /// An optional setting filter that is applied if a setting is a
23 | /// member of .
24 | ///
25 | public SafelistSettingFilter(IEnumerable safeSettings, ISettingFilter? innerFilter = null)
26 | {
27 | #if NET6_0_OR_GREATER
28 | ArgumentNullException.ThrowIfNull(safeSettings);
29 | #else
30 | if (safeSettings is null)
31 | {
32 | throw new ArgumentNullException(nameof(safeSettings));
33 | }
34 | #endif
35 | _safeSettings = new HashSet(safeSettings, StringComparer.OrdinalIgnoreCase);
36 | InnerFilter = innerFilter ?? NullSettingFilter.Instance;
37 | }
38 |
39 | ///
40 | /// Gets the collection of settings (and their child settings) that are safe - all
41 | /// other settings will be blocked.
42 | ///
43 | public IEnumerable SafeSettings => _safeSettings;
44 |
45 | ///
46 | /// Gets the inner setting filter that is applied if a setting is a
47 | /// member of .
48 | ///
49 | public ISettingFilter InnerFilter { get; }
50 |
51 | ///
52 | /// Returns whether the specified setting should be allowed to be changed.
53 | ///
54 | /// The setting to potentially change.
55 | ///
56 | /// The headers of the message that was received that has initiated the change.
57 | ///
58 | ///
59 | /// if the setting is allowed to be changed; otherwise
60 | /// if the setting is not allowed to be changed.
61 | ///
62 | public bool ShouldProcessSettingChange(string setting, IReadOnlyDictionary receivedMessageHeaders) =>
63 | _safeSettings.HasSetting(setting) && InnerFilter.ShouldProcessSettingChange(setting, receivedMessageHeaders);
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/RockLib.Configuration.ObjectFactory/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | end_of_line = lf
5 |
6 | [*.cs]
7 | # Styling
8 | indent_style = space
9 | csharp_indent_case_contents = true
10 | csharp_indent_switch_labels = true
11 | csharp_new_line_before_catch = true
12 | csharp_new_line_before_else = true
13 | csharp_new_line_before_finally = true
14 | csharp_new_line_before_members_in_anonymous_types = false
15 | csharp_new_line_before_members_in_object_initializers = false
16 | csharp_new_line_before_open_brace = methods, control_blocks, types, properties, lambdas, accessors, object_collection_array_initializers
17 | csharp_new_line_between_query_expression_clauses = true
18 | csharp_prefer_braces = false:suggestion
19 | csharp_prefer_simple_default_expression = true:suggestion
20 | csharp_preferred_modifier_order = public,private,internal,protected,static,readonly,async,override,sealed:suggestion
21 | csharp_preserve_single_line_blocks = true
22 | csharp_preserve_single_line_statements = true
23 | csharp_space_after_cast = false
24 | csharp_space_after_colon_in_inheritance_clause = true
25 | csharp_space_after_keywords_in_control_flow_statements = true
26 | csharp_space_before_colon_in_inheritance_clause = true
27 | csharp_space_between_method_call_empty_parameter_list_parentheses = false
28 | csharp_space_between_method_call_name_and_opening_parenthesis = false
29 | csharp_space_between_method_call_parameter_list_parentheses = false
30 | csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
31 | csharp_space_between_method_declaration_parameter_list_parentheses = false
32 | csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
33 | csharp_style_expression_bodied_accessors = true:suggestion
34 | csharp_style_expression_bodied_constructors = false:suggestion
35 | csharp_style_expression_bodied_methods = false:suggestion
36 | csharp_style_expression_bodied_properties = true:suggestion
37 | csharp_style_inlined_variable_declaration = true:suggestion
38 | csharp_style_var_elsewhere = true:suggestion
39 | csharp_style_var_for_built_in_types = true:suggestion
40 | csharp_style_var_when_type_is_apparent = true:suggestion
41 | dotnet_sort_system_directives_first = false
42 | dotnet_style_explicit_tuple_names = true:suggestion
43 | dotnet_style_object_initializer = true:suggestion
44 | csharp_style_pattern_local_over_anonymous_function = false:suggestion
45 | dotnet_style_predefined_type_for_member_access = true:suggestion
46 | dotnet_style_prefer_inferred_anonymous_type_member_names = false:suggestion
47 | dotnet_style_prefer_inferred_tuple_names = true:suggestion
48 | dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
49 | dotnet_style_require_accessibility_modifiers = for_non_interface_members:suggestion
50 | dotnet_style_qualification_for_field = false:suggestion
51 | dotnet_style_qualification_for_method = false:suggestion
52 | dotnet_style_qualification_for_property = false:suggestion
53 |
54 |
55 | # Analyzer Configuration
56 | # These are rules we want to either ignore or have set as suggestion or info
57 |
58 | # CA1014: Mark assemblies with CLSCompliant
59 | # https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1014
60 | dotnet_diagnostic.CA1014.severity = none
61 |
62 | # CA1725: Parameter names should match base declaration
63 | # https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1725
64 | dotnet_diagnostic.CA1725.severity = suggestion
65 |
66 | # CA2227: Collection properties should be read only
67 | # https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca2227
68 | dotnet_diagnostic.CA2227.severity = suggestion
--------------------------------------------------------------------------------
/RockLib.Configuration.ObjectFactory/AlternateNameAttribute.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace RockLib.Configuration.ObjectFactory
4 | {
5 | ///
6 | /// Defines an alternate name for a constructor parameter or property.
7 | ///
8 | [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property, AllowMultiple = true)]
9 | public sealed class AlternateNameAttribute : Attribute
10 | {
11 | ///
12 | /// Initializes a new instance of the class.
13 | ///
14 | /// The alternate name.
15 | public AlternateNameAttribute(string name) => Name = name ?? throw new ArgumentNullException(nameof(name));
16 |
17 | ///
18 | /// Gets the alternate name.
19 | ///
20 | public string Name { get; }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/RockLib.Configuration.ObjectFactory/AssemblyInformation.cs:
--------------------------------------------------------------------------------
1 | using System.Runtime.CompilerServices;
2 |
3 | [assembly: InternalsVisibleTo("RockLib.Configuration.ObjectFactory.Tests")]
--------------------------------------------------------------------------------
/RockLib.Configuration.ObjectFactory/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # RockLib.Configuration.ObjectFactory Changelog
2 |
3 | All notable changes to this project will be documented in this file.
4 |
5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7 |
8 | ## 4.0.0 - 2025-01-29
9 |
10 | #### Changed
11 | - Removed .NET 6 as a target framework
12 |
13 | ## 3.0.0 - 2024-02-14
14 |
15 | #### Changed
16 | - Finalized 3.0.0 version.
17 |
18 | ## 3.0.0-alpha.1 - 2023-12-14
19 |
20 | #### Changed
21 | - Removed .NET Core 3.1 as a TFM and added .NET 8. Supported targets are now .NET 4.8, .NET 6, and .NET 8.
22 |
23 | ## 2.0.2 - 2022-06-06
24 |
25 | #### Changed
26 |
27 | **BREAKING CHANGES**
28 |
29 | - This is a bug fix that can be a breaking change. This fix eliminates a memory leak that can occur with `ConfigReloadingProxy`. The type passed to `CreateReloadingProxy()` must now implement `IDisposable`. Note that this is a runtime check, so you should check any `Type` objects passed to this method and make the appropriate updates in your code. Otherwise, your code will fail with `ArgumentException`-based exceptions. Review your code to ensure:
30 | - The interface passed to `CreateReloadingProxy()` must now implement `IDisposable`
31 | - This also means that you must call `Dispose()` on the object reference returned by `CreateReloadingProxy()`
32 |
33 | ## 2.0.1 - 2022-04-25
34 |
35 | #### Changed
36 |
37 | - Updated `CompareTo()` method in `ConstructorOrderInfo` to also compare the parameter types during comparison.
38 |
39 | ## 2.0.0 - 2022-02-07
40 |
41 | #### Added
42 |
43 | - Added `.editorconfig` and `Directory.Build.props` files to ensure consistency.
44 |
45 | #### Changed
46 |
47 | - Supported targets: net6.0, netcoreapp3.1, and net48.
48 | - As the package now uses nullable reference types, some method parameters now specify if they can accept nullable values.
49 | - Updated attributes to be `sealed`.
50 | - Fixes ambiguous constructor issue between injectable and named parameters counts.
51 | - `Resolver` no longer catches all exceptions for invocations of provided `Func<>` objects on construction.
52 |
53 | ## 1.6.9 - 2021-08-11
54 |
55 | #### Changed
56 |
57 | - Changes "Quicken Loans" to "Rocket Mortgage".
58 |
59 | ## 1.6.8 - 2021-05-06
60 |
61 | #### Added
62 |
63 | - Adds SourceLink to nuget package.
64 |
65 | #### Changed
66 |
67 | - Updates System.Reflection.Emit package to latest version.
68 |
69 | ----
70 |
71 | **Note:** Release notes in the above format are not available for earlier versions of
72 | RockLib.Configuration.ObjectFactory. What follows below are the original release notes.
73 |
74 | ----
75 |
76 | ## 1.6.7
77 |
78 | Relaxes restrictions around types that implement IEnumerable. Only implementations of IEnumerable from the System.Collections namespace are disallowed.
79 |
80 | ## 1.6.6
81 |
82 | Adds net5.0 target.
83 |
84 | ## 1.6.5
85 |
86 | Fixes bug when a constructor has an optional nullable enum parameter with default value of null and the parameter was not supplied.
87 |
88 | ## 1.6.4
89 |
90 | Adds support for optional nullable enumerations in contructors.
91 |
92 | ## 1.6.3
93 |
94 | Adds icon to project and nuget package.
95 |
96 | ## 1.6.2
97 |
98 | Updates to align with Nuget conventions.
99 |
100 | ## 1.6.1
101 |
102 | Adds the [ConfigSection] attribute, for use by configuration editing tools.
103 |
104 | ## 1.6.0
105 |
106 | Adds the [AlternateName] attribute, allowing users to specify one or more alternate names for a constructor parameter / writable property.
107 |
108 | ## 1.5.0
109 |
110 | Adds support for members of type `Func`.
111 |
112 | ## 1.4.0
113 |
114 | Adds support for arbitrary DI containers, such as Ninject or Unity.
115 |
116 | ## 1.3.1
117 |
118 | Improves the debugging experience of config reloading proxy objects.
119 |
120 | ## 1.3.0
121 |
122 | Adds the ability to handle identifiers with different casing strategies - PascalCase, camelCase, snake_case, and kebab-case.
123 |
124 | ## 1.2.3
125 |
126 | Fixes two bugs related to members of type `object`:
127 |
128 | - If the target type is `object`, and it is being created by a configuration that has a value, then just use the configuration value directly. Previously, a conversion error would occur.
129 | - If the target type is `object`, and it has a default type of string-to-anything dictionary, and if the configuration has an "object" shape, then create the string dictionary from configuration. Previously, an nonsensical error would occur stating that it couldn't convert the dictionary type to the dictionary type.
130 |
131 | ## 1.2.2
132 |
133 | Improvements around when not to reload:
134 |
135 | - A reloading proxy doesn't reload if its configuration section hasn't changed.
136 | - If a section has `reloadOnChange` explicitly configured to be `false`, then the `CreateReloadingProxy` extension method returns a regular object, not a config reloading proxy object.
137 | - A small expansion of the definition of what a "type-specified object" is to allow a `reloadOnChange` of `false`.
138 |
139 | ## 1.2.1
140 |
141 | Fixes a constructor selection bug.
142 |
143 | ## 1.2.0
144 |
145 | Adds the ability to create a "config reloading proxy" - an object that reloads itself when its configuration changes.
146 |
147 | ## 1.1.4
148 |
149 | Adds support for IReadOnlyDictionary.
150 |
151 | ## 1.1.3
152 |
153 | Fixes a constructor selection bug.
154 |
155 | ## 1.1.2
156 |
157 | Fixes bug where top-level object wouldn't get its default type applied.
158 |
159 | ## 1.1.1
160 |
161 | This version enables read-only properties with a type that implements non-generic IList and does not have a public parameterless constructor.
162 |
163 | ## 1.1.0
164 |
165 | Adds support for non-generic lists.
166 |
167 | ## 1.0.0
168 |
169 | Initial release.
170 |
--------------------------------------------------------------------------------
/RockLib.Configuration.ObjectFactory/ConfigReloadingProxyReferenceModel.cs:
--------------------------------------------------------------------------------
1 | #if REFERENCE_MODEL
2 | using Microsoft.Extensions.Configuration;
3 | using System;
4 |
5 | // These pragmas are here just to quiet the compiler.
6 | // This code is just reference code, it's already been published,
7 | // and it's not worth updating to resolve these CA issues.
8 | #pragma warning disable CA1033 // Interface methods should be callable by child types
9 | #pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
10 | namespace RockLib.Configuration.ObjectFactory.ReferenceModel
11 | {
12 | public interface IFoo
13 | {
14 | int Bar { get; }
15 | int Baz(int factor);
16 | string? Qux { get; set; } // Special case - read/write reference-type property
17 | event EventHandler? Garply;
18 | }
19 |
20 | public class ProxyFoo : ConfigReloadingProxy, IFoo
21 | {
22 | public ProxyFoo(IConfiguration section, DefaultTypes defaultTypes, ValueConverters valueConverters, Type declaringType, string memberName)
23 | : base(section, defaultTypes, valueConverters, declaringType, memberName, null)
24 | {
25 | }
26 |
27 | // Properties are strictly pass-through.
28 | int IFoo.Bar => Object.Bar;
29 |
30 | // Methods are strictly pass-through.
31 | int IFoo.Baz(int qux) => Object.Baz(qux);
32 |
33 | // Properties are strictly pass-through.
34 | string? IFoo.Qux { get => Object.Qux; set => Object.Qux = value; }
35 |
36 | // Events are pass-through, but also need to capture handlers in a private field.
37 | private EventHandler? _garply;
38 | event EventHandler? IFoo.Garply
39 | {
40 | add
41 | {
42 | Object.Garply += value;
43 | _garply += value;
44 | }
45 | remove
46 | {
47 | Object.Garply -= value;
48 | _garply -= value;
49 | }
50 | }
51 |
52 | protected override void TransferState(IFoo oldObject, IFoo newObject)
53 | {
54 | if(oldObject is not null && newObject is not null)
55 | {
56 | // Event handlers from the old object need to be copied to the new one.
57 | newObject.Garply += _garply;
58 |
59 | // Special case for when the interface has a read/write property: if the new
60 | // property value is null and the old property value is not null, then copy
61 | // the value from old to new.
62 | if (oldObject.Qux is not null && newObject.Qux is null)
63 | {
64 | newObject.Qux = oldObject.Qux;
65 | }
66 | }
67 | }
68 | }
69 | }
70 | #pragma warning restore CS1591 // Missing XML comment for publicly visible type or member
71 | #pragma warning restore CA1033 // Interface methods should be callable by child types
72 | #endif
73 |
--------------------------------------------------------------------------------
/RockLib.Configuration.ObjectFactory/ConfigSectionAttribute.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace RockLib.Configuration.ObjectFactory
4 | {
5 | ///
6 | /// Associates the path to a configuration section with a target type. The contents
7 | /// of such a configuration section should declare an object of the target type.
8 | ///
9 | [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
10 | public sealed class ConfigSectionAttribute : Attribute
11 | {
12 | ///
13 | /// Initializes a new instance of the class,
14 | /// associating the specified path to a configuration section with the specified type.
15 | /// The contents of such a configuration section should declare an object of the target type.
16 | ///
17 | ///
18 | /// The path to a target configuration section. The contents of such a configuration
19 | /// section should declare an object of the type of the parameter.
20 | ///
21 | ///
22 | /// The type of object that should be able to be created using a configuration section
23 | /// specified by the parameter.
24 | ///
25 | public ConfigSectionAttribute(string path, Type type)
26 | {
27 | Path = path ?? throw new ArgumentNullException(nameof(path));
28 | Type = type ?? throw new ArgumentNullException(nameof(type));
29 | }
30 |
31 | ///
32 | /// Gets the path to a target configuration section. The contents of such a configuration
33 | /// section should declare an object of the type of the property.
34 | ///
35 | public string Path { get; }
36 |
37 | ///
38 | /// Gets the type of object that should be able to be created using a configuration section
39 | /// specified by the property.
40 | ///
41 | public Type Type { get; }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/RockLib.Configuration.ObjectFactory/ConstructorOrderInfo.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.Configuration;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Reflection;
6 |
7 | namespace RockLib.Configuration.ObjectFactory
8 | {
9 | internal sealed class ConstructorOrderInfo : IComparable
10 | {
11 | public ConstructorOrderInfo(ConstructorInfo constructor, Dictionary availableMembers, IResolver? resolver)
12 | {
13 | Constructor = constructor;
14 | var parameters = constructor.GetParameters();
15 | TotalParameters = parameters.Length;
16 | if (TotalParameters == 0)
17 | {
18 | IsInvokableWithoutDefaultParameters = true;
19 | IsInvokableWithDefaultParameters = true;
20 | MissingParameterNames = new List();
21 | MatchedParameters = 0;
22 | ParameterTypes = new List();
23 | }
24 | else
25 | {
26 | bool HasAvailableValue(ParameterInfo p) =>
27 | p.GetNames().Any(name => availableMembers.ContainsKey(name)) || (resolver?.CanResolve(p) ?? false);
28 | bool HasAvailableNamedValue(ParameterInfo p) =>
29 | p.GetNames().Any(name => availableMembers.ContainsKey(name));
30 |
31 | IsInvokableWithoutDefaultParameters = parameters.Count(HasAvailableValue) == TotalParameters;
32 | IsInvokableWithDefaultParameters = parameters.Count(p => HasAvailableValue(p) || p.HasDefaultValue) == TotalParameters;
33 | MissingParameterNames = parameters.Where(p => !HasAvailableValue(p) && !p.HasDefaultValue).Select(p => p.Name!).ToList();
34 | MatchedParameters = parameters.Count(HasAvailableValue);
35 | MatchedNamedParameters = parameters.Count(HasAvailableNamedValue);
36 | ParameterTypes = parameters.Select(p => p.ParameterType).ToList();
37 | }
38 | }
39 |
40 | public ConstructorInfo Constructor { get; }
41 | public bool IsInvokableWithoutDefaultParameters { get; }
42 | public bool IsInvokableWithDefaultParameters { get; }
43 | public int MatchedParameters { get; }
44 | public int MatchedNamedParameters { get; }
45 | public List ParameterTypes { get; }
46 | public int TotalParameters { get; }
47 | public List MissingParameterNames { get; }
48 |
49 | public int CompareTo(ConstructorOrderInfo? other)
50 | {
51 | if (other is null) return 1;
52 | if (IsInvokableWithoutDefaultParameters && !other.IsInvokableWithoutDefaultParameters) return -1;
53 | if (!IsInvokableWithoutDefaultParameters && other.IsInvokableWithoutDefaultParameters) return 1;
54 | if (IsInvokableWithDefaultParameters && !other.IsInvokableWithDefaultParameters) return -1;
55 | if (!IsInvokableWithDefaultParameters && other.IsInvokableWithDefaultParameters) return 1;
56 | if (MatchedParameters > other.MatchedParameters) return -1;
57 | if (MatchedParameters < other.MatchedParameters) return 1;
58 | if (TotalParameters > other.TotalParameters) return -1;
59 | if (TotalParameters < other.TotalParameters) return 1;
60 | if (MatchedNamedParameters > other.MatchedNamedParameters) return -1;
61 | if (MatchedNamedParameters < other.MatchedNamedParameters) return 1;
62 | // Finds parameter types in ParameterTypes that are not in other.ParameterTypes
63 | if (ParameterTypes.Except(other.ParameterTypes).Any()) return -1;
64 | // Finds parameter types in other.ParameterTypes that are not in ParameterTypes
65 | if (other.ParameterTypes.Except(ParameterTypes).Any()) return 1;
66 | return 0;
67 | }
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/RockLib.Configuration.ObjectFactory/ConvertMethodAttribute.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace RockLib.Configuration.ObjectFactory
4 | {
5 | ///
6 | /// Defines which method should be used to convert a configuration string value to a target type. The method
7 | /// must be static, have a single parameter of type string, and return a type (other than )
8 | /// that is assignable to the member that this attribute decorates.
9 | ///
10 | [AttributeUsage(AttributeTargets.Property | AttributeTargets.Parameter | AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = false, Inherited = false)]
11 | public sealed class ConvertMethodAttribute : Attribute
12 | {
13 | ///
14 | /// Initializes a new instance of the class.
15 | ///
16 | ///
17 | /// The name of the method that does the conversion for the member that this attribute decorates.
18 | ///
19 | public ConvertMethodAttribute(string convertMethodName) => ConvertMethodName = convertMethodName ?? throw new ArgumentNullException(nameof(convertMethodName));
20 |
21 | ///
22 | /// Get the name of the method that does the conversion for the member that this attribute decorates.
23 | ///
24 | public string ConvertMethodName { get; }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/RockLib.Configuration.ObjectFactory/DefaultTypeAttribute.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace RockLib.Configuration.ObjectFactory
4 | {
5 | ///
6 | /// Defines the default type of a property, constructor parameter, class, or interface. The value of this attribute
7 | /// must be assignable to the member it decorates.
8 | ///
9 | [AttributeUsage(AttributeTargets.Property | AttributeTargets.Parameter | AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple = false, Inherited = false)]
10 | public sealed class DefaultTypeAttribute : Attribute
11 | {
12 | ///
13 | /// Initializes a new instance of the type.
14 | ///
15 | /// The default type of the member that this attribute decorates.
16 | public DefaultTypeAttribute(Type value) => Value = value ?? throw new ArgumentNullException(nameof(value));
17 |
18 | ///
19 | /// Gets the default type of the member that this attribute decorates.
20 | ///
21 | public Type Value { get; }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/RockLib.Configuration.ObjectFactory/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 | true
4 |
5 |
6 | all
7 | Rocket Mortgage
8 | latest
9 | enable
10 | net48;net8.0
11 | NU1603,NU1701
12 | true
13 |
14 |
15 |
16 | all
17 | runtime; build; native; contentfiles; analyzers; buildtransitive
18 |
19 |
20 |
--------------------------------------------------------------------------------
/RockLib.Configuration.ObjectFactory/IResolver.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics.CodeAnalysis;
2 | using System.Reflection;
3 |
4 | namespace RockLib.Configuration.ObjectFactory
5 | {
6 | ///
7 | /// Represents a dependency injection container, such as Ninject, Unity,
8 | /// Autofac, or StructureMap.
9 | ///
10 | public interface IResolver
11 | {
12 | ///
13 | /// Returns a value indicating whether a dependency can be retrieved
14 | /// that is suitable for the specified parameter.
15 | ///
16 | ///
17 | /// A constructor parameter that needs a value.
18 | ///
19 | ///
20 | /// true if a dependency can be retrieved for the specified parameter;
21 | /// otherwise, false.
22 | ///
23 | bool CanResolve(ParameterInfo parameter);
24 |
25 | ///
26 | /// Retrieves a dependency suitable for the specified parameter.
27 | ///
28 | ///
29 | /// A constructor parameter that needs a value.
30 | ///
31 | ///
32 | /// When this method returns, contains the object that was resolved
33 | /// for the specified parameter, if it is resolvable; otherwise, null.
34 | /// This parameter is passed uninitialized.
35 | ///
36 | ///
37 | /// true if a dependency was successfully retrieved; otherwise, false.
38 | ///
39 | bool TryResolve(ParameterInfo parameter, [MaybeNullWhen(false)] out object value);
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/RockLib.Configuration.ObjectFactory/InternalsVisibleTo.cs:
--------------------------------------------------------------------------------
1 | #if DEBUG
2 | using System.Runtime.CompilerServices;
3 |
4 | [assembly: InternalsVisibleTo("RockLib.Configuration.ObjectFactory.Tests")]
5 | #endif
--------------------------------------------------------------------------------
/RockLib.Configuration.ObjectFactory/Members.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Reflection;
5 |
6 | namespace RockLib.Configuration.ObjectFactory
7 | {
8 | internal static class Members
9 | {
10 | public static IEnumerable Find(Type? declaringType, string? memberName)
11 | {
12 | if (declaringType is null || memberName is null) return Enumerable.Empty();
13 | var constructorParameters = FindConstructorParameters(declaringType, memberName).ToList();
14 | return FindProperties(declaringType, memberName, constructorParameters).Concat(constructorParameters);
15 | }
16 |
17 | private static IEnumerable FindConstructorParameters(Type declaringType, string memberName) =>
18 | declaringType.GetConstructors(BindingFlags.Public | BindingFlags.Instance)
19 | .SelectMany(c => c.GetParameters())
20 | .Where(p => StringComparer.OrdinalIgnoreCase.Equals(p.Name, memberName))
21 | .Select(p => new Member(p.Name!, p.ParameterType, MemberType.ConstructorParameter));
22 |
23 | private static IEnumerable FindProperties(Type declaringType, string memberName, List constructorParameters) =>
24 | declaringType.GetProperties(BindingFlags.Public | BindingFlags.Instance)
25 | .Where(p => StringComparer.OrdinalIgnoreCase.Equals(p.Name, memberName)
26 | && (p.CanWrite
27 | || ((p.IsReadonlyList() || p.IsReadonlyDictionary())
28 | && !constructorParameters.Any(c => StringComparer.OrdinalIgnoreCase.Equals(c.Name, memberName)))))
29 | .Select(p => new Member(p.Name, p.PropertyType, MemberType.Property));
30 |
31 | public static bool IsReadonlyList(this PropertyInfo p) =>
32 | p.CanRead
33 | && !p.CanWrite
34 | && ((p.PropertyType.IsGenericType
35 | && (p.PropertyType.GetGenericTypeDefinition() == typeof(List<>)
36 | || p.PropertyType.GetGenericTypeDefinition() == typeof(IList<>)
37 | || p.PropertyType.GetGenericTypeDefinition() == typeof(ICollection<>)))
38 | || p.PropertyType.IsNonGenericList());
39 |
40 | public static bool IsReadonlyDictionary(this PropertyInfo p) =>
41 | p.CanRead
42 | && !p.CanWrite
43 | && p.PropertyType.IsGenericType
44 | && (p.PropertyType.GetGenericTypeDefinition() == typeof(Dictionary<,>)
45 | || p.PropertyType.GetGenericTypeDefinition() == typeof(IDictionary<,>));
46 | }
47 |
48 | internal sealed class Member
49 | {
50 | public Member(string name, Type type, MemberType memberType)
51 | {
52 | Name = name;
53 | Type = type;
54 | MemberType = memberType;
55 | }
56 |
57 | public string Name { get; }
58 | public Type Type { get; }
59 | public MemberType MemberType { get; }
60 |
61 | public override string ToString() =>
62 | $"{(MemberType == MemberType.Property ? "Property" : "Constructor parameter")}: {Type} {Name}";
63 | }
64 |
65 | internal enum MemberType
66 | {
67 | Property,
68 | ConstructorParameter
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/RockLib.Configuration.ObjectFactory/NullableAttributes.cs:
--------------------------------------------------------------------------------
1 | // The point of the types in this file are to "patch" in the nullable attributes
2 | // (https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/attributes/nullable-analysis)
3 | // for .NET 4.8. This code came from https://source.dot.net/#System.Private.CoreLib/NullableAttributes.cs
4 |
5 | #if NET48
6 | namespace System.Diagnostics.CodeAnalysis
7 | {
8 | [AttributeUsage(AttributeTargets.Parameter, Inherited = false)]
9 | internal sealed class MaybeNullWhenAttribute
10 | : Attribute
11 | {
12 | /// Initializes the attribute with the specified return value condition.
13 | ///
14 | /// The return value condition. If the method returns this value, the associated parameter may be null.
15 | ///
16 | public MaybeNullWhenAttribute(bool returnValue) => ReturnValue = returnValue;
17 |
18 | /// Gets the return value condition.
19 | public bool ReturnValue { get; }
20 | }
21 | }
22 | #endif
--------------------------------------------------------------------------------
/RockLib.Configuration.ObjectFactory/RockLib.Configuration.ObjectFactory.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | Debug;Release;ReferenceModel
4 |
5 |
6 | RockLib.Configuration.ObjectFactory
7 | 4.0.0
8 | RockLib
9 | Creates objects from IConfiguration and IConfigurationSection objects. A replacement for some of the functionality of Microsoft.Extensions.Configuration.Binder.
10 | false
11 | A changelog is available at https://github.com/RockLib/RockLib.Configuration/blob/main/RockLib.Configuration.ObjectFactory/CHANGELOG.md.
12 | https://github.com/RockLib/RockLib.Configuration/tree/master/RockLib.Configuration.ObjectFactory
13 | LICENSE.md
14 | icon.png
15 | Copyright 2017-2023 (c) Rocket Mortgage. All rights reserved.
16 | Configuration Factory Binder IConfiguration IConfigurationSection
17 | 4.0.0
18 | True
19 | True
20 | True
21 | Embedded
22 |
23 |
24 | bin\$(Configuration)\$(TargetFramework)\$(PackageId).xml
25 |
26 |
27 | true
28 |
29 |
30 |
31 |
32 |
33 |
34 | TRACE;REFERENCE_MODEL
35 | true
36 |
37 |
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/RockLib.Configuration.ObjectFactory/RockLib.Configuration.ObjectFactory.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.2.32516.85
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RockLib.Configuration.ObjectFactory", "RockLib.Configuration.ObjectFactory.csproj", "{1A866445-42C8-4A9B-82C5-D3B34EBF9147}"
7 | EndProject
8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RockLib.Configuration.ObjectFactory.Tests", "..\Tests\RockLib.Configuration.ObjectFactory.Tests\RockLib.Configuration.ObjectFactory.Tests.csproj", "{9DA180C2-E6BD-42E3-B157-D3164F6F269F}"
9 | EndProject
10 | Global
11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
12 | Debug|Any CPU = Debug|Any CPU
13 | ReferenceModel|Any CPU = ReferenceModel|Any CPU
14 | Release|Any CPU = Release|Any CPU
15 | EndGlobalSection
16 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
17 | {1A866445-42C8-4A9B-82C5-D3B34EBF9147}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
18 | {1A866445-42C8-4A9B-82C5-D3B34EBF9147}.Debug|Any CPU.Build.0 = Debug|Any CPU
19 | {1A866445-42C8-4A9B-82C5-D3B34EBF9147}.ReferenceModel|Any CPU.ActiveCfg = ReferenceModel|Any CPU
20 | {1A866445-42C8-4A9B-82C5-D3B34EBF9147}.ReferenceModel|Any CPU.Build.0 = ReferenceModel|Any CPU
21 | {1A866445-42C8-4A9B-82C5-D3B34EBF9147}.Release|Any CPU.ActiveCfg = Release|Any CPU
22 | {1A866445-42C8-4A9B-82C5-D3B34EBF9147}.Release|Any CPU.Build.0 = Release|Any CPU
23 | {9DA180C2-E6BD-42E3-B157-D3164F6F269F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
24 | {9DA180C2-E6BD-42E3-B157-D3164F6F269F}.Debug|Any CPU.Build.0 = Debug|Any CPU
25 | {9DA180C2-E6BD-42E3-B157-D3164F6F269F}.ReferenceModel|Any CPU.ActiveCfg = ReferenceModel|Any CPU
26 | {9DA180C2-E6BD-42E3-B157-D3164F6F269F}.ReferenceModel|Any CPU.Build.0 = ReferenceModel|Any CPU
27 | {9DA180C2-E6BD-42E3-B157-D3164F6F269F}.Release|Any CPU.ActiveCfg = Release|Any CPU
28 | {9DA180C2-E6BD-42E3-B157-D3164F6F269F}.Release|Any CPU.Build.0 = Release|Any CPU
29 | EndGlobalSection
30 | GlobalSection(SolutionProperties) = preSolution
31 | HideSolutionNode = FALSE
32 | EndGlobalSection
33 | GlobalSection(ExtensibilityGlobals) = postSolution
34 | SolutionGuid = {DF36CA15-3280-487B-AA81-A9F8C00A4CBB}
35 | EndGlobalSection
36 | EndGlobal
37 |
--------------------------------------------------------------------------------
/RockLib.Configuration.ProxyFactory/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | end_of_line = lf
5 |
6 | [*.cs]
7 | # Styling
8 | indent_style = space
9 | indent_size = 4
10 | csharp_indent_case_contents = true
11 | csharp_indent_switch_labels = true
12 | csharp_new_line_before_catch = true
13 | csharp_new_line_before_else = true
14 | csharp_new_line_before_finally = true
15 | csharp_new_line_before_members_in_anonymous_types = false
16 | csharp_new_line_before_members_in_object_initializers = false
17 | csharp_new_line_before_open_brace = methods, control_blocks, types, properties, lambdas, accessors, object_collection_array_initializers
18 | csharp_new_line_between_query_expression_clauses = true
19 | csharp_prefer_braces = false:suggestion
20 | csharp_prefer_simple_default_expression = true:suggestion
21 | csharp_preferred_modifier_order = public,private,internal,protected,static,readonly,async,override,sealed:suggestion
22 | csharp_preserve_single_line_blocks = true
23 | csharp_preserve_single_line_statements = true
24 | csharp_space_after_cast = false
25 | csharp_space_after_colon_in_inheritance_clause = true
26 | csharp_space_after_keywords_in_control_flow_statements = true
27 | csharp_space_before_colon_in_inheritance_clause = true
28 | csharp_space_between_method_call_empty_parameter_list_parentheses = false
29 | csharp_space_between_method_call_name_and_opening_parenthesis = false
30 | csharp_space_between_method_call_parameter_list_parentheses = false
31 | csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
32 | csharp_space_between_method_declaration_parameter_list_parentheses = false
33 | csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
34 | csharp_style_expression_bodied_accessors = true:suggestion
35 | csharp_style_expression_bodied_constructors = false:suggestion
36 | csharp_style_expression_bodied_methods = false:suggestion
37 | csharp_style_expression_bodied_properties = true:suggestion
38 | csharp_style_inlined_variable_declaration = true:suggestion
39 | csharp_style_var_elsewhere = true:suggestion
40 | csharp_style_var_for_built_in_types = true:suggestion
41 | csharp_style_var_when_type_is_apparent = true:suggestion
42 | dotnet_sort_system_directives_first = false
43 | dotnet_style_explicit_tuple_names = true:suggestion
44 | dotnet_style_object_initializer = true:suggestion
45 | csharp_style_pattern_local_over_anonymous_function = false:suggestion
46 | dotnet_style_predefined_type_for_member_access = true:suggestion
47 | dotnet_style_prefer_inferred_anonymous_type_member_names = false:suggestion
48 | dotnet_style_prefer_inferred_tuple_names = true:suggestion
49 | dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
50 | dotnet_style_require_accessibility_modifiers = for_non_interface_members:suggestion
51 | dotnet_style_qualification_for_field = false:suggestion
52 | dotnet_style_qualification_for_method = false:suggestion
53 | dotnet_style_qualification_for_property = false:suggestion
54 |
55 |
56 | # Analyzer Configuration
57 | # These are rules we want to either ignore or have set as suggestion or info
58 |
59 | # CA1014: Mark assemblies with CLSCompliant
60 | # https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1014
61 | dotnet_diagnostic.CA1014.severity = none
62 |
63 | # CA1725: Parameter names should match base declaration
64 | # https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1725
65 | dotnet_diagnostic.CA1725.severity = suggestion
66 |
67 | # CA2227: Collection properties should be read only
68 | # https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca2227
69 | dotnet_diagnostic.CA2227.severity = suggestion
70 |
--------------------------------------------------------------------------------
/RockLib.Configuration.ProxyFactory/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # RockLib.Configuration.ProxyFactory Changelog
2 |
3 | All notable changes to this project will be documented in this file.
4 |
5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7 |
8 | ## 4.0.0 - 2025-01-29
9 |
10 | #### Changed
11 | - Final version of 4.0.0.
12 |
13 | ## 4.0.0-alpha.1 - 2025-01-29
14 |
15 | #### Changed
16 | - Removed .NET 6 as a target framework
17 | - Updated RockLib.Configuration.ObjectFactory to 4.0.0
18 |
19 | ## 3.0.0 - 2024-02-14
20 |
21 | #### Changed
22 | - Final version of 3.0.0.
23 |
24 | ## 3.0.0-alpha.1 - 2023-12-14
25 |
26 | #### Changed
27 | - Removed .NET Core 3.1 as a TFM and added .NET 8. Supported targets are now .NET 4.8, .NET 6, and .NET 8.
28 |
29 | ## 2.0.1 - 2023-02-14
30 |
31 | #### Added
32 | - Added update for RockLib.Configuration.ObjectFactory reference version `2.0.2`
33 |
34 | ## 2.0.0 - 2022-02-09
35 |
36 | #### Added
37 | - Added `.editorconfig` and `Directory.Build.props` files to ensure consistency.
38 |
39 | #### Changed
40 | - Supported targets: net6.0, netcoreapp3.1, and net48.
41 | - As the package now uses nullable reference types, some method parameters now specify if they can accept nullable values.
42 |
43 | ## 1.0.7 - 2021-08-11
44 |
45 | #### Changed
46 |
47 | - Changes "Quicken Loans" to "Rocket Mortgage".
48 | - Updates RockLib.Configuration.ObjectFactory to latest version, [1.6.9](https://github.com/RockLib/RockLib.Configuration/blob/main/RockLib.Configuration.ObjectFactory/CHANGELOG.md#169---2021-08-11).
49 |
50 | ## 1.0.6 - 2021-05-06
51 |
52 | #### Added
53 |
54 | - Adds SourceLink to nuget package.
55 |
56 | #### Changed
57 |
58 | - Updates RockLib.Configuration.ObjectFactory package to latest version, which includes SourceLink.
59 | - Updates System.Reflection.Emit and System.ValueTuple packages to latest versions.
60 |
61 | ----
62 |
63 | **Note:** Release notes in the above format are not available for earlier versions of
64 | RockLib.Configuration.ProxyFactory. What follows below are the original release notes.
65 |
66 | ----
67 |
68 | ## 1.0.5
69 |
70 | Adds net5.0 target.
71 |
72 | ## 1.0.4
73 |
74 | Adds icon to project and nuget package.
75 |
76 | ## 1.0.2
77 |
78 | Updates RockLib.Configuration.ObjectFactory package to latest version.
79 |
80 | ## 1.0.1
81 |
82 | Updates RockLib.Configuration.ObjectFactory package to latest version.
83 |
84 | ## 1.0.0
85 |
86 | Initial release.
87 |
--------------------------------------------------------------------------------
/RockLib.Configuration.ProxyFactory/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 | true
4 |
5 |
6 | all
7 | Rocket Mortgage
8 | latest
9 | enable
10 | net48;net8.0
11 | NU1603,NU1701
12 | true
13 |
14 |
15 |
16 | all
17 | runtime; build; native; contentfiles; analyzers; buildtransitive
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/RockLib.Configuration.ProxyFactory/Exceptions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using System.Reflection;
4 |
5 | namespace RockLib.Configuration.ProxyFactory
6 | {
7 | internal static class Exceptions
8 | {
9 | internal static ArgumentException CannotCreateProxyOfNonInterfaceType(Type type) =>
10 | new ArgumentException($"Cannot create proxy instance of non-interface type {type}.", nameof(type));
11 |
12 | internal static ArgumentException TargetInterfaceCannotHaveAnyMethods(Type type, MethodInfo m) =>
13 | new ArgumentException($"Cannot create proxy {type} implementation: target interface cannot contain any methods. `{m}`", nameof(type));
14 |
15 | internal static ArgumentException TargetInterfaceCannotHaveAnyEvents(Type type, EventInfo e) =>
16 | new ArgumentException($"Cannot create proxy {type} implementation: target interface cannot contain any events. `{e}`", nameof(type));
17 |
18 | internal static ArgumentException TargetInterfaceCannotHaveAnyIndexerProperties(Type type, PropertyInfo p) =>
19 | new ArgumentException($"Cannot create proxy {type} implementation: target interface cannot contain any indexer properties. `{p.PropertyType.Name} this[{string.Join(", ", p.GetIndexParameters().Select(i => i.ParameterType.Name))}] {{ {(p.CanRead ? "get; " : "")} {(p.CanWrite ? "set; " : "")}}}`", nameof(type));
20 |
21 | internal static ArgumentException TargetInterfaceCannotHaveAnyWriteOnlyProperties(Type type, PropertyInfo p) =>
22 | new ArgumentException($"Cannot create proxy {type} implementation: target interface cannot contain write-only methods. `{p} {{ set; }}`", nameof(type));
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/RockLib.Configuration.ProxyFactory/InternalsVisibleTo.cs:
--------------------------------------------------------------------------------
1 | #if DEBUG
2 | using System.Runtime.CompilerServices;
3 |
4 | [assembly: InternalsVisibleTo("RockLib.Configuration.ProxyFactory.Tests")]
5 | #endif
--------------------------------------------------------------------------------
/RockLib.Configuration.ProxyFactory/README.md:
--------------------------------------------------------------------------------
1 | # :warning: Deprecation Warning :warning:
2 |
3 | This library has been deprecated and will no longer receive updates.
4 |
5 | ---
6 |
7 | RockLib has been a cornerstone of our open source efforts here at Rocket Mortgage, and it's played a significant role in our journey to drive innovation and collaboration within our organization and the open source community. It's been amazing to witness the collective creativity and hard work that you all have poured into this project.
8 |
9 | However, as technology relentlessly evolves, so must we. The decision to deprecate this library is rooted in our commitment to staying at the cutting edge of technological advancements. While this chapter is ending, it opens the door to exciting new opportunities on the horizon.
10 |
11 | We want to express our heartfelt thanks to all the contributors and users who have been a part of this incredible journey. Your contributions, feedback, and enthusiasm have been invaluable, and we are genuinely grateful for your dedication. 🚀
12 |
13 | ---
14 |
15 | # RockLib.Configuration.ProxyFactory
16 |
17 | A factory that creates instances of property-only interfaces, defined at run-time, and populated with values defined in an instance of `IConfiguration`.
18 |
19 | > [!WARNING]
20 | > The 4.0.0 release of this library will be the final version with upgrades and changes. Bug fixes will continue to be released as needed. We strongly encourage developers to use standard .NET configuration libraries directly like `Microsoft.Extensions.Configuration` in place of `RockLib.Configuration`.
21 |
22 | ## Supported Targets
23 |
24 | This library supports the following targets:
25 | - .NET 6
26 | - .NET Core 3.1
27 | - .NET Framework 4.8
28 |
29 | ## Overview
30 |
31 | Let's say we have a class, `Foo`, that has various settings that it requires.
32 |
33 | ```csharp
34 | public class Foo
35 | {
36 | private readonly IFooSettings _settings;
37 | public Foo(IFooSettings settings) => _settings = settings;
38 | // TODO: Define methods that use the settings
39 | }
40 |
41 | public interface IFooSettings
42 | {
43 | bool IsBar { get; }
44 | int BazCount { get; }
45 | string QuxCapacitor { get; }
46 | }
47 | ```
48 |
49 | When unit testing our `Foo` class, it is easy to use any mocking framework to define each of the settings as needed for each test. But when our class is being used in production, we'll want these values to come from configuration. In order to do this, we could use the `Create` extension method from `RockLib.Configuration.ObjectFactory` or the `Get` extension method from `Microsoft.Extensions.Configuration.Binder`. But those extension methods require a concrete target, so we would need to first define an implementation class for the settings interface. That implementation class is low-value, copy/paste code that we would like to avoid having to write.
50 |
51 | The `CreateProxy` extension method fills the gap by creating a concrete implementation type at run-time - a "proxy" class. It then uses that proxy class as the target type for the `Create` extension method from `RockLib.Configuration.ObjectFactory` and returns the result of that call.
52 |
53 | ```csharp
54 | IConfiguration configuration; // TODO: get from somewhere
55 | IFooSettings settings = configuration.CreateProxy();
56 | Foo foo = new Foo(settings);
57 | ```
58 |
--------------------------------------------------------------------------------
/RockLib.Configuration.ProxyFactory/RockLib.Configuration.ProxyFactory.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | RockLib.Configuration.ProxyFactory
4 | 4.0.0
5 | Creates proxy objects from interfaces using IConfiguration and IConfigurationSection objects.
6 | bin\$(Configuration)\$(TargetFramework)\$(PackageId).xml
7 | false
8 | A changelog is available at https://github.com/RockLib/RockLib.Configuration/blob/main/RockLib.Configuration.ProxyFactory/CHANGELOG.md.
9 | https://github.com/RockLib/RockLib.Configuration/tree/master/RockLib.Configuration.ProxyFactory
10 | LICENSE.md
11 | icon.png
12 | Copyright 2025 (c) Rocket Mortgage. All rights reserved.
13 | Configuration Factory Proxy IConfiguration IConfigurationSection
14 | 4.0.0
15 | True
16 | True
17 | True
18 | Embedded
19 |
20 |
21 | true
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/RockLib.Configuration.ProxyFactory/RockLib.Configuration.ProxyFactory.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 15
4 | VisualStudioVersion = 15.0.27004.2009
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RockLib.Configuration.ProxyFactory", "RockLib.Configuration.ProxyFactory.csproj", "{668E5809-C181-4FFD-AF2B-29D342FD6909}"
7 | EndProject
8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RockLib.Configuration.ProxyFactory.Tests", "..\Tests\RockLib.Configuration.ProxyFactory.Tests\RockLib.Configuration.ProxyFactory.Tests.csproj", "{0AF2FE77-91AA-43C5-AC7E-7E3E31627283}"
9 | EndProject
10 | Global
11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
12 | Debug|Any CPU = Debug|Any CPU
13 | Release|Any CPU = Release|Any CPU
14 | EndGlobalSection
15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
16 | {668E5809-C181-4FFD-AF2B-29D342FD6909}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
17 | {668E5809-C181-4FFD-AF2B-29D342FD6909}.Debug|Any CPU.Build.0 = Debug|Any CPU
18 | {668E5809-C181-4FFD-AF2B-29D342FD6909}.Release|Any CPU.ActiveCfg = Release|Any CPU
19 | {668E5809-C181-4FFD-AF2B-29D342FD6909}.Release|Any CPU.Build.0 = Release|Any CPU
20 | {0AF2FE77-91AA-43C5-AC7E-7E3E31627283}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
21 | {0AF2FE77-91AA-43C5-AC7E-7E3E31627283}.Debug|Any CPU.Build.0 = Debug|Any CPU
22 | {0AF2FE77-91AA-43C5-AC7E-7E3E31627283}.Release|Any CPU.ActiveCfg = Release|Any CPU
23 | {0AF2FE77-91AA-43C5-AC7E-7E3E31627283}.Release|Any CPU.Build.0 = Release|Any CPU
24 | EndGlobalSection
25 | GlobalSection(SolutionProperties) = preSolution
26 | HideSolutionNode = FALSE
27 | EndGlobalSection
28 | GlobalSection(ExtensibilityGlobals) = postSolution
29 | SolutionGuid = {DF36CA15-3280-487B-AA81-A9F8C00A4CBB}
30 | EndGlobalSection
31 | EndGlobal
32 |
--------------------------------------------------------------------------------
/RockLib.Configuration/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | end_of_line = lf
5 |
6 | [*.cs]
7 | # Styling
8 | indent_style = space
9 | indent_size = 4
10 | csharp_indent_case_contents = true
11 | csharp_indent_switch_labels = true
12 | csharp_new_line_before_catch = true
13 | csharp_new_line_before_else = true
14 | csharp_new_line_before_finally = true
15 | csharp_new_line_before_members_in_anonymous_types = false
16 | csharp_new_line_before_members_in_object_initializers = false
17 | csharp_new_line_before_open_brace = methods, control_blocks, types, properties, lambdas, accessors, object_collection_array_initializers
18 | csharp_new_line_between_query_expression_clauses = true
19 | csharp_prefer_braces = false:suggestion
20 | csharp_prefer_simple_default_expression = true:suggestion
21 | csharp_preferred_modifier_order = public,private,internal,protected,static,readonly,async,override,sealed:suggestion
22 | csharp_preserve_single_line_blocks = true
23 | csharp_preserve_single_line_statements = true
24 | csharp_space_after_cast = false
25 | csharp_space_after_colon_in_inheritance_clause = true
26 | csharp_space_after_keywords_in_control_flow_statements = true
27 | csharp_space_before_colon_in_inheritance_clause = true
28 | csharp_space_between_method_call_empty_parameter_list_parentheses = false
29 | csharp_space_between_method_call_name_and_opening_parenthesis = false
30 | csharp_space_between_method_call_parameter_list_parentheses = false
31 | csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
32 | csharp_space_between_method_declaration_parameter_list_parentheses = false
33 | csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
34 | csharp_style_expression_bodied_accessors = true:suggestion
35 | csharp_style_expression_bodied_constructors = false:suggestion
36 | csharp_style_expression_bodied_methods = false:suggestion
37 | csharp_style_expression_bodied_properties = true:suggestion
38 | csharp_style_inlined_variable_declaration = true:suggestion
39 | csharp_style_var_elsewhere = true:suggestion
40 | csharp_style_var_for_built_in_types = true:suggestion
41 | csharp_style_var_when_type_is_apparent = true:suggestion
42 | dotnet_sort_system_directives_first = false
43 | dotnet_style_explicit_tuple_names = true:suggestion
44 | dotnet_style_object_initializer = true:suggestion
45 | csharp_style_pattern_local_over_anonymous_function = false:suggestion
46 | dotnet_style_predefined_type_for_member_access = true:suggestion
47 | dotnet_style_prefer_inferred_anonymous_type_member_names = false:suggestion
48 | dotnet_style_prefer_inferred_tuple_names = true:suggestion
49 | dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
50 | dotnet_style_require_accessibility_modifiers = for_non_interface_members:suggestion
51 | dotnet_style_qualification_for_field = false:suggestion
52 | dotnet_style_qualification_for_method = false:suggestion
53 | dotnet_style_qualification_for_property = false:suggestion
54 |
55 |
56 | # Analyzer Configuration
57 | # These are rules we want to either ignore or have set as suggestion or info
58 |
59 | # CA1014: Mark assemblies with CLSCompliant
60 | # https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1014
61 | dotnet_diagnostic.CA1014.severity = none
62 |
63 | # CA1725: Parameter names should match base declaration
64 | # https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1725
65 | dotnet_diagnostic.CA1725.severity = suggestion
66 |
67 | # CA2227: Collection properties should be read only
68 | # https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca2227
69 | dotnet_diagnostic.CA2227.severity = suggestion
70 |
--------------------------------------------------------------------------------
/RockLib.Configuration/AppSettings.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace RockLib.Configuration
4 | {
5 | ///
6 | /// Defines an indexer property that retrieves settings from the "AppSettings" section from
7 | /// .
8 | ///
9 | public sealed class AppSettings
10 | {
11 | private AppSettings() { }
12 |
13 | internal static AppSettings Instance { get; } = new AppSettings();
14 |
15 | ///
16 | /// Gets the setting associated with the parameter.
17 | ///
18 | /// The key of the setting to look up.
19 | /// The setting associated with the parameter.
20 | ///
21 | /// If the given key is not found in the "AppSettings" section of .
22 | ///
23 | public string this[string key] => Config.Root?[$"AppSettings:{key}"] ??
24 | throw new KeyNotFoundException($"Unable to locate {nameof(Config.AppSettings)} key '{key}' in {typeof(Config).FullName}.{nameof(Config.Root)}.");
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/RockLib.Configuration/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # RockLib.Configuration Changelog
2 |
3 | All notable changes to this project will be documented in this file.
4 |
5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7 |
8 | ## 5.0.0 - 2025-01-29
9 |
10 | #### Changed
11 | - Updated `CompositeConfigurationSection`, `MemoryConfigurationBuilderExtensions`, and `Config` members for nullability.
12 | - Updated Microsoft NuGet package references to latest 8.* versions.
13 |
14 | #### Removed
15 | - Removed support for .NET 6.
16 |
17 | ## 4.0.3 - 2024-07-12
18 |
19 | #### Added
20 | - Direct dependency on System.Text.Json to .NET 6 package to resolve vulnerability.
21 | - Updated multiple dependencies in the test projects.
22 |
23 | ## 4.0.2 - 2024-10-10
24 |
25 | #### Changed
26 | - Re-release to pull and build with the latest System.Text.Json to fix vulnerability.
27 |
28 | ## 4.0.1 - 2024-07-12
29 |
30 | #### Added
31 | - Direct dependency on System.Text.Json to .NET 8 package to resolve vulnerability.
32 |
33 | ## 4.0.1-alpha.1 - 2024-07-12
34 |
35 | #### Added
36 | - Direct dependency on System.Text.Json to .NET 8 package to resolve vulnerability.
37 |
38 | ## 4.0.0 - 2024-02-14
39 |
40 | #### Changed
41 | - Finalized 4.0.0 version.
42 |
43 | ## 4.0.0-alpha.1 - 2023-12-14
44 |
45 | #### Changed
46 | - Removed .NET Core 3.1 as a TFM and added .NET 8. Supported targets are now .NET 4.8, .NET 6, and .NET 8.
47 | - Removed RockLib.Immutable as a dependency, and added `Semimutable<>` and `SoftLock<>` internally.
48 |
49 | ## 3.1.0 - 2023-03-24
50 |
51 | #### Added
52 | - Added support for generic host projects to allow loading of correct environment appsettings files.
53 |
54 | ## 3.1.0-alpha.1 - 2023-03-21
55 |
56 | #### Added
57 | - Added support for generic host projects to allow loading of correct environment appsettings files.
58 |
59 | ## 3.0.0 - 2022-02-10
60 |
61 | #### Added
62 | - Added `.editorconfig` and `Directory.Build.props` files to ensure consistency.
63 |
64 | #### Changed
65 | - Supported targets: net6.0, netcoreapp3.1, and net48.
66 | - As the package now uses nullable reference types, some method parameters now specify if they can accept nullable values.
67 | - `Config.ResetRoot()` relies on an private method that no longer silently catches all exceptions.
68 |
69 | ## 2.5.3 - 2021-08-11
70 |
71 | #### Changed
72 |
73 | - Changes "Quicken Loans" to "Rocket Mortgage".
74 | - Updates RockLib.Immutable to latest version, [1.0.7](https://github.com/RockLib/RockLib.Immutable/blob/main/RockLib.Immutable/CHANGELOG.md#107---2021-08-10).
75 |
76 | ## 2.5.2 - 2021-05-06
77 |
78 | #### Added
79 |
80 | - Adds SourceLink to nuget package.
81 |
82 | #### Changed
83 |
84 | - Updates RockLib.Immutable package to latest version, which includes SourceLink.
85 |
86 | ----
87 |
88 | **Note:** Release notes in the above format are not available for earlier versions of
89 | RockLib.Configuration. What follows below are the original release notes.
90 |
91 | ----
92 |
93 | ## 2.5.1
94 |
95 | Adds net5.0 target.
96 |
97 | ## 2.5.0
98 |
99 | Adds SetBasePath method to Config static class, allowing users to opt-in to setting the base path of the configuration builder when using the default config root.
100 |
101 | ## 2.4.4
102 |
103 | Adds icon to project and nuget package.
104 |
105 | ## 2.4.3
106 |
107 | Update dependency package.
108 |
109 | ## 2.4.2
110 |
111 | Updates to align with Nuget conventions.
112 |
113 | ## 2.4.1
114 |
115 | Fixes the list section ordering bug.
116 |
117 | ## 2.4.0
118 |
119 | Adds extension methods to create a composite configuration section from a configuration.
120 |
121 | ## 2.3.1
122 |
123 | Fixes a case sensitivity bug in the configuration manager provider.
124 |
125 | ## 2.3.0
126 |
127 | - Adds a `reloadOnChange` flag to the `AddAppSettingsJson` (for all targets) and `AddConfigurationManager` (for .NET Framework targets) extension methods.
128 | - The default value of `Config.Root` uses both extension methods and sends a value of true for each.
129 |
130 | ## 2.2.1
131 |
132 | Fix support for web projects with Web.Config
133 |
134 | ## 2.2.0
135 |
136 | - Adds support for RockLib.Secrets without a hard dependency
137 | - Adds .SetConfigRoot extension for IConfigurationBuilder
138 |
139 | ## 2.1.0
140 |
141 | For .NET Framework applications/libraries, the `AddConfigurationManager` extension method adds any `RockLibConfigurationSection` sections declared in app.config/web.config to the configuration builder. The default value of Config.Root also includes these sections.
142 |
143 | ## 2.0.0
144 |
145 | Changes the type of Config.Root from IConfigurationRoot to IConfiguration.
146 |
147 | ## 1.0.0
148 |
149 | Initial release.
150 |
--------------------------------------------------------------------------------
/RockLib.Configuration/CompositeConfigurationExtensions.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.Configuration;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 |
6 | namespace RockLib.Configuration
7 | {
8 | ///
9 | /// Defines extension methods for obtaining composite configuration sections.
10 | /// That is, a configuration section composed of the settings contained with
11 | /// multiple sections at different keys within a single configuration root.
12 | ///
13 | public static class CompositeConfigurationExtensions
14 | {
15 | ///
16 | /// Gets a composite configuration sub-section with the specified keys.
17 | ///
18 | /// The source of the composite section.
19 | /// The keys of the configuration sections.
20 | /// The .
21 | public static IConfigurationSection GetCompositeSection(this IConfiguration configuration, IEnumerable keys)
22 | {
23 | #if NET6_0_OR_GREATER
24 | ArgumentNullException.ThrowIfNull(keys);
25 | #else
26 | if (keys is null) { throw new ArgumentNullException(nameof(keys)); }
27 | #endif
28 | return configuration.GetCompositeSection(keys.ToArray());
29 | }
30 |
31 | ///
32 | /// Gets a composite configuration sub-section with the specified keys.
33 | ///
34 | /// The source of the composite section.
35 | /// The keys of the configuration sections.
36 | /// The .
37 | public static IConfigurationSection GetCompositeSection(this IConfiguration configuration, params string[] keys)
38 | {
39 | #if NET6_0_OR_GREATER
40 | ArgumentNullException.ThrowIfNull(configuration);
41 | ArgumentNullException.ThrowIfNull(keys);
42 | #else
43 | if (configuration is null) { throw new ArgumentNullException(nameof(configuration)); }
44 |
45 | if (keys is null) { throw new ArgumentNullException(nameof(keys)); }
46 | #endif
47 |
48 | if (keys.Length == 0)
49 | {
50 | throw new ArgumentException("Must contain at least one key.", nameof(keys));
51 | }
52 |
53 | if (keys.Any(key => string.IsNullOrEmpty(key)))
54 | {
55 | throw new ArgumentException("Cannot contain null or empty keys.", nameof(keys));
56 | }
57 |
58 | return new CompositeConfigurationSection(keys.Select(key => configuration.GetSection(key)));
59 | }
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/RockLib.Configuration/CompositeConfigurationSection.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.Configuration;
2 | using Microsoft.Extensions.Primitives;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 |
7 | namespace RockLib.Configuration
8 | {
9 | internal sealed class CompositeConfigurationSection : IConfigurationSection
10 | {
11 | private readonly Lazy> _allSections;
12 | private readonly Lazy _primarySection;
13 | private readonly Lazy> _children;
14 |
15 | public CompositeConfigurationSection(IEnumerable sections)
16 | {
17 | _allSections = new Lazy>(() => sections.ToList());
18 |
19 | _primarySection = new Lazy(() => _allSections.Value.FirstOrDefault(section => section.Value is not null)
20 | ?? _allSections.Value.First());
21 |
22 | _children = new Lazy>(() =>
23 | _allSections.Value.SelectMany(section => section.GetChildren())
24 | .GroupBy(s => s.Key, StringComparer.OrdinalIgnoreCase)
25 | .Select(s => new CompositeConfigurationSection(s))
26 | .OrderBy(s => s.Key, CompositeSectionKeyComparer.Instance)
27 | .ToList());
28 | }
29 |
30 | public IConfigurationSection GetSection(string key) =>
31 | new CompositeConfigurationSection(_allSections.Value.Select(s => s.GetSection(key)));
32 |
33 | public IEnumerable GetChildren() => _children.Value;
34 |
35 | public IChangeToken GetReloadToken() => _primarySection.Value.GetReloadToken();
36 |
37 | public string? this[string key]
38 | {
39 | get => _allSections.Value.FirstOrDefault(s => s[key] is not null)?[key];
40 | set
41 | {
42 | foreach (var section in _allSections.Value)
43 | {
44 | if (section[key] is null)
45 | continue;
46 | section[key] = value;
47 | return;
48 | }
49 | _primarySection.Value[key] = value;
50 | }
51 | }
52 |
53 | public string Key => _primarySection.Value.Key;
54 |
55 | public string Path => _primarySection.Value.Path;
56 |
57 | public string? Value
58 | {
59 | #if NET8_0_OR_GREATER
60 | #pragma warning disable CS8603 // Possible null reference return.
61 | #endif
62 | get => _primarySection.Value.Value;
63 | #if NET8_0_OR_GREATER
64 | #pragma warning restore CS8603 // Possible null reference return.
65 | #endif
66 | #if NET8_0_OR_GREATER
67 | #pragma warning disable CS8767 // Nullability of reference types in type of parameter doesn't match implicitly implemented member (possibly because of nullability attributes).
68 | #endif
69 | set => _primarySection.Value.Value = value;
70 | #if NET8_0_OR_GREATER
71 | #pragma warning restore CS8767 // Nullability of reference types in type of parameter doesn't match implicitly implemented member (possibly because of nullability attributes).
72 | #endif
73 | }
74 |
75 | private sealed class CompositeSectionKeyComparer : IComparer
76 | {
77 | public static readonly IComparer Instance = new CompositeSectionKeyComparer();
78 |
79 | private CompositeSectionKeyComparer() { }
80 |
81 | int IComparer.Compare(string? x, string? y)
82 | {
83 | var xIsInt = uint.TryParse(x, out var xValue);
84 | var yIsInt = uint.TryParse(y, out var yValue);
85 |
86 | // Do not change order when neither key is numeric.
87 | if (!xIsInt && !yIsInt)
88 | {
89 | return 0;
90 | }
91 |
92 | // Put numeric keys in ascending order.
93 | if (xIsInt && yIsInt)
94 | {
95 | return xValue.CompareTo(yValue);
96 | }
97 |
98 | // Put numeric keys after non-numeric keys.
99 | if (xIsInt)
100 | {
101 | return 1;
102 | }
103 |
104 | return -1;
105 | }
106 | }
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/RockLib.Configuration/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 | true
4 |
5 |
6 | all
7 | Rocket Mortgage
8 | latest
9 | enable
10 | net48;net8.0
11 | NU1603,NU1701
12 | true
13 |
14 |
15 |
16 | all
17 | runtime; build; native; contentfiles; analyzers; buildtransitive
18 |
19 |
20 |
--------------------------------------------------------------------------------
/RockLib.Configuration/RockLib.Configuration.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | Copyright 2017-2025 (c) Rocket Mortgage. All rights reserved.
4 | Embedded
5 | Provides a central location for an instance of IConfigurationRoot to be used as the "default" configuration by .NET libraries and applications. Replaces some of the functionality of the .NET Framework System.Configuration.ConfigurationManager class.
6 | True
7 | True
8 | RockLib.Configuration
9 | LICENSE.md
10 | false
11 | A changelog is available at https://github.com/RockLib/RockLib.Configuration/blob/main/RockLib.Configuration/CHANGELOG.md.
12 | https://github.com/RockLib/RockLib.Configuration
13 | 5.0.0
14 | icon.png
15 | Configuration ConfigurationRoot IConfigurationRoot ConfigurationManager AppSettings
16 | True
17 | 5.0.0
18 |
19 |
20 | bin\$(Configuration)\$(TargetFramework)\$(PackageId).xml
21 |
22 |
23 | true
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/RockLib.Configuration/RockLib.Configuration.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 16
4 | VisualStudioVersion = 16.0.31205.134
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RockLib.Configuration", "RockLib.Configuration.csproj", "{33869C6F-C539-4FC7-89A6-114BFA3B05D8}"
7 | EndProject
8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RockLib.Configuration.CustomConfigTests", "..\Tests\RockLib.Configuration.CustomConfigTests\RockLib.Configuration.CustomConfigTests.csproj", "{39DDD7E6-739F-4631-AF64-7A7EB1D55A1A}"
9 | EndProject
10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RockLib.Configuration.AppSettingsConfigTests", "..\Tests\RockLib.Configuration.AppSettingsConfigTests\RockLib.Configuration.AppSettingsConfigTests.csproj", "{B256C4B7-00A4-4FB5-9820-96DDF5F16E30}"
11 | EndProject
12 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RockLib.Configuration.Tests", "..\Tests\RockLib.Configuration.Tests\RockLib.Configuration.Tests.csproj", "{1FC97D1E-0A4B-446D-A605-18A5BC3ED207}"
13 | EndProject
14 | Global
15 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
16 | Debug|Any CPU = Debug|Any CPU
17 | Release|Any CPU = Release|Any CPU
18 | EndGlobalSection
19 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
20 | {33869C6F-C539-4FC7-89A6-114BFA3B05D8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
21 | {33869C6F-C539-4FC7-89A6-114BFA3B05D8}.Debug|Any CPU.Build.0 = Debug|Any CPU
22 | {33869C6F-C539-4FC7-89A6-114BFA3B05D8}.Release|Any CPU.ActiveCfg = Release|Any CPU
23 | {33869C6F-C539-4FC7-89A6-114BFA3B05D8}.Release|Any CPU.Build.0 = Release|Any CPU
24 | {39DDD7E6-739F-4631-AF64-7A7EB1D55A1A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
25 | {39DDD7E6-739F-4631-AF64-7A7EB1D55A1A}.Debug|Any CPU.Build.0 = Debug|Any CPU
26 | {39DDD7E6-739F-4631-AF64-7A7EB1D55A1A}.Release|Any CPU.ActiveCfg = Release|Any CPU
27 | {39DDD7E6-739F-4631-AF64-7A7EB1D55A1A}.Release|Any CPU.Build.0 = Release|Any CPU
28 | {B256C4B7-00A4-4FB5-9820-96DDF5F16E30}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
29 | {B256C4B7-00A4-4FB5-9820-96DDF5F16E30}.Debug|Any CPU.Build.0 = Debug|Any CPU
30 | {B256C4B7-00A4-4FB5-9820-96DDF5F16E30}.Release|Any CPU.ActiveCfg = Release|Any CPU
31 | {B256C4B7-00A4-4FB5-9820-96DDF5F16E30}.Release|Any CPU.Build.0 = Release|Any CPU
32 | {1FC97D1E-0A4B-446D-A605-18A5BC3ED207}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
33 | {1FC97D1E-0A4B-446D-A605-18A5BC3ED207}.Debug|Any CPU.Build.0 = Debug|Any CPU
34 | {1FC97D1E-0A4B-446D-A605-18A5BC3ED207}.Release|Any CPU.ActiveCfg = Release|Any CPU
35 | {1FC97D1E-0A4B-446D-A605-18A5BC3ED207}.Release|Any CPU.Build.0 = Release|Any CPU
36 | EndGlobalSection
37 | GlobalSection(SolutionProperties) = preSolution
38 | HideSolutionNode = FALSE
39 | EndGlobalSection
40 | GlobalSection(ExtensibilityGlobals) = postSolution
41 | SolutionGuid = {DF36CA15-3280-487B-AA81-A9F8C00A4CBB}
42 | EndGlobalSection
43 | EndGlobal
44 |
--------------------------------------------------------------------------------
/RockLib.Configuration/RockLibConfigurationBuilderExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Microsoft.Extensions.Configuration;
3 |
4 | namespace RockLib.Configuration
5 | {
6 | ///
7 | /// Extension methods for adding configuration providers to an instance of .
8 | ///
9 | public static class RockLibConfigurationBuilderExtensions
10 | {
11 | ///
12 | /// The default value for whether a configuration should be reloaded when its source changes.
13 | ///
14 | public const bool DefaultReloadOnChange = false;
15 |
16 | ///
17 | /// Sets the value of the property by building the specified
18 | /// .
19 | ///
20 | ///
21 | /// The that will be the source of
22 | /// the property.
23 | ///
24 | /// The
25 | public static IConfigurationBuilder SetConfigRoot(this IConfigurationBuilder builder)
26 | {
27 | Config.SetRoot(() => builder.Build());
28 | return builder;
29 | }
30 |
31 | ///
32 | /// Adds the ASP.NET Core appsettings.json configuration provider to the builder using the configuration file "appsettings.json",
33 | /// relative to the base path stored in of the builder.
34 | ///
35 | /// The to add to.
36 | /// If is null.
37 | /// The .
38 | public static IConfigurationBuilder AddAppSettingsJson(this IConfigurationBuilder builder) =>
39 | builder.AddAppSettingsJson(DefaultReloadOnChange);
40 |
41 | ///
42 | /// Adds the ASP.NET Core appsettings.json configuration provider to the builder using the configuration file "appsettings.json",
43 | /// relative to the base path stored in of the builder.
44 | ///
45 | /// The to add to.
46 | /// Whether the configuration should be reloaded if the appsettings.json file changes.
47 | /// If is null.
48 | /// The .
49 | public static IConfigurationBuilder AddAppSettingsJson(this IConfigurationBuilder builder, bool reloadOnChange)
50 | {
51 | #if NET6_0_OR_GREATER
52 | ArgumentNullException.ThrowIfNull(builder);
53 | #else
54 | if (builder is null) { throw new ArgumentNullException(nameof(builder)); }
55 | #endif
56 |
57 | // we want the optional value to be true so that it will not throw a runtime exception if the file is not found
58 | builder = builder.AddJsonFile("appsettings.json", optional: true, reloadOnChange: reloadOnChange);
59 |
60 | var environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
61 |
62 | if (string.IsNullOrEmpty(environment))
63 | environment = Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT");
64 |
65 | if (string.IsNullOrEmpty(environment))
66 | environment = Environment.GetEnvironmentVariable("ROCKLIB_ENVIRONMENT");
67 |
68 | if (!string.IsNullOrEmpty(environment))
69 | builder = builder.AddJsonFile($"appsettings.{environment}.json", optional: true, reloadOnChange: reloadOnChange);
70 |
71 | return builder;
72 | }
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/RockLib.Configuration/SoftLock.cs:
--------------------------------------------------------------------------------
1 | using System.Threading;
2 |
3 | namespace RockLib.Configuration;
4 |
5 | ///
6 | /// This is a copy of
7 | /// https://raw.githubusercontent.com/RockLib/RockLib.Threading/main/RockLib.Threading/SoftLock.cs.
8 | /// There are no updates to this code, so it was decided to inline the implementation into RockLib.Configuration
9 | ///
10 | internal sealed class SoftLock
11 | {
12 | private const int _lockNotAcquired = 0;
13 | private const int _lockAcquired = 1;
14 |
15 | private int _lock;
16 |
17 | ///
18 | /// Try to acquire the lock. Returns true if the lock is acquired. Returns false if the lock has
19 | /// already been acquired.
20 | ///
21 | /// True, if the lock was acquired. False, if another thread currently has the lock
22 | public bool TryAcquire()
23 | {
24 | return Interlocked.Exchange(ref _lock, _lockAcquired) == _lockNotAcquired;
25 | }
26 |
27 | ///
28 | /// Release the lock. Should only be called after successfully acquiring the lock.
29 | ///
30 | public void Release()
31 | {
32 | Interlocked.Exchange(ref _lock, _lockNotAcquired);
33 | }
34 |
35 | ///
36 | /// Gets a value indicating whether the lock has been acquired.
37 | ///
38 | public bool IsLockAcquired => _lock == _lockAcquired;
39 | }
--------------------------------------------------------------------------------
/Tests/RockLib.Configuration.AppSettingsConfigTests/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | end_of_line = lf
5 |
6 | [*.cs]
7 | # Styling
8 | indent_style = space
9 | indent_size = 4
10 | csharp_indent_case_contents = true
11 | csharp_indent_switch_labels = true
12 | csharp_new_line_before_catch = true
13 | csharp_new_line_before_else = true
14 | csharp_new_line_before_finally = true
15 | csharp_new_line_before_members_in_anonymous_types = false
16 | csharp_new_line_before_members_in_object_initializers = false
17 | csharp_new_line_before_open_brace = methods, control_blocks, types, properties, lambdas, accessors, object_collection_array_initializers
18 | csharp_new_line_between_query_expression_clauses = true
19 | csharp_prefer_braces = false:suggestion
20 | csharp_prefer_simple_default_expression = true:suggestion
21 | csharp_preferred_modifier_order = public,private,internal,protected,static,readonly,async,override,sealed:suggestion
22 | csharp_preserve_single_line_blocks = true
23 | csharp_preserve_single_line_statements = true
24 | csharp_space_after_cast = false
25 | csharp_space_after_colon_in_inheritance_clause = true
26 | csharp_space_after_keywords_in_control_flow_statements = true
27 | csharp_space_before_colon_in_inheritance_clause = true
28 | csharp_space_between_method_call_empty_parameter_list_parentheses = false
29 | csharp_space_between_method_call_name_and_opening_parenthesis = false
30 | csharp_space_between_method_call_parameter_list_parentheses = false
31 | csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
32 | csharp_space_between_method_declaration_parameter_list_parentheses = false
33 | csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
34 | csharp_style_expression_bodied_accessors = true:suggestion
35 | csharp_style_expression_bodied_constructors = false:suggestion
36 | csharp_style_expression_bodied_methods = false:suggestion
37 | csharp_style_expression_bodied_properties = true:suggestion
38 | csharp_style_inlined_variable_declaration = true:suggestion
39 | csharp_style_var_elsewhere = true:suggestion
40 | csharp_style_var_for_built_in_types = true:suggestion
41 | csharp_style_var_when_type_is_apparent = true:suggestion
42 | dotnet_sort_system_directives_first = false
43 | dotnet_style_explicit_tuple_names = true:suggestion
44 | dotnet_style_object_initializer = true:suggestion
45 | csharp_style_pattern_local_over_anonymous_function = false:suggestion
46 | dotnet_style_predefined_type_for_member_access = true:suggestion
47 | dotnet_style_prefer_inferred_anonymous_type_member_names = false:suggestion
48 | dotnet_style_prefer_inferred_tuple_names = true:suggestion
49 | dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
50 | dotnet_style_require_accessibility_modifiers = for_non_interface_members:suggestion
51 | dotnet_style_qualification_for_field = false:suggestion
52 | dotnet_style_qualification_for_method = false:suggestion
53 | dotnet_style_qualification_for_property = false:suggestion
54 |
55 |
56 | # Analyzer Configuration
57 | # These are rules we want to either ignore or have set as suggestion or info
58 |
59 | # CA1014: Mark assemblies with CLSCompliant
60 | # https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1014
61 | dotnet_diagnostic.CA1014.severity = none
62 |
63 | # CA1725: Parameter names should match base declaration
64 | # https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1725
65 | dotnet_diagnostic.CA1725.severity = suggestion
66 |
67 | # CA2227: Collection properties should be read only
68 | # https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca2227
69 | dotnet_diagnostic.CA2227.severity = suggestion
70 |
--------------------------------------------------------------------------------
/Tests/RockLib.Configuration.AppSettingsConfigTests/AssemblySettings.cs:
--------------------------------------------------------------------------------
1 | using Xunit;
2 |
3 | [assembly: CollectionBehavior(CollectionBehavior.CollectionPerAssembly)]
--------------------------------------------------------------------------------
/Tests/RockLib.Configuration.AppSettingsConfigTests/ConfigTests.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.Primitives;
2 | using Newtonsoft.Json;
3 | using Newtonsoft.Json.Linq;
4 | using System;
5 | using System.IO;
6 | using System.Threading;
7 | using Xunit;
8 |
9 | namespace RockLib.Configuration.AppSettingsConfigTests
10 | {
11 | public class ConfigTests
12 | {
13 | public ConfigTests()
14 | {
15 | // Set the environment variable before reading configs.
16 | Environment.SetEnvironmentVariable("ASPNETCORE_ENVIRONMENT", "Development");
17 | Environment.SetEnvironmentVariable("ROCKLIB_ENVIRONMENT", "Staging");
18 | Environment.SetEnvironmentVariable("AppSettings:RockLib.Environment", "Prod");
19 | }
20 |
21 | [Fact(DisplayName = "AppSettingsConfigTests: IsDefault is true by default.")]
22 | public void IsDefaultIsTrueByDefault()
23 | {
24 | Assert.True(Config.IsDefault);
25 | }
26 |
27 | [Fact(DisplayName = "AppSettingsConfigTests: 'appsettings.json' is used by default.")]
28 | public void FileConfigIsUsedByDefault()
29 | {
30 | Assert.Equal("201740", Config.AppSettings["ApplicationId"]);
31 | }
32 |
33 | [Fact(DisplayName = "AppSettingsConfigTests: Environment variables override values from 'appsettings.json'.")]
34 | public void EnvironmentVariablesOverrideFileConfig()
35 | {
36 | // Note that the "AppSettings:RockLib.Environment" environment variable was
37 | // set in the static constructor with a value of "Prod".
38 | Assert.Equal("Prod", Config.AppSettings["RockLib.Environment"]);
39 | }
40 |
41 | [Fact(DisplayName = "AppSettingsConfigTests: IsLocked is true after the ConfigurationRoot property has been accessed.")]
42 | public void IsLockedIsTrueAfterConfigurationRootHasBeenAccessed()
43 | {
44 | // Accessing ConfigurationRoot causes IsLocked to be true.
45 | var root = Config.Root;
46 |
47 | Assert.True(Config.IsLocked);
48 | }
49 |
50 | [Fact(DisplayName = "AppSettingsConfigTests: Changing environment variable values when IsLocked is true has no effect.")]
51 | public void EnvironmentVariablesDoNotOverrideAfterLocked()
52 | {
53 | // Accessing ConfigurationRoot causes IsLocked to be true.
54 | var root = Config.Root;
55 |
56 | // This is the same environment variable that we successfully changed in the static constructor.
57 | // Setting it this time, however, is too late.
58 | Environment.SetEnvironmentVariable("AppSettings:RockLib.Environment", "Beta");
59 |
60 | // No effect.
61 | Assert.Equal("Prod", Config.AppSettings["RockLib.Environment"]);
62 | }
63 |
64 | [Fact(DisplayName = "AppSettingsConfigTests: appsettings.development.json is the loaded as FileName due to priority")]
65 | public void AppSettingsDevelopmentJsonIsLoaded()
66 | {
67 | Assert.Equal("appsettings.development.json", Config.AppSettings["FileName"]);
68 | }
69 |
70 | [Fact]
71 | public void ReloadTest()
72 | {
73 | try
74 | {
75 | var section = Config.Root!.GetSection("element_to_reload");
76 |
77 | Assert.Equal("123", section.Value);
78 |
79 | using var waitHandle = new AutoResetEvent(false);
80 |
81 | ChangeToken.OnChange(section.GetReloadToken, () => waitHandle.Set());
82 |
83 | WriteConfig(456);
84 |
85 | waitHandle.WaitOne();
86 |
87 | Assert.Equal("456", section.Value);
88 | }
89 | finally
90 | {
91 | WriteConfig(123);
92 | }
93 | }
94 |
95 | private static void WriteConfig(int value)
96 | {
97 | var json = JObject.Parse(File.ReadAllText("appsettings.json"));
98 | ((JValue)json["element_to_reload"]!).Value = value;
99 | File.WriteAllText("appsettings.json", json.ToString(Formatting.Indented));
100 | }
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/Tests/RockLib.Configuration.AppSettingsConfigTests/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 | true
4 |
5 |
6 | all
7 | Rocket Mortgage
8 | latest
9 | enable
10 | net48;net8.0
11 | NU1603,NU1701
12 | true
13 |
14 |
15 |
16 | all
17 | runtime; build; native; contentfiles; analyzers; buildtransitive
18 |
19 |
20 |
--------------------------------------------------------------------------------
/Tests/RockLib.Configuration.AppSettingsConfigTests/RockLib.Configuration.AppSettingsConfigTests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | runtime; build; native; contentfiles; analyzers; buildtransitive
8 | all
9 |
10 |
11 | runtime; build; native; contentfiles; analyzers; buildtransitive
12 | all
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | Always
22 |
23 |
24 | Always
25 |
26 |
27 | Always
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/Tests/RockLib.Configuration.AppSettingsConfigTests/appsettings.development.json:
--------------------------------------------------------------------------------
1 | {
2 | "AppSettings": {
3 | "RockLib.Environment": "Dev",
4 | "FileName": "appsettings.development.json"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Tests/RockLib.Configuration.AppSettingsConfigTests/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "AppSettings": {
3 | "ApplicationId": 201740,
4 | "RockLib.Environment": "Test",
5 | "FileName": "appsettings.json"
6 | },
7 |
8 | "element_to_reload": 123
9 | }
10 |
--------------------------------------------------------------------------------
/Tests/RockLib.Configuration.AppSettingsConfigTests/appsettings.staging.json:
--------------------------------------------------------------------------------
1 | {
2 | "AppSettings": {
3 | "RockLib.Environment": "Beta",
4 | "FileName": "appsettings.staging.json"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Tests/RockLib.Configuration.AspNetCore.Tests/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | end_of_line = lf
5 |
6 | [*.cs]
7 | # Styling
8 | indent_style = space
9 | indent_size = 4
10 | csharp_indent_case_contents = true
11 | csharp_indent_switch_labels = true
12 | csharp_new_line_before_catch = true
13 | csharp_new_line_before_else = true
14 | csharp_new_line_before_finally = true
15 | csharp_new_line_before_members_in_anonymous_types = false
16 | csharp_new_line_before_members_in_object_initializers = false
17 | csharp_new_line_before_open_brace = methods, control_blocks, types, properties, lambdas, accessors, object_collection_array_initializers
18 | csharp_new_line_between_query_expression_clauses = true
19 | csharp_prefer_braces = false:suggestion
20 | csharp_prefer_simple_default_expression = true:suggestion
21 | csharp_preferred_modifier_order = public,private,internal,protected,static,readonly,async,override,sealed:suggestion
22 | csharp_preserve_single_line_blocks = true
23 | csharp_preserve_single_line_statements = true
24 | csharp_space_after_cast = false
25 | csharp_space_after_colon_in_inheritance_clause = true
26 | csharp_space_after_keywords_in_control_flow_statements = true
27 | csharp_space_before_colon_in_inheritance_clause = true
28 | csharp_space_between_method_call_empty_parameter_list_parentheses = false
29 | csharp_space_between_method_call_name_and_opening_parenthesis = false
30 | csharp_space_between_method_call_parameter_list_parentheses = false
31 | csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
32 | csharp_space_between_method_declaration_parameter_list_parentheses = false
33 | csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
34 | csharp_style_expression_bodied_accessors = true:suggestion
35 | csharp_style_expression_bodied_constructors = false:suggestion
36 | csharp_style_expression_bodied_methods = false:suggestion
37 | csharp_style_expression_bodied_properties = true:suggestion
38 | csharp_style_inlined_variable_declaration = true:suggestion
39 | csharp_style_var_elsewhere = true:suggestion
40 | csharp_style_var_for_built_in_types = true:suggestion
41 | csharp_style_var_when_type_is_apparent = true:suggestion
42 | dotnet_sort_system_directives_first = false
43 | dotnet_style_explicit_tuple_names = true:suggestion
44 | dotnet_style_object_initializer = true:suggestion
45 | csharp_style_pattern_local_over_anonymous_function = false:suggestion
46 | dotnet_style_predefined_type_for_member_access = true:suggestion
47 | dotnet_style_prefer_inferred_anonymous_type_member_names = false:suggestion
48 | dotnet_style_prefer_inferred_tuple_names = true:suggestion
49 | dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
50 | dotnet_style_require_accessibility_modifiers = for_non_interface_members:suggestion
51 | dotnet_style_qualification_for_field = false:suggestion
52 | dotnet_style_qualification_for_method = false:suggestion
53 | dotnet_style_qualification_for_property = false:suggestion
54 |
55 |
56 | # Analyzer Configuration
57 | # These are rules we want to either ignore or have set as suggestion or info
58 |
59 | # CA1014: Mark assemblies with CLSCompliant
60 | # https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1014
61 | dotnet_diagnostic.CA1014.severity = none
62 |
63 | # CA1725: Parameter names should match base declaration
64 | # https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1725
65 | dotnet_diagnostic.CA1725.severity = suggestion
66 |
67 | # CA2227: Collection properties should be read only
68 | # https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca2227
69 | dotnet_diagnostic.CA2227.severity = suggestion
70 |
--------------------------------------------------------------------------------
/Tests/RockLib.Configuration.AspNetCore.Tests/AspNetExtensionsTests.cs:
--------------------------------------------------------------------------------
1 | using FluentAssertions;
2 | using Microsoft.AspNetCore.Hosting;
3 | using Microsoft.Extensions.Configuration;
4 | using Microsoft.Extensions.DependencyInjection;
5 | using System;
6 | using Xunit;
7 |
8 | namespace RockLib.Configuration.AspNetCore.Tests;
9 |
10 | public class AspNetExtensionsTests
11 | {
12 | [Fact]
13 | public void SetConfigRootSetsConfigRoot()
14 | {
15 | var configRoot = new ConfigurationBuilder().Build();
16 |
17 | var builder = new TestWebHostBuilder(configRoot);
18 |
19 | builder.SetConfigRoot();
20 |
21 | Config.Root.Should().BeSameAs(configRoot);
22 | }
23 |
24 | private sealed class TestWebHostBuilder : IWebHostBuilder
25 | {
26 | private readonly IConfiguration _configRoot;
27 |
28 | public TestWebHostBuilder(IConfiguration configRoot)
29 | {
30 | _configRoot = configRoot;
31 | }
32 |
33 | public IWebHostBuilder ConfigureServices(Action configureServices)
34 | {
35 | var context = new WebHostBuilderContext { Configuration = _configRoot };
36 | configureServices(context, null);
37 | return this;
38 | }
39 |
40 | public IWebHost Build() => throw new NotImplementedException();
41 | public IWebHostBuilder ConfigureAppConfiguration(Action configureDelegate) => throw new NotImplementedException();
42 | public IWebHostBuilder ConfigureServices(Action configureServices) => throw new NotImplementedException();
43 | public string GetSetting(string key) => throw new NotImplementedException();
44 | public IWebHostBuilder UseSetting(string key, string? value) => throw new NotImplementedException();
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/Tests/RockLib.Configuration.AspNetCore.Tests/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 | true
4 |
5 |
6 | all
7 | Rocket Mortgage
8 | Copyright 2025 (c) Rocket Mortgage. All rights reserved.
9 | latest
10 | enable
11 | net48;net8.0
12 | NU1603,NU1701
13 | true
14 |
15 |
16 |
17 | all
18 | runtime; build; native; contentfiles; analyzers; buildtransitive
19 |
20 |
21 |
--------------------------------------------------------------------------------
/Tests/RockLib.Configuration.AspNetCore.Tests/RockLib.Configuration.AspNetCore.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | false
4 |
5 |
6 |
7 |
8 |
9 |
10 | runtime; build; native; contentfiles; analyzers; buildtransitive
11 | all
12 |
13 |
14 | runtime; build; native; contentfiles; analyzers; buildtransitive
15 | all
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/Tests/RockLib.Configuration.CustomConfigTests/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | end_of_line = lf
5 |
6 | [*.cs]
7 | # Styling
8 | indent_style = space
9 | indent_size = 4
10 | csharp_indent_case_contents = true
11 | csharp_indent_switch_labels = true
12 | csharp_new_line_before_catch = true
13 | csharp_new_line_before_else = true
14 | csharp_new_line_before_finally = true
15 | csharp_new_line_before_members_in_anonymous_types = false
16 | csharp_new_line_before_members_in_object_initializers = false
17 | csharp_new_line_before_open_brace = methods, control_blocks, types, properties, lambdas, accessors, object_collection_array_initializers
18 | csharp_new_line_between_query_expression_clauses = true
19 | csharp_prefer_braces = false:suggestion
20 | csharp_prefer_simple_default_expression = true:suggestion
21 | csharp_preferred_modifier_order = public,private,internal,protected,static,readonly,async,override,sealed:suggestion
22 | csharp_preserve_single_line_blocks = true
23 | csharp_preserve_single_line_statements = true
24 | csharp_space_after_cast = false
25 | csharp_space_after_colon_in_inheritance_clause = true
26 | csharp_space_after_keywords_in_control_flow_statements = true
27 | csharp_space_before_colon_in_inheritance_clause = true
28 | csharp_space_between_method_call_empty_parameter_list_parentheses = false
29 | csharp_space_between_method_call_name_and_opening_parenthesis = false
30 | csharp_space_between_method_call_parameter_list_parentheses = false
31 | csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
32 | csharp_space_between_method_declaration_parameter_list_parentheses = false
33 | csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
34 | csharp_style_expression_bodied_accessors = true:suggestion
35 | csharp_style_expression_bodied_constructors = false:suggestion
36 | csharp_style_expression_bodied_methods = false:suggestion
37 | csharp_style_expression_bodied_properties = true:suggestion
38 | csharp_style_inlined_variable_declaration = true:suggestion
39 | csharp_style_var_elsewhere = true:suggestion
40 | csharp_style_var_for_built_in_types = true:suggestion
41 | csharp_style_var_when_type_is_apparent = true:suggestion
42 | dotnet_sort_system_directives_first = false
43 | dotnet_style_explicit_tuple_names = true:suggestion
44 | dotnet_style_object_initializer = true:suggestion
45 | csharp_style_pattern_local_over_anonymous_function = false:suggestion
46 | dotnet_style_predefined_type_for_member_access = true:suggestion
47 | dotnet_style_prefer_inferred_anonymous_type_member_names = false:suggestion
48 | dotnet_style_prefer_inferred_tuple_names = true:suggestion
49 | dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
50 | dotnet_style_require_accessibility_modifiers = for_non_interface_members:suggestion
51 | dotnet_style_qualification_for_field = false:suggestion
52 | dotnet_style_qualification_for_method = false:suggestion
53 | dotnet_style_qualification_for_property = false:suggestion
54 |
55 |
56 | # Analyzer Configuration
57 | # These are rules we want to either ignore or have set as suggestion or info
58 |
59 | # CA1014: Mark assemblies with CLSCompliant
60 | # https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1014
61 | dotnet_diagnostic.CA1014.severity = none
62 |
63 | # CA1725: Parameter names should match base declaration
64 | # https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1725
65 | dotnet_diagnostic.CA1725.severity = suggestion
66 |
67 | # CA2227: Collection properties should be read only
68 | # https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca2227
69 | dotnet_diagnostic.CA2227.severity = suggestion
70 |
--------------------------------------------------------------------------------
/Tests/RockLib.Configuration.CustomConfigTests/AssemblySettings.cs:
--------------------------------------------------------------------------------
1 | using Xunit;
2 |
3 | [assembly: CollectionBehavior(CollectionBehavior.CollectionPerAssembly)]
--------------------------------------------------------------------------------
/Tests/RockLib.Configuration.CustomConfigTests/ConfigTests.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.Configuration;
2 | using System.Collections.Generic;
3 | using Xunit;
4 |
5 | #if NET8_0_OR_GREATER
6 | #pragma warning disable CS8620 // Argument cannot be used for parameter due to differences in the nullability of reference types.
7 | #endif
8 | namespace RockLib.Configuration.CustomConfigurationManagerTests;
9 |
10 | public class ConfigTests
11 | {
12 | private readonly IConfiguration _configuration;
13 |
14 | public ConfigTests()
15 | {
16 | _configuration = new ConfigurationBuilder()
17 | .AddInMemoryCollection(
18 | new Dictionary
19 | {
20 | { "AppSettings:Environment", "Test" },
21 | { "AppSettings:ApplicationId", "200001" }
22 | })
23 | .Build();
24 |
25 | Config.SetRoot(_configuration);
26 | }
27 |
28 | [Fact(DisplayName = "CustomConfigurationManagerTests: IsDefault is false when SetConfigurationRoot has been called.")]
29 | public void IsDefaultIsFalseWhenCustomized()
30 | {
31 | Assert.False(Config.IsDefault);
32 | }
33 |
34 | [Fact(DisplayName = "CustomConfigurationManagerTests: The ConfigurationRoot property is the same instance passed to SetConfigurationRoot.")]
35 | public void TheCustomConfigurationRootIsUsed()
36 | {
37 | Assert.Same(_configuration, Config.Root);
38 | Assert.Equal("Test", Config.AppSettings["Environment"]);
39 | Assert.Equal("200001", Config.AppSettings["ApplicationId"]);
40 | }
41 | }
42 | #if NET8_0_OR_GREATER
43 | #pragma warning restore CS8620 // Argument cannot be used for parameter due to differences in the nullability of reference types.
44 | #endif
--------------------------------------------------------------------------------
/Tests/RockLib.Configuration.CustomConfigTests/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 | true
4 |
5 |
6 | all
7 | Rocket Mortgage
8 | latest
9 | enable
10 | net48;net8.0
11 | NU1603,NU1701
12 | true
13 |
14 |
15 |
16 | all
17 | runtime; build; native; contentfiles; analyzers; buildtransitive
18 |
19 |
20 |
--------------------------------------------------------------------------------
/Tests/RockLib.Configuration.CustomConfigTests/RockLib.Configuration.CustomConfigTests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | runtime; build; native; contentfiles; analyzers; buildtransitive
8 | all
9 |
10 |
11 | runtime; build; native; contentfiles; analyzers; buildtransitive
12 | all
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/Tests/RockLib.Configuration.MessagingProvider.Tests/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | end_of_line = lf
5 |
6 | [*.cs]
7 | # Styling
8 | indent_style = space
9 | indent_size = 4
10 | csharp_indent_case_contents = true
11 | csharp_indent_switch_labels = true
12 | csharp_new_line_before_catch = true
13 | csharp_new_line_before_else = true
14 | csharp_new_line_before_finally = true
15 | csharp_new_line_before_members_in_anonymous_types = false
16 | csharp_new_line_before_members_in_object_initializers = false
17 | csharp_new_line_before_open_brace = methods, control_blocks, types, properties, lambdas, accessors, object_collection_array_initializers
18 | csharp_new_line_between_query_expression_clauses = true
19 | csharp_prefer_braces = false:suggestion
20 | csharp_prefer_simple_default_expression = true:suggestion
21 | csharp_preferred_modifier_order = public,private,internal,protected,static,readonly,async,override,sealed:suggestion
22 | csharp_preserve_single_line_blocks = true
23 | csharp_preserve_single_line_statements = true
24 | csharp_space_after_cast = false
25 | csharp_space_after_colon_in_inheritance_clause = true
26 | csharp_space_after_keywords_in_control_flow_statements = true
27 | csharp_space_before_colon_in_inheritance_clause = true
28 | csharp_space_between_method_call_empty_parameter_list_parentheses = false
29 | csharp_space_between_method_call_name_and_opening_parenthesis = false
30 | csharp_space_between_method_call_parameter_list_parentheses = false
31 | csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
32 | csharp_space_between_method_declaration_parameter_list_parentheses = false
33 | csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
34 | csharp_style_expression_bodied_accessors = true:suggestion
35 | csharp_style_expression_bodied_constructors = false:suggestion
36 | csharp_style_expression_bodied_methods = false:suggestion
37 | csharp_style_expression_bodied_properties = true:suggestion
38 | csharp_style_inlined_variable_declaration = true:suggestion
39 | csharp_style_var_elsewhere = true:suggestion
40 | csharp_style_var_for_built_in_types = true:suggestion
41 | csharp_style_var_when_type_is_apparent = true:suggestion
42 | dotnet_sort_system_directives_first = false
43 | dotnet_style_explicit_tuple_names = true:suggestion
44 | dotnet_style_object_initializer = true:suggestion
45 | csharp_style_pattern_local_over_anonymous_function = false:suggestion
46 | dotnet_style_predefined_type_for_member_access = true:suggestion
47 | dotnet_style_prefer_inferred_anonymous_type_member_names = false:suggestion
48 | dotnet_style_prefer_inferred_tuple_names = true:suggestion
49 | dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
50 | dotnet_style_require_accessibility_modifiers = for_non_interface_members:suggestion
51 | dotnet_style_qualification_for_field = false:suggestion
52 | dotnet_style_qualification_for_method = false:suggestion
53 | dotnet_style_qualification_for_property = false:suggestion
54 |
55 |
56 | # Analyzer Configuration
57 | # These are rules we want to either ignore or have set as suggestion or info
58 |
59 | # CA1014: Mark assemblies with CLSCompliant
60 | # https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1014
61 | dotnet_diagnostic.CA1014.severity = none
62 |
63 | # CA1725: Parameter names should match base declaration
64 | # https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1725
65 | dotnet_diagnostic.CA1725.severity = suggestion
66 |
67 | # CA2227: Collection properties should be read only
68 | # https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca2227
69 | dotnet_diagnostic.CA2227.severity = suggestion
70 |
--------------------------------------------------------------------------------
/Tests/RockLib.Configuration.MessagingProvider.Tests/BlocklistSettingFilterTests.cs:
--------------------------------------------------------------------------------
1 | using FluentAssertions;
2 | using Moq;
3 | using System;
4 | using System.Collections.Generic;
5 | using Xunit;
6 |
7 | namespace RockLib.Configuration.MessagingProvider.Tests
8 | {
9 | public static class BlocklistSettingFilterTests
10 | {
11 | private static readonly string[] blockedSettings = new[] { "foo" };
12 |
13 | [Fact]
14 | public static void ConstructorThrowsIfBlockedSettingsIsNull()
15 | {
16 | var action = () => new BlocklistSettingFilter(null!);
17 | action.Should().ThrowExactly();
18 | }
19 |
20 | [Fact]
21 | public static void ConstructorSetsBlockedSettings()
22 | {
23 | var blockedSettings = new[] { "foo" };
24 |
25 | var filter = new BlocklistSettingFilter(blockedSettings);
26 |
27 | filter.BlockedSettings.Should().BeEquivalentTo(blockedSettings);
28 | }
29 |
30 | [Fact]
31 | public static void ConstructorSetsInnerFilter()
32 | {
33 | var innerFilter = Mock.Of();// new FakeSettingFilter();
34 |
35 | var filter = new BlocklistSettingFilter(blockedSettings, innerFilter);
36 |
37 | filter.InnerFilter.Should().BeSameAs(innerFilter);
38 | }
39 |
40 | [Fact]
41 | public static void ReturnsWhatTheInnerFilterReturnsWhenTheSettingIsNotInTheBlocklist()
42 | {
43 | var mockInnerFilter = new Mock();
44 | mockInnerFilter
45 | .Setup(m => m.ShouldProcessSettingChange(It.IsAny(), It.IsAny>()))
46 | .Returns(false);
47 |
48 | var filter = new BlocklistSettingFilter(blockedSettings, mockInnerFilter.Object);
49 |
50 | var receivedMessageHeaders = new Dictionary();
51 |
52 | filter.ShouldProcessSettingChange("bar", receivedMessageHeaders)
53 | .Should().Be(false);
54 |
55 | mockInnerFilter.Verify(m => m.ShouldProcessSettingChange(
56 | It.Is(s => s == "bar"), It.Is>(headers => headers == receivedMessageHeaders)));
57 | }
58 |
59 | [Fact]
60 | public static void ReturnsFalseWhenTheSettingIsInTheBlocklist()
61 | {
62 | var mockInnerFilter = new Mock();
63 |
64 | var filter = new BlocklistSettingFilter(blockedSettings, mockInnerFilter.Object);
65 |
66 | filter.ShouldProcessSettingChange("foo", new Dictionary())
67 | .Should().Be(false);
68 |
69 | mockInnerFilter.Verify(m => m.ShouldProcessSettingChange(
70 | It.IsAny(), It.IsAny>()), Times.Never);
71 | }
72 |
73 | [Fact]
74 | public static void ReturnsFalseWhenTheSettingIsAChildOfAnItemInTheBlocklist()
75 | {
76 | var mockInnerFilter = new Mock();
77 |
78 | var filter = new BlocklistSettingFilter(blockedSettings, mockInnerFilter.Object);
79 |
80 | filter.ShouldProcessSettingChange("foo:bar", new Dictionary())
81 | .Should().Be(false);
82 |
83 | mockInnerFilter.Verify(m => m.ShouldProcessSettingChange(
84 | It.IsAny(), It.IsAny>()), Times.Never);
85 | }
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/Tests/RockLib.Configuration.MessagingProvider.Tests/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 | true
4 |
5 |
6 | all
7 | Rocket Mortgage
8 | Copyright 2025 (c) Rocket Mortgage. All rights reserved.
9 | latest
10 | enable
11 | net48;net8.0
12 | NU1603,NU1701
13 | true
14 |
15 |
16 |
17 | all
18 | runtime; build; native; contentfiles; analyzers; buildtransitive
19 |
20 |
21 |
--------------------------------------------------------------------------------
/Tests/RockLib.Configuration.MessagingProvider.Tests/FakeReceiver.cs:
--------------------------------------------------------------------------------
1 | using RockLib.Messaging;
2 | using System;
3 |
4 | namespace RockLib.Configuration.MessagingProvider.Tests
5 | {
6 | // This class is only for the appsettings.json test.
7 | // Use Moq to create a mocked version of Receiver instead.
8 | public sealed class FakeReceiver : Receiver
9 | {
10 | public FakeReceiver(string name)
11 | : base(name) { }
12 |
13 | protected override void Start()
14 | {
15 | throw new NotImplementedException();
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/Tests/RockLib.Configuration.MessagingProvider.Tests/MessagingConfigurationSourceTests.cs:
--------------------------------------------------------------------------------
1 | using FluentAssertions;
2 | using Moq;
3 | using RockLib.Messaging;
4 | using System;
5 | using Xunit;
6 |
7 | namespace RockLib.Configuration.MessagingProvider.Tests
8 | {
9 | public static class MessagingConfigurationSourceTests
10 | {
11 | [Fact]
12 | public static void ConstructorThrowsIfReceiverIsNull()
13 | {
14 | var action = () => new MessagingConfigurationSource(null!);
15 | action.Should().ThrowExactly().WithMessage("*receiver*");
16 | }
17 |
18 | [Fact]
19 | public static void ConstructorThrowsIfReceiverIsUsedByAnotherMessagingConfigurationSource()
20 | {
21 | using var receiver = new Mock("fake").Object;
22 |
23 | // Create a source with the receiver and throw it away.
24 | _ = new MessagingConfigurationSource(receiver);
25 |
26 | // Passing the same receiver to another source causes it to throw.
27 | var action = () => new MessagingConfigurationSource(receiver);
28 | action.Should().ThrowExactly().WithMessage("The same instance of IReceiver cannot be used to create multiple instances of MessagingConfigurationSource.*receiver*");
29 | }
30 |
31 | [Fact]
32 | public static void ConstructorThrowsIfReceiverIsAlreadyStarted()
33 | {
34 | using var receiver = new Mock("fake").Object;
35 | receiver.Start(m => m.AcknowledgeAsync());
36 |
37 | var action = () => new MessagingConfigurationSource(receiver);
38 | action.Should().ThrowExactly().WithMessage("The receiver is already started.*receiver*");
39 | }
40 |
41 | [Fact]
42 | public static void ConstructorSetsReceiverProperty()
43 | {
44 | using var receiver = new Mock("fake").Object;
45 |
46 | var source = new MessagingConfigurationSource(receiver);
47 |
48 | source.Receiver.Should().BeSameAs(receiver);
49 | }
50 |
51 | [Fact]
52 | public static void ConstructorSetsSettingFilterProperty()
53 | {
54 | using var receiver = new Mock("fake").Object;
55 | var filter = Mock.Of();
56 |
57 | var source = new MessagingConfigurationSource(receiver, filter);
58 |
59 | source.SettingFilter.Should().BeSameAs(filter);
60 | }
61 |
62 | [Fact]
63 | public static void BuildMethodReturnsMessagingConfigurationProvider()
64 | {
65 | using var receiver = new Mock("fake").Object;
66 | var filter = Mock.Of();
67 |
68 | var source = new MessagingConfigurationSource(receiver, filter);
69 |
70 | var provider = source.Build(null!);
71 |
72 | provider.Should().BeOfType();
73 |
74 | var messagingProvider = (MessagingConfigurationProvider)provider;
75 |
76 | messagingProvider.Receiver.Should().BeSameAs(receiver);
77 | messagingProvider.SettingFilter.Should().BeSameAs(filter);
78 | }
79 |
80 | [Fact]
81 | public static void BuildMethodReturnsSameMessagingConfigurationProviderEachTime()
82 | {
83 | using var receiver = new Mock("fake").Object;
84 |
85 | var source = new MessagingConfigurationSource(receiver);
86 |
87 | var provider1 = source.Build(null!);
88 | var provider2 = source.Build(null!);
89 |
90 | provider1.Should().BeSameAs(provider2);
91 | }
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/Tests/RockLib.Configuration.MessagingProvider.Tests/NullSettingFilterTests.cs:
--------------------------------------------------------------------------------
1 | using FluentAssertions;
2 | using System.Collections.Generic;
3 | using Xunit;
4 |
5 | namespace RockLib.Configuration.MessagingProvider.Tests
6 | {
7 | public static class NullSettingFilterTests
8 | {
9 | [Fact]
10 | public static void AlwaysReturnsTrue()
11 | {
12 | NullSettingFilter.Instance.ShouldProcessSettingChange(null!, new Dictionary()).Should().BeTrue();
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Tests/RockLib.Configuration.MessagingProvider.Tests/RockLib.Configuration.MessagingProvider.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | false
4 | xUnit1030
5 |
6 |
7 |
8 |
9 |
10 |
11 | Always
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | runtime; build; native; contentfiles; analyzers; buildtransitive
21 | all
22 |
23 |
24 | runtime; build; native; contentfiles; analyzers; buildtransitive
25 | all
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/Tests/RockLib.Configuration.MessagingProvider.Tests/RockLibMessagingProviderExtensionsTests.cs:
--------------------------------------------------------------------------------
1 | using FluentAssertions;
2 | using Microsoft.Extensions.Configuration;
3 | using Microsoft.Extensions.Configuration.Json;
4 | using Moq;
5 | using RockLib.Messaging;
6 | using Xunit;
7 |
8 | namespace RockLib.Configuration.MessagingProvider.Tests
9 | {
10 | public static class RockLibMessagingProviderExtensionsTests
11 | {
12 | [Fact]
13 | public static void AddRockLibMessagingProviderExtensionMethod1AddsAMessagingConfigurationSourceToTheConfigurationBuilder()
14 | {
15 | var filter = Mock.Of();
16 |
17 | var builder = new ConfigurationBuilder()
18 | .AddJsonFile("appsettings.json")
19 | .AddRockLibMessagingProvider("fake", filter);
20 |
21 | builder.Sources.Should().HaveCount(2);
22 | builder.Sources[0].Should().BeOfType();
23 | builder.Sources[1].Should().BeOfType();
24 |
25 | var source = (MessagingConfigurationSource)builder.Sources[1];
26 |
27 | source.Receiver.Should().BeOfType();
28 | source.Receiver.Name.Should().Be("fake");
29 | source.SettingFilter.Should().BeSameAs(filter);
30 | }
31 |
32 | [Fact]
33 | public static void AddRockLibMessagingProviderExtensionMethod2AddsAMessagingConfigurationSourceToTheConfigurationBuilder()
34 | {
35 | using var receiver = new Mock("fake").Object;
36 | var filter = Mock.Of();
37 |
38 | var builder = new ConfigurationBuilder();
39 |
40 | builder.AddRockLibMessagingProvider(receiver, filter);
41 |
42 | builder.Sources.Should().HaveCount(1);
43 | builder.Sources[0].Should().BeOfType();
44 |
45 | var source = (MessagingConfigurationSource)builder.Sources[0];
46 |
47 | source.Receiver.Should().BeSameAs(receiver);
48 | source.SettingFilter.Should().BeSameAs(filter);
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/Tests/RockLib.Configuration.MessagingProvider.Tests/SafelistSettingFilterTests.cs:
--------------------------------------------------------------------------------
1 | using FluentAssertions;
2 | using Moq;
3 | using System;
4 | using System.Collections.Generic;
5 | using Xunit;
6 |
7 | namespace RockLib.Configuration.MessagingProvider.Tests
8 | {
9 | public static class SafelistSettingFilterTests
10 | {
11 | private static readonly string[] safeSettings = new[] { "foo" };
12 |
13 | [Fact]
14 | public static void ConstructorThrowsIfSafeSettingsIsNull()
15 | {
16 | var action = () => new SafelistSettingFilter(null!);
17 | action.Should().ThrowExactly();
18 | }
19 |
20 | [Fact]
21 | public static void ConstructorSetsSafeSettings()
22 | {
23 | var safeSettings = new[] { "foo" };
24 |
25 | var filter = new SafelistSettingFilter(safeSettings);
26 |
27 | filter.SafeSettings.Should().BeEquivalentTo(safeSettings);
28 | }
29 |
30 | [Fact]
31 | public static void ConstructorSetsInnerFilter()
32 | {
33 | var innerFilter = Mock.Of();
34 |
35 | var filter = new SafelistSettingFilter(safeSettings, innerFilter);
36 |
37 | filter.InnerFilter.Should().BeSameAs(innerFilter);
38 | }
39 |
40 | [Fact]
41 | public static void ReturnsWhatTheInnerFilterReturnsWhenTheSettingIsInTheSafelist()
42 | {
43 | var mockInnerFilter = new Mock();
44 | mockInnerFilter
45 | .Setup(m => m.ShouldProcessSettingChange(It.IsAny(), It.IsAny>()))
46 | .Returns(false);
47 |
48 | var filter = new SafelistSettingFilter(safeSettings, mockInnerFilter.Object);
49 |
50 | var receivedMessageHeaders = new Dictionary();
51 |
52 | filter.ShouldProcessSettingChange("foo", receivedMessageHeaders)
53 | .Should().Be(false);
54 |
55 | mockInnerFilter.Verify(m => m.ShouldProcessSettingChange(
56 | It.Is(s => s == "foo"), It.Is>(headers => headers == receivedMessageHeaders)));
57 | }
58 |
59 | [Fact]
60 | public static void ReturnsWhatTheInnerFilterReturnsWhenTheSettingIsAChildOfAnItemInTheAllowlist()
61 | {
62 | var mockInnerFilter = new Mock();
63 | mockInnerFilter
64 | .Setup(m => m.ShouldProcessSettingChange(It.IsAny(), It.IsAny>()))
65 | .Returns(false);
66 |
67 | var filter = new SafelistSettingFilter(safeSettings, mockInnerFilter.Object);
68 |
69 | var receivedMessageHeaders = new Dictionary();
70 |
71 | filter.ShouldProcessSettingChange("foo:bar", receivedMessageHeaders)
72 | .Should().Be(false);
73 |
74 | mockInnerFilter.Verify(m => m.ShouldProcessSettingChange(
75 | It.Is(s => s == "foo:bar"), It.Is>(headers => headers == receivedMessageHeaders)));
76 | }
77 |
78 | [Fact]
79 | public static void ReturnsFalseWhenTheSettingIsNotInTheSafelist()
80 | {
81 | var mockInnerFilter = new Mock();
82 |
83 | var filter = new SafelistSettingFilter(safeSettings, mockInnerFilter.Object);
84 |
85 | filter.ShouldProcessSettingChange("bar", new Dictionary())
86 | .Should().Be(false);
87 |
88 | mockInnerFilter.Verify(m => m.ShouldProcessSettingChange(
89 | It.IsAny(), It.IsAny>()), Times.Never);
90 | }
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/Tests/RockLib.Configuration.MessagingProvider.Tests/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "RockLib.Messaging": {
3 | "receivers": {
4 | "type": "RockLib.Configuration.MessagingProvider.Tests.FakeReceiver, RockLib.Configuration.MessagingProvider.Tests",
5 | "value": {
6 | "name": "fake"
7 | },
8 | "reloadOnChange": false
9 | }
10 | }
11 | }
--------------------------------------------------------------------------------
/Tests/RockLib.Configuration.ObjectFactory.Tests/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | end_of_line = lf
5 |
6 | [*.cs]
7 | # Styling
8 | indent_style = space
9 | csharp_indent_case_contents = true
10 | csharp_indent_switch_labels = true
11 | csharp_new_line_before_catch = true
12 | csharp_new_line_before_else = true
13 | csharp_new_line_before_finally = true
14 | csharp_new_line_before_members_in_anonymous_types = false
15 | csharp_new_line_before_members_in_object_initializers = false
16 | csharp_new_line_before_open_brace = methods, control_blocks, types, properties, lambdas, accessors, object_collection_array_initializers
17 | csharp_new_line_between_query_expression_clauses = true
18 | csharp_prefer_braces = false:suggestion
19 | csharp_prefer_simple_default_expression = true:suggestion
20 | csharp_preferred_modifier_order = public,private,internal,protected,static,readonly,async,override,sealed:suggestion
21 | csharp_preserve_single_line_blocks = true
22 | csharp_preserve_single_line_statements = true
23 | csharp_space_after_cast = false
24 | csharp_space_after_colon_in_inheritance_clause = true
25 | csharp_space_after_keywords_in_control_flow_statements = true
26 | csharp_space_before_colon_in_inheritance_clause = true
27 | csharp_space_between_method_call_empty_parameter_list_parentheses = false
28 | csharp_space_between_method_call_name_and_opening_parenthesis = false
29 | csharp_space_between_method_call_parameter_list_parentheses = false
30 | csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
31 | csharp_space_between_method_declaration_parameter_list_parentheses = false
32 | csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
33 | csharp_style_expression_bodied_accessors = true:suggestion
34 | csharp_style_expression_bodied_constructors = false:suggestion
35 | csharp_style_expression_bodied_methods = false:suggestion
36 | csharp_style_expression_bodied_properties = true:suggestion
37 | csharp_style_inlined_variable_declaration = true:suggestion
38 | csharp_style_var_elsewhere = true:suggestion
39 | csharp_style_var_for_built_in_types = true:suggestion
40 | csharp_style_var_when_type_is_apparent = true:suggestion
41 | dotnet_sort_system_directives_first = false
42 | dotnet_style_explicit_tuple_names = true:suggestion
43 | dotnet_style_object_initializer = true:suggestion
44 | csharp_style_pattern_local_over_anonymous_function = false:suggestion
45 | dotnet_style_predefined_type_for_member_access = true:suggestion
46 | dotnet_style_prefer_inferred_anonymous_type_member_names = false:suggestion
47 | dotnet_style_prefer_inferred_tuple_names = true:suggestion
48 | dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
49 | dotnet_style_require_accessibility_modifiers = for_non_interface_members:suggestion
50 | dotnet_style_qualification_for_field = false:suggestion
51 | dotnet_style_qualification_for_method = false:suggestion
52 | dotnet_style_qualification_for_property = false:suggestion
53 |
54 |
55 | # Analyzer Configuration
56 | # These are rules we want to either ignore or have set as suggestion or info
57 |
58 | # CA1014: Mark assemblies with CLSCompliant
59 | # https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1014
60 | dotnet_diagnostic.CA1014.severity = none
61 |
62 | # CA1725: Parameter names should match base declaration
63 | # https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1725
64 | dotnet_diagnostic.CA1725.severity = suggestion
65 |
66 | # CA2227: Collection properties should be read only
67 | # https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca2227
68 | dotnet_diagnostic.CA2227.severity = suggestion
--------------------------------------------------------------------------------
/Tests/RockLib.Configuration.ObjectFactory.Tests/AssemblySettings.cs:
--------------------------------------------------------------------------------
1 | using Xunit;
2 |
3 | [assembly: CollectionBehavior(CollectionBehavior.CollectionPerAssembly)]
--------------------------------------------------------------------------------
/Tests/RockLib.Configuration.ObjectFactory.Tests/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 | true
4 |
5 |
6 | all
7 | Rocket Mortgage
8 | latest
9 | enable
10 | net48;net8.0
11 | NU1603,NU1701
12 | true
13 |
14 |
15 |
16 | all
17 | runtime; build; native; contentfiles; analyzers; buildtransitive
18 |
19 |
20 |
--------------------------------------------------------------------------------
/Tests/RockLib.Configuration.ObjectFactory.Tests/RockLib.Configuration.ObjectFactory.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | false
4 | Tests
5 | Debug;Release;ReferenceModel
6 |
7 |
8 | TRACE;REFERENCE_MODEL
9 |
10 |
11 |
12 |
13 |
14 |
15 | runtime; build; native; contentfiles; analyzers; buildtransitive
16 | all
17 |
18 |
19 | runtime; build; native; contentfiles; analyzers; buildtransitive
20 | all
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/Tests/RockLib.Configuration.ProxyFactory.Tests/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | end_of_line = lf
5 |
6 | [*.cs]
7 | # Styling
8 | indent_style = space
9 | indent_size = 4
10 | csharp_indent_case_contents = true
11 | csharp_indent_switch_labels = true
12 | csharp_new_line_before_catch = true
13 | csharp_new_line_before_else = true
14 | csharp_new_line_before_finally = true
15 | csharp_new_line_before_members_in_anonymous_types = false
16 | csharp_new_line_before_members_in_object_initializers = false
17 | csharp_new_line_before_open_brace = methods, control_blocks, types, properties, lambdas, accessors, object_collection_array_initializers
18 | csharp_new_line_between_query_expression_clauses = true
19 | csharp_prefer_braces = false:suggestion
20 | csharp_prefer_simple_default_expression = true:suggestion
21 | csharp_preferred_modifier_order = public,private,internal,protected,static,readonly,async,override,sealed:suggestion
22 | csharp_preserve_single_line_blocks = true
23 | csharp_preserve_single_line_statements = true
24 | csharp_space_after_cast = false
25 | csharp_space_after_colon_in_inheritance_clause = true
26 | csharp_space_after_keywords_in_control_flow_statements = true
27 | csharp_space_before_colon_in_inheritance_clause = true
28 | csharp_space_between_method_call_empty_parameter_list_parentheses = false
29 | csharp_space_between_method_call_name_and_opening_parenthesis = false
30 | csharp_space_between_method_call_parameter_list_parentheses = false
31 | csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
32 | csharp_space_between_method_declaration_parameter_list_parentheses = false
33 | csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
34 | csharp_style_expression_bodied_accessors = true:suggestion
35 | csharp_style_expression_bodied_constructors = false:suggestion
36 | csharp_style_expression_bodied_methods = false:suggestion
37 | csharp_style_expression_bodied_properties = true:suggestion
38 | csharp_style_inlined_variable_declaration = true:suggestion
39 | csharp_style_var_elsewhere = true:suggestion
40 | csharp_style_var_for_built_in_types = true:suggestion
41 | csharp_style_var_when_type_is_apparent = true:suggestion
42 | dotnet_sort_system_directives_first = false
43 | dotnet_style_explicit_tuple_names = true:suggestion
44 | dotnet_style_object_initializer = true:suggestion
45 | csharp_style_pattern_local_over_anonymous_function = false:suggestion
46 | dotnet_style_predefined_type_for_member_access = true:suggestion
47 | dotnet_style_prefer_inferred_anonymous_type_member_names = false:suggestion
48 | dotnet_style_prefer_inferred_tuple_names = true:suggestion
49 | dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
50 | dotnet_style_require_accessibility_modifiers = for_non_interface_members:suggestion
51 | dotnet_style_qualification_for_field = false:suggestion
52 | dotnet_style_qualification_for_method = false:suggestion
53 | dotnet_style_qualification_for_property = false:suggestion
54 |
55 |
56 | # Analyzer Configuration
57 | # These are rules we want to either ignore or have set as suggestion or info
58 |
59 | # CA1014: Mark assemblies with CLSCompliant
60 | # https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1014
61 | dotnet_diagnostic.CA1014.severity = none
62 |
63 | # CA1725: Parameter names should match base declaration
64 | # https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1725
65 | dotnet_diagnostic.CA1725.severity = suggestion
66 |
67 | # CA2227: Collection properties should be read only
68 | # https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca2227
69 | dotnet_diagnostic.CA2227.severity = suggestion
70 |
--------------------------------------------------------------------------------
/Tests/RockLib.Configuration.ProxyFactory.Tests/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 | true
4 |
5 |
6 | all
7 | Rocket Mortgage
8 | latest
9 | enable
10 | net48;net8.0
11 | NU1603,NU1701
12 | true
13 |
14 |
15 |
16 | all
17 | runtime; build; native; contentfiles; analyzers; buildtransitive
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/Tests/RockLib.Configuration.ProxyFactory.Tests/RockLib.Configuration.ProxyFactory.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | Tests
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | runtime; build; native; contentfiles; analyzers; buildtransitive
12 | all
13 |
14 |
15 | runtime; build; native; contentfiles; analyzers; buildtransitive
16 | all
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/Tests/RockLib.Configuration.Tests/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | end_of_line = lf
5 |
6 | [*.cs]
7 | # Styling
8 | indent_style = space
9 | indent_size = 4
10 | csharp_indent_case_contents = true
11 | csharp_indent_switch_labels = true
12 | csharp_new_line_before_catch = true
13 | csharp_new_line_before_else = true
14 | csharp_new_line_before_finally = true
15 | csharp_new_line_before_members_in_anonymous_types = false
16 | csharp_new_line_before_members_in_object_initializers = false
17 | csharp_new_line_before_open_brace = methods, control_blocks, types, properties, lambdas, accessors, object_collection_array_initializers
18 | csharp_new_line_between_query_expression_clauses = true
19 | csharp_prefer_braces = false:suggestion
20 | csharp_prefer_simple_default_expression = true:suggestion
21 | csharp_preferred_modifier_order = public,private,internal,protected,static,readonly,async,override,sealed:suggestion
22 | csharp_preserve_single_line_blocks = true
23 | csharp_preserve_single_line_statements = true
24 | csharp_space_after_cast = false
25 | csharp_space_after_colon_in_inheritance_clause = true
26 | csharp_space_after_keywords_in_control_flow_statements = true
27 | csharp_space_before_colon_in_inheritance_clause = true
28 | csharp_space_between_method_call_empty_parameter_list_parentheses = false
29 | csharp_space_between_method_call_name_and_opening_parenthesis = false
30 | csharp_space_between_method_call_parameter_list_parentheses = false
31 | csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
32 | csharp_space_between_method_declaration_parameter_list_parentheses = false
33 | csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
34 | csharp_style_expression_bodied_accessors = true:suggestion
35 | csharp_style_expression_bodied_constructors = false:suggestion
36 | csharp_style_expression_bodied_methods = false:suggestion
37 | csharp_style_expression_bodied_properties = true:suggestion
38 | csharp_style_inlined_variable_declaration = true:suggestion
39 | csharp_style_var_elsewhere = true:suggestion
40 | csharp_style_var_for_built_in_types = true:suggestion
41 | csharp_style_var_when_type_is_apparent = true:suggestion
42 | dotnet_sort_system_directives_first = false
43 | dotnet_style_explicit_tuple_names = true:suggestion
44 | dotnet_style_object_initializer = true:suggestion
45 | csharp_style_pattern_local_over_anonymous_function = false:suggestion
46 | dotnet_style_predefined_type_for_member_access = true:suggestion
47 | dotnet_style_prefer_inferred_anonymous_type_member_names = false:suggestion
48 | dotnet_style_prefer_inferred_tuple_names = true:suggestion
49 | dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
50 | dotnet_style_require_accessibility_modifiers = for_non_interface_members:suggestion
51 | dotnet_style_qualification_for_field = false:suggestion
52 | dotnet_style_qualification_for_method = false:suggestion
53 | dotnet_style_qualification_for_property = false:suggestion
54 |
55 |
56 | # Analyzer Configuration
57 | # These are rules we want to either ignore or have set as suggestion or info
58 |
59 | # CA1014: Mark assemblies with CLSCompliant
60 | # https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1014
61 | dotnet_diagnostic.CA1014.severity = none
62 |
63 | # CA1725: Parameter names should match base declaration
64 | # https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1725
65 | dotnet_diagnostic.CA1725.severity = suggestion
66 |
67 | # CA2227: Collection properties should be read only
68 | # https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca2227
69 | dotnet_diagnostic.CA2227.severity = suggestion
70 |
--------------------------------------------------------------------------------
/Tests/RockLib.Configuration.Tests/AssemblySettings.cs:
--------------------------------------------------------------------------------
1 | using Xunit;
2 |
3 | [assembly: CollectionBehavior(CollectionBehavior.CollectionPerAssembly)]
--------------------------------------------------------------------------------
/Tests/RockLib.Configuration.Tests/CompositeSectionTests.cs:
--------------------------------------------------------------------------------
1 | using FluentAssertions;
2 | using Microsoft.Extensions.Configuration;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using Xunit;
6 |
7 | namespace RockLib.Configuration.Tests;
8 |
9 | #if NET8_0_OR_GREATER
10 | #pragma warning disable CS8620 // Argument cannot be used for parameter due to differences in the nullability of reference types.
11 | #endif
12 | public class CompositeSectionTests
13 | {
14 | [Fact]
15 | public void CompositeSectionCombinesSections()
16 | {
17 | var config = new ConfigurationBuilder()
18 | .AddInMemoryCollection(new Dictionary
19 | {
20 | { "foo.bar:garply:baz", "123" },
21 | { "foo_bar:garply:qux", "abc" },
22 | }).Build();
23 |
24 | var foobarSection = config.GetCompositeSection("foo.bar", "foo_bar");
25 |
26 | foobarSection["garply:baz"].Should().Be("123");
27 | foobarSection["garply:qux"].Should().Be("abc");
28 |
29 | var garplySection = foobarSection.GetSection("garply");
30 | garplySection["baz"].Should().Be("123");
31 | garplySection["qux"].Should().Be("abc");
32 |
33 | var children = foobarSection.GetChildren().ToList();
34 | children.Count.Should().Be(1);
35 | children[0]["baz"].Should().Be("123");
36 | children[0]["qux"].Should().Be("abc");
37 | }
38 |
39 | [Fact]
40 | public void CompositeSectionPathOrderIsSignificant()
41 | {
42 | var config = new ConfigurationBuilder()
43 | .AddInMemoryCollection(new Dictionary
44 | {
45 | { "foo_bar:garply:baz", "abc" }, // Both of these settings have
46 | { "foo.bar:garply:baz", "123" }, // the same composite path.
47 | }).Build();
48 |
49 | // Since the dot section is the first key, its value should be selected.
50 | var foobarSection = config.GetCompositeSection("foo.bar", "foo_bar");
51 |
52 | foobarSection["garply:baz"].Should().Be("123");
53 |
54 | var garplySection = foobarSection.GetSection("garply");
55 | garplySection["baz"].Should().Be("123");
56 |
57 | var children = foobarSection.GetChildren().ToList();
58 | children.Count.Should().Be(1);
59 | children[0]["baz"].Should().Be("123");
60 | }
61 |
62 | [Fact]
63 | public void GetChildrenMaintainsOrderWhenTheChildAreListItemsAndTheSecondItemComesFromTheFirstSectionOfTheCompositeSection()
64 | {
65 | var config = new ConfigurationBuilder()
66 | .AddInMemoryCollection(new Dictionary
67 | {
68 | { "second_section:0:baz", "abc" },
69 | { "first_section:1:baz", "xyz" },
70 | }).Build();
71 |
72 | var foobarSection = config.GetCompositeSection("first_section", "second_section");
73 |
74 | var children = foobarSection.GetChildren().ToList();
75 | children.Count.Should().Be(2);
76 | children[0]["baz"].Should().Be("abc");
77 | children[1]["baz"].Should().Be("xyz");
78 | }
79 | }
80 | #if NET8_0_OR_GREATER
81 | #pragma warning restore CS8620 // Argument cannot be used for parameter due to differences in the nullability of reference types.
82 | #endif
--------------------------------------------------------------------------------
/Tests/RockLib.Configuration.Tests/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 | true
4 |
5 |
6 | all
7 | Rocket Mortgage
8 | latest
9 | enable
10 | net48;net8.0
11 | NU1603,NU1701
12 | true
13 |
14 |
15 |
16 | all
17 | runtime; build; native; contentfiles; analyzers; buildtransitive
18 |
19 |
20 |
--------------------------------------------------------------------------------
/Tests/RockLib.Configuration.Tests/RockLib.Configuration.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | false
4 |
5 |
6 |
7 |
8 |
9 |
10 | runtime; build; native; contentfiles; analyzers; buildtransitive
11 | all
12 |
13 |
14 | runtime; build; native; contentfiles; analyzers; buildtransitive
15 | all
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RockLib/RockLib.Configuration/d960875310ad43a4331d443275c6045d5cfaf124/icon.png
--------------------------------------------------------------------------------