C
C#2mo ago
Mr. Dink

✅ Access a runtime Dictionary easier

My company is switching from ConfigurationManager with transforms over to a 3rd party secrets manager. The secrets manager returns a Dictionary<string, object> on start up. I'm needing to go through and replace all ConfigurationManager calls throughout the project and I was wondering if there's an easier way to access these values rather than magic strings. My first thought is a static class where I define all the property shortcuts and keys. Then verify all keys are present on project load when we get the secrets. Am I barking up the wrong tree and there's an established way?
18 Replies
Sossenbinder
Sossenbinder2mo ago
So you're not using IConfiguration? General guidance in order to avoid the issue you mentioned is to use IOptions and go through DI Instead of using magic strings everywhere
Angius
Angius2mo ago
Your company decided to go with something that's not type-safe and relies on magic strings So, yeah Magic strings it is
Sossenbinder
Sossenbinder2mo ago
I wonder what kind of 3rd party config provider this is
Angius
Angius2mo ago
Sure, you could store the keys in constants or something, and call
string? s = config[ConfigKeys.Database.Server] as string;
string? s = config[ConfigKeys.Database.Server] as string;
or you could use IOptions pattern and just do
string s = config.Database.Server;
string s = config.Database.Server;
like a sane human being If your company is made up of halfwits, then sure, constants make more sense than magic strings everywhere
Mr. Dink
Mr. Dink2mo ago
It's a .NET Framework project from 2014 that we're slowly porting to core that has changed hands more than a dozen times.
Angius
Angius2mo ago
But if you have any pull, slap them in the face and tell them to do it correctly An outdated version of framework doesn't mean you have to settle for shit code The most proper way would be to get a good and proper strongly-typed object instead of a dictionary
Mr. Dink
Mr. Dink2mo ago
No, but it does say a lot about the quantity of tech debt that has been established for more than a decade.
Angius
Angius2mo ago
If the proper way is a no-go for whatever reason (it's 100% a bullshit reason btw) you can go with constants as you planned
Sossenbinder
Sossenbinder2mo ago
If it's of any use, you can make an options-style binding work in net framework as well, worst case you might have to do a bit of custom binding but I'm reasonably sure there's a path to make it work as well, granted you have a DI container setup in place
Mr. Dink
Mr. Dink2mo ago
We'd probably have to wait for a DI option until after the full upgrade. I imagine we'd want to use the built-in. We don't use IoC containers atm. I'll look into the IOptions pattern though. Thanks!
Sossenbinder
Sossenbinder2mo ago
If you're not familiar with it it'll probably be a good read to get the idea behind transforming configuration based on key value indexing to a properly typed configuration object to pass around, yeah
Mr. Dink
Mr. Dink2mo ago
Yep. Will do, thanks! Wasn't sure if there was an established pattern. Good to know.
Unknown User
Unknown User2mo ago
Message Not Public
Sign In & Join Server To View
MODiX
MODiX2mo ago
appsettings.json:
{
"My": {
"Foo": {
"Kix": 5
}
}
}
{
"My": {
"Foo": {
"Kix": 5
}
}
}
src/Foo/FooOptions.cs:
public class FooOptions
{
public const string SectionName = "My:Foo";

public string Bar {get;set;} = "default value for bar";
public int Kix {get;set;} = -1;
public DateTime? Pouet {get;set;} = default;
}
public class FooOptions
{
public const string SectionName = "My:Foo";

public string Bar {get;set;} = "default value for bar";
public int Kix {get;set;} = -1;
public DateTime? Pouet {get;set;} = default;
}
src/Foo/FooServiceCollectionExtensions.cs:
namespace Microsoft.Extensions.DependencyInjection; // <==== recommanded for service.Add so that you don't clutter Startup file

public class FooServiceCollectionExtensions
{
public static IServiceCollection AddFoo(this IServiceCollection services) =>
services
.AddOptions<FooOptions>()
.BindConfiguration(FooOptions.SectionName)
.Validate(options => options.Kix >= 0, $"The configuration key '{FooOptions.SectionName}:{nameof(Kix)}' cannot be negative")
;

public static IServiceCollection AddFoo(this IServiceCollection services, Action<FooOptions> configure) =>
services
.AddFoo()
.Configure(configure);
namespace Microsoft.Extensions.DependencyInjection; // <==== recommanded for service.Add so that you don't clutter Startup file

public class FooServiceCollectionExtensions
{
public static IServiceCollection AddFoo(this IServiceCollection services) =>
services
.AddOptions<FooOptions>()
.BindConfiguration(FooOptions.SectionName)
.Validate(options => options.Kix >= 0, $"The configuration key '{FooOptions.SectionName}:{nameof(Kix)}' cannot be negative")
;

public static IServiceCollection AddFoo(this IServiceCollection services, Action<FooOptions> configure) =>
services
.AddFoo()
.Configure(configure);
Program.cs / Startup.cs:
services.AddFoo();
// or
services.AddFoo(fooOptions => fooOptions.Kix = 12);
services.AddFoo();
// or
services.AddFoo(fooOptions => fooOptions.Kix = 12);
Bar.cs:
public class Bar
{
private readonly FooOptions _fooOptions;

// .Value in ctor is fine only if it's always ever a non-changing value (no reload and/or no scoped resolution)
public Bar(IOptions<FooOptions> fooOptions)
=> _fooOptions = fooOptions.Value;
}
public class Bar
{
private readonly FooOptions _fooOptions;

// .Value in ctor is fine only if it's always ever a non-changing value (no reload and/or no scoped resolution)
public Bar(IOptions<FooOptions> fooOptions)
=> _fooOptions = fooOptions.Value;
}
Unknown User
Unknown User2mo ago
Message Not Public
Sign In & Join Server To View
Mr. Dink
Mr. Dink2mo ago
Okay, I made some progress on it. HashiCorp returns the secrets in a Dictionary<string, object> or a custom object if provided. The problem I'm on is I can't find any documentation on how to bind that to a configuration. The closest I could come up with is a json stream, by serializing a custom object then passing that in as a memory stream but that seems like a hack.
Unknown User
Unknown User2mo ago
Message Not Public
Sign In & Join Server To View
Mr. Dink
Mr. Dink5w ago
Ah. Didn't realize someone else closed it. No worries. I'll figure something out.
Want results from more Discord servers?
Add your server