Skip to content

Commit dedbbeb

Browse files
committed
MET-4188 add GetOptions extension to IConfiguration
1 parent 2e11c5b commit dedbbeb

File tree

6 files changed

+173
-15
lines changed

6 files changed

+173
-15
lines changed

README.md

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Samhammer.Options
1+
# Samhammer.Options
22

33
## Usage
44

@@ -93,6 +93,29 @@ appsettings.json
9393
}
9494
```
9595

96+
### How to get options from IConfiguration ##
97+
It is possible to bind options directly from IConfiguration. \
98+
This can be helpful in Program.cs to use typed Options there, even before they are registered in IOC.
99+
100+
```csharp
101+
var builder = WebApplication.CreateBuilder(args);
102+
var key = builder.Configuration.GetOptions<ExampleOptions>().MyKey;
103+
```
104+
105+
```csharp
106+
[Option]
107+
public class ExampleOptions{
108+
public string MyKey { get; set; }
109+
}
110+
```
111+
112+
appsettings.json
113+
```json
114+
"ExampleOptions": {
115+
"MyKey": "1234"
116+
}
117+
```
118+
96119
## Configuration
97120
Starting with version 3.1.5 all customizations needs to be done with the options action.
98121

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
using System.Collections.Generic;
2+
using FluentAssertions;
3+
using Microsoft.Extensions.Configuration;
4+
using Samhammer.Options.Abstractions;
5+
using Xunit;
6+
7+
namespace Samhammer.Options.Test
8+
{
9+
public class ConfigurationExtensionsTest
10+
{
11+
private readonly ConfigurationBuilder configurationBuilder;
12+
13+
private readonly List<KeyValuePair<string, string>> configurationValues;
14+
15+
private IConfiguration Configuration => configurationBuilder.Build();
16+
17+
public ConfigurationExtensionsTest()
18+
{
19+
configurationBuilder = new ConfigurationBuilder();
20+
configurationValues = new List<KeyValuePair<string, string>>();
21+
configurationBuilder.AddInMemoryCollection(configurationValues);
22+
}
23+
24+
[Fact]
25+
public void GetOptions_FromConfiguration_WithAttribute()
26+
{
27+
// arrange
28+
configurationValues.Add(new KeyValuePair<string, string>("TestOptions:Url", "http://localhost"));
29+
30+
// act
31+
var options = Configuration.GetOptions<TestOptionsWithAttribute>();
32+
33+
// assert
34+
var expectedOptions = new TestOptionsWithAttribute { Url = "http://localhost" };
35+
options.Should().BeEquivalentTo(expectedOptions);
36+
}
37+
38+
[Fact]
39+
public void GetOptions_FromConfiguration_WithRootAttribute()
40+
{
41+
// arrange
42+
configurationValues.Add(new KeyValuePair<string, string>("Url", "http://localhost"));
43+
44+
// act
45+
var options = Configuration.GetOptions<TestOptionsWithRootAttribute>();
46+
47+
// assert
48+
var expectedOptions = new TestOptionsWithRootAttribute { Url = "http://localhost" };
49+
options.Should().BeEquivalentTo(expectedOptions);
50+
}
51+
52+
[Fact]
53+
public void GetOption_FromConfiguration_WithoutAttribute()
54+
{
55+
// arrange
56+
configurationValues.Add(new KeyValuePair<string, string>("TestOptions:Url", "http://localhost"));
57+
58+
// act
59+
var options = Configuration.GetOptions<TestOptions>();
60+
61+
// assert
62+
var expectedOptions = new TestOptions { Url = "http://localhost" };
63+
options.Should().BeEquivalentTo(expectedOptions);
64+
}
65+
66+
[Option(sectionName: "TestOptions")]
67+
private class TestOptionsWithAttribute
68+
{
69+
public string Url { get; set; }
70+
}
71+
72+
[Option(FromRootSection = true)]
73+
private class TestOptionsWithRootAttribute
74+
{
75+
public string Url { get; set; }
76+
}
77+
78+
private class TestOptions
79+
{
80+
public string Url { get; set; }
81+
}
82+
}
83+
}

src/Samhammer.Options.sln.DotSettings

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,12 @@
66
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=LocalConstants/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /&gt;</s:String>
77
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateInstanceFields/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String>
88
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateStaticFields/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String>
9+
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=4a98fdf6_002D7d98_002D4f5a_002Dafeb_002Dea44ad98c70c/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Instance" AccessRightKinds="Private" Description="Instance fields (private)"&gt;&lt;ElementKinds&gt;&lt;Kind Name="FIELD" /&gt;&lt;Kind Name="READONLY_FIELD" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" WarnAboutPrefixesAndSuffixes="False" Prefix="" Suffix="" Style="aaBb" /&gt;&lt;/Policy&gt;</s:String>
10+
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=a4f433b8_002Dabcd_002D4e55_002Da08f_002D82e78cef0f0c/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Any" AccessRightKinds="Any" Description="Local constants"&gt;&lt;ElementKinds&gt;&lt;Kind Name="LOCAL_CONSTANT" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" WarnAboutPrefixesAndSuffixes="False" Prefix="" Suffix="" Style="AaBb" /&gt;&lt;/Policy&gt;</s:String>
11+
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=f9fce829_002De6f4_002D4cb2_002D80f1_002D5497c44f51df/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Static" AccessRightKinds="Private" Description="Static fields (private)"&gt;&lt;ElementKinds&gt;&lt;Kind Name="FIELD" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" WarnAboutPrefixesAndSuffixes="False" Prefix="" Suffix="" Style="aaBb" /&gt;&lt;/Policy&gt;</s:String>
912
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpKeepExistingMigration/@EntryIndexedValue">True</s:Boolean>
1013
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpPlaceEmbeddedOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
1114
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpUseContinuousIndentInsideBracesMigration/@EntryIndexedValue">True</s:Boolean>
1215
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean>
16+
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EPredefinedNamingRulesToUserRulesUpgrade/@EntryIndexedValue">True</s:Boolean>
1317
</wpf:ResourceDictionary>
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
using System.Linq;
2+
using Microsoft.Extensions.Configuration;
3+
using Samhammer.Options.Abstractions;
4+
using Samhammer.Options.Utils;
5+
6+
namespace Samhammer.Options
7+
{
8+
public static class ConfigurationExtensions
9+
{
10+
public static T GetOptions<T>(this IConfiguration configuration) where T : new()
11+
{
12+
var optionType = typeof(T);
13+
var attributes = ReflectionUtils.GetAttributesOfType<OptionAttribute>(optionType).ToList();
14+
15+
var section = attributes.Count > 0
16+
? ConfigurationResolver.GetSection(configuration, optionType, attributes.First())
17+
: ConfigurationResolver.GetSection(configuration, optionType);
18+
19+
var option = section.Get<T>();
20+
return option != null ? option : new T();
21+
}
22+
}
23+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
using System;
2+
using Microsoft.Extensions.Configuration;
3+
using Samhammer.Options.Abstractions;
4+
5+
namespace Samhammer.Options
6+
{
7+
internal static class ConfigurationResolver
8+
{
9+
internal static IConfiguration GetSection(IConfiguration configuration, Type optionType, OptionAttribute optionAttribute)
10+
{
11+
var sectionName = GetSectionName(optionType, optionAttribute);
12+
return optionAttribute.FromRootSection ? configuration : configuration.GetSection(sectionName);
13+
}
14+
15+
internal static IConfiguration GetSection(IConfiguration configuration, Type optionType)
16+
{
17+
var sectionName = GetSectionName(optionType);
18+
return configuration.GetSection(sectionName);
19+
}
20+
21+
private static string GetSectionName(Type optionType, OptionAttribute attribute)
22+
{
23+
var key = attribute.SectionName;
24+
25+
if (string.IsNullOrWhiteSpace(key))
26+
{
27+
key = optionType.Name;
28+
}
29+
30+
return key;
31+
}
32+
33+
private static string GetSectionName(Type optionType)
34+
{
35+
return optionType.Name;
36+
}
37+
}
38+
}

src/Samhammer.Options/OptionsResolver.cs

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -51,25 +51,12 @@ public void ResolveConfigurations(IServiceCollection services)
5151

5252
private void ResolveConfiguration(IServiceCollection services, Type optionType, OptionAttribute optionAttribute)
5353
{
54-
var sectionName = GetSectionName(optionType, optionAttribute);
55-
var section = optionAttribute.FromRootSection ? Configuration : Configuration.GetSection(sectionName);
54+
var section = ConfigurationResolver.GetSection(Configuration, optionType, optionAttribute);
5655

5756
var genericMethod = AddOptionMethod.MakeGenericMethod(optionType);
5857
genericMethod.Invoke(this, new object[] { services, section, optionAttribute.IocName });
5958
}
6059

61-
private string GetSectionName(Type optionType, OptionAttribute attribute)
62-
{
63-
var key = attribute.SectionName;
64-
65-
if (string.IsNullOrWhiteSpace(key))
66-
{
67-
key = optionType.Name;
68-
}
69-
70-
return key;
71-
}
72-
7360
private void AddOption<T>(IServiceCollection services, IConfiguration section, string iocOptionName = null) where T : class
7461
{
7562
services.Configure<T>(

0 commit comments

Comments
 (0)