Skip to content

Commit a39f2fd

Browse files
committed
ConfigurationParser used in ParsableRetrievalStrategy now uses factory method and lives transiently during Parse call only
1 parent 4ba6b6a commit a39f2fd

12 files changed

Lines changed: 230 additions & 74 deletions

File tree

src/ConfigurationRepository.Dapper/DependencyInjection/ConfigurationBuilderExtensions.cs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,6 @@ public static IConfigurationBuilder AddDapperRepository(
2727
configureRepository.Invoke(repository);
2828
configureSource?.Invoke(source);
2929

30-
source.RetrievalStrategy ??= DictionaryRetrievalStrategy.Instance;
31-
3230
return builder.Add(source);
3331
}
3432

@@ -52,8 +50,6 @@ public static IConfigurationBuilder AddDapperJsonRepository(
5250
configureRepository.Invoke(repository);
5351
configureSource?.Invoke(source);
5452

55-
source.ConfigurationParser ??= new JsonConfigurationParser();
56-
5753
return builder.Add(source);
5854
}
5955
}

src/ConfigurationRepository.Dapper/README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ Let's assume that we have a database that stores a configuration table as key-va
1616
1717
So then in our application Program.cs file we may add a configuration provider like this:
1818
> ```csharp
19+
> using ConfigurationRepository;
20+
>
1921
> var builder = WebApplication.CreateBuilder(args);
2022
>
2123
> var connectionString = builder.Configuration.GetConnectionString("Dapper");
@@ -37,6 +39,8 @@ So then in our application Program.cs file we may add a configuration provider l
3739
3840
If our database source can change at any time in any way we may also add configuration reloader that with periodically reload our configuration from database:
3941
> ```csharp
42+
> using ConfigurationRepository;
43+
>
4044
> var builder = WebApplication.CreateBuilder(args);
4145
>
4246
> var connectionString = builder.Configuration.GetConnectionString("Dapper");
@@ -78,6 +82,8 @@ What if our config in database is too heavy to reload it frequently and we want
7882
7983
Then we make our configuration versioned by adding SelectCurrentVersionQuery to our repository:
8084
> ```csharp
85+
> using ConfigurationRepository;
86+
>
8187
> var builder = WebApplication.CreateBuilder(args);
8288
>
8389
> var connectionString = builder.Configuration.GetConnectionString("Dapper");

src/ConfigurationRepository.EntityFramework/DependencyInjection/ConfigurationBuilderExtensions.cs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@ public static IConfigurationBuilder AddEfCoreRepository(
4646
new RepositoryDbContext(options));
4747

4848
source.Repository = repository;
49-
source.RetrievalStrategy = DictionaryRetrievalStrategy.Instance;
5049

5150
configureSource?.Invoke(source);
5251

@@ -98,8 +97,6 @@ public static IConfigurationBuilder AddEfCoreJsonRepository(
9897
};
9998

10099
source.Repository = repository;
101-
source.ConfigurationParser = new JsonConfigurationParser();
102-
103100
configureSource?.Invoke(source);
104101

105102
return builder.Add(source);

src/ConfigurationRepository.SqlClient/DependencyInjection/ConfigurationBuilderExtensions.cs

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,13 @@ public static IConfigurationBuilder AddSqlClientRepository(
2121
{
2222
var source = new ConfigurationRepositorySource();
2323
var repository = new SqlClientDictionaryConfigurationRepository();
24+
repository.TryAddConnectionString(builder);
2425

2526
source.Repository = repository;
2627

2728
configureRepository.Invoke(repository);
2829
configureSource?.Invoke(source);
2930

30-
source.RetrievalStrategy ??= DictionaryRetrievalStrategy.Instance;
31-
3231
return builder.Add(source);
3332
}
3433

@@ -46,14 +45,26 @@ public static IConfigurationBuilder AddSqlClientJsonRepository(
4645
{
4746
var source = new ParsableConfigurationRepositorySource();
4847
var repository = new SqlClientParsableConfigurationRepository();
48+
repository.TryAddConnectionString(builder);
4949

5050
source.Repository = repository;
5151

5252
configureRepository.Invoke(repository);
5353
configureSource?.Invoke(source);
5454

55-
source.ConfigurationParser ??= new JsonConfigurationParser();
56-
5755
return builder.Add(source);
5856
}
57+
58+
private static SqlClientConfigurationRepository TryAddConnectionString(
59+
this SqlClientConfigurationRepository repository,
60+
IConfigurationBuilder builder)
61+
{
62+
var connectionString = builder.GetDatabaseConnectionString();
63+
if (connectionString is not null)
64+
{
65+
repository.ConnectionString = connectionString;
66+
}
67+
68+
return repository;
69+
}
5970
}

src/ConfigurationRepository/DependencyInjection/ConfigurationBuilderExtensions.cs

Lines changed: 124 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ public static class ConfigurationBuilderExtensions
1313
/// <param name="builder">A configuration builder instance for adding <see cref="IRepository"/> or it`s descendant.</param>
1414
/// <param name="repository">An <see cref="IRepository"/> object.</param>
1515
/// <param name="configureSource">If set, configures <see cref="ConfigurationRepositorySource"/>.</param>
16-
/// <returns>The <see cref="IConfigurationBuilder"/>.</returns>
16+
/// <returns>An <see cref="IConfigurationBuilder"/>.</returns>
1717
public static IConfigurationBuilder AddRepository<TSource>(
1818
this IConfigurationBuilder builder,
1919
IRepository repository,
@@ -30,13 +30,67 @@ public static IConfigurationBuilder AddRepository<TSource>(
3030
return builder;
3131
}
3232

33+
/// <summary>
34+
/// Adds an <see cref="IRepository"/> object to <paramref name="builder"/> with <see cref="DictionaryRetrievalStrategy"/>.
35+
/// </summary>
36+
/// <param name="builder">A configuration builder instance for adding <see cref="IRepository"/> or it`s descendant.</param>
37+
/// <param name="repository">An <see cref="IRepository"/> object.</param>
38+
/// <param name="configureSource">If set, configures <see cref="ConfigurationRepositorySource"/>.</param>
39+
/// <returns>An <see cref="IConfigurationBuilder"/>.</returns>
40+
public static IConfigurationBuilder AddDictionaryRepository<TSource>(
41+
this IConfigurationBuilder builder,
42+
IRepository repository,
43+
Action<TSource>? configureSource = null)
44+
where TSource : ConfigurationRepositorySource, new()
45+
{
46+
var source = new TSource
47+
{
48+
Repository = repository,
49+
RetrievalStrategy = DictionaryRetrievalStrategy.Instance
50+
};
51+
configureSource?.Invoke(source);
52+
53+
builder.Add(source);
54+
return builder;
55+
}
56+
57+
/// <summary>
58+
/// Adds an <see cref="IRepository"/> object to <paramref name="builder"/> with <see cref="ParsableRetrievalStrategy"/>.
59+
/// </summary>
60+
/// <param name="builder">A configuration builder instance for adding <see cref="IRepository"/> or it`s descendant.</param>
61+
/// <param name="repository">An <see cref="IRepository"/> object.</param>
62+
/// <param name="parserFactory">A factory method that returns an instance of
63+
/// configuration parser to be used for parsing data being loaded from repository. If not specified then a factory
64+
/// creating instance of <see cref="JsonConfigurationParser"/> is used by default.</param>
65+
/// <param name="configureSource">If set, configures <see cref="ConfigurationRepositorySource"/>.</param>
66+
/// <returns>An <see cref="IConfigurationBuilder"/>.</returns>
67+
public static IConfigurationBuilder AddParsableRepository<TSource>(
68+
this IConfigurationBuilder builder,
69+
IRepository repository,
70+
Func<IConfigurationParser>? parserFactory = null,
71+
Action<TSource>? configureSource = null)
72+
where TSource : ConfigurationRepositorySource, new()
73+
{
74+
parserFactory ??= () => new JsonConfigurationParser();
75+
76+
var source = new TSource
77+
{
78+
Repository = repository,
79+
RetrievalStrategy = new ParsableRetrievalStrategy(parserFactory)
80+
};
81+
configureSource?.Invoke(source);
82+
83+
builder.Add(source);
84+
return builder;
85+
}
86+
3387
/// <summary>
3488
/// Adds an <see cref="IRepository"/> object to <paramref name="builder"/>.
3589
/// </summary>
3690
/// <param name="builder">A configuration builder instance for adding <see cref="IRepository"/>.</param>
3791
/// <param name="repository">An <see cref="IRepository"/> object.</param>
3892
/// <param name="configureSource">If set, configures <see cref="ConfigurationRepositorySource"/>.</param>
39-
/// <returns>The <see cref="IConfigurationBuilder"/>.</returns>
93+
/// <returns>An <see cref="IConfigurationBuilder"/>.</returns>
4094
public static IConfigurationBuilder AddRepository(
4195
this IConfigurationBuilder builder,
4296
IRepository repository,
@@ -45,12 +99,46 @@ public static IConfigurationBuilder AddRepository(
4599
return builder.AddRepository<ConfigurationRepositorySource>(repository, configureSource);
46100
}
47101

102+
/// <summary>
103+
/// Adds an <see cref="IRepository"/> object to <paramref name="builder"/> with <see cref="DictionaryRetrievalStrategy"/>.
104+
/// </summary>
105+
/// <param name="builder">A configuration builder instance for adding <see cref="IRepository"/>.</param>
106+
/// <param name="repository">An <see cref="IRepository"/> object.</param>
107+
/// <param name="configureSource">If set, configures <see cref="ConfigurationRepositorySource"/>.</param>
108+
/// <returns>An <see cref="IConfigurationBuilder"/>.</returns>
109+
public static IConfigurationBuilder AddDictionaryRepository(
110+
this IConfigurationBuilder builder,
111+
IRepository repository,
112+
Action<ConfigurationRepositorySource>? configureSource = null)
113+
{
114+
return builder.AddDictionaryRepository<ConfigurationRepositorySource>(repository, configureSource);
115+
}
116+
117+
/// <summary>
118+
/// Adds an <see cref="IRepository"/> object to <paramref name="builder"/> with <see cref="DictionaryRetrievalStrategy"/>.
119+
/// </summary>
120+
/// <param name="builder">A configuration builder instance for adding <see cref="IRepository"/>.</param>
121+
/// <param name="repository">An <see cref="IRepository"/> object.</param>
122+
/// <param name="parserFactory">A factory method that returns an instance of
123+
/// configuration parser to be used for parsing data being loaded from repository. If not specified then a factory
124+
/// creating instance of <see cref="JsonConfigurationParser"/> is used by default.</param>
125+
/// <param name="configureSource">If set, configures <see cref="ConfigurationRepositorySource"/>.</param>
126+
/// <returns>An <see cref="IConfigurationBuilder"/>.</returns>
127+
public static IConfigurationBuilder AddParsableRepository(
128+
this IConfigurationBuilder builder,
129+
IRepository repository,
130+
Func<IConfigurationParser>? parserFactory = null,
131+
Action<ConfigurationRepositorySource>? configureSource = null)
132+
{
133+
return builder.AddParsableRepository<ConfigurationRepositorySource>(repository, parserFactory, configureSource);
134+
}
135+
48136
/// <summary>
49137
/// Sets a default action to be invoked for repository providers when an error occurs.
50138
/// </summary>
51139
/// <param name="builder">A configuration builder instance for adding property with handler.</param>
52-
/// <param name="handler">The Action to be invoked on a database load exception.</param>
53-
/// <returns>The <see cref="IConfigurationBuilder"/>.</returns>
140+
/// <param name="handler">An Action to be invoked on a database load exception.</param>
141+
/// <returns>An <see cref="IConfigurationBuilder"/>.</returns>
54142
public static IConfigurationBuilder SetRepositoryLoadExceptionHandler(this IConfigurationBuilder builder,
55143
Action<RepositoryLoadExceptionContext> handler)
56144
{
@@ -63,8 +151,8 @@ public static IConfigurationBuilder SetRepositoryLoadExceptionHandler(this IConf
63151
/// <summary>
64152
/// Gets a default action to be invoked for repository providers when an error occurs.
65153
/// </summary>
66-
/// <param name="builder">The <see cref="IConfigurationBuilder"/>.</param>
67-
/// <returns>The Action to be invoked on a database load exception, if set.</returns>
154+
/// <param name="builder">An <see cref="IConfigurationBuilder"/>.</param>
155+
/// <returns>An <see cref="Action{RepositoryLoadExceptionContext}"/> to be invoked on a database load exception, if set.</returns>
68156
public static Action<RepositoryLoadExceptionContext>? GetRepositoryLoadExceptionHandler(this IConfigurationBuilder builder)
69157
{
70158
_ = builder ?? throw new ArgumentNullException(nameof(builder));
@@ -75,40 +163,45 @@ public static IConfigurationBuilder SetRepositoryLoadExceptionHandler(this IConf
75163
}
76164

77165
/// <summary>
78-
/// Sets a configuration parser to be used parsing data being loaded from repository.
166+
/// Sets a <see cref="IConfigurationParser"/> factory that returns an instance
167+
/// of configuration parser to be used for parsing data being loaded from repository.
79168
/// </summary>
80-
/// <param name="builder">The <see cref="IConfigurationBuilder"/> to add to.</param>
81-
/// <param name="parser">The configuration parser to be used parsing load data from repository.</param>
82-
/// <returns>The <see cref="IConfigurationBuilder"/>.</returns>
83-
public static IConfigurationBuilder SetParsableConfigurationParser(this IConfigurationBuilder builder,
84-
IConfigurationParser parser)
169+
/// <param name="builder">An <see cref="IConfigurationBuilder"/> to add to.</param>
170+
/// <param name="parserFactory">A factory method that returns an instance of
171+
/// configuration parser to be used for parsing data being loaded from repository.</param>
172+
/// <returns>An <see cref="IConfigurationBuilder"/>.</returns>
173+
public static IConfigurationBuilder SetConfigurationParserFactory(this IConfigurationBuilder builder,
174+
Func<IConfigurationParser> parserFactory)
85175
{
86176
_ = builder ?? throw new ArgumentNullException(nameof(builder));
87177

88-
builder.Properties[ParsableConfigurationParserKey] = parser;
178+
builder.Properties[ConfigurationParserFactoryKey] = parserFactory;
89179
return builder;
90180
}
91181

92182
/// <summary>
93-
/// Gets a configuration parser to be used when parsing configuration data being loaded from database.
94-
/// If no parser is set then JsonConfigurationParser is used by default.
183+
/// Gets a <see cref="IConfigurationParser"/> factory from <paramref name="builder"/>
184+
/// properties. That factory returns an instanceof configuration parser to be used
185+
/// for parsing data being loaded from repository. If no one is found then a factory
186+
/// creating instance of <see cref="JsonConfigurationParser"/> is used by default.
95187
/// </summary>
96-
/// <param name="builder">The <see cref="IConfigurationBuilder"/>.</param>
97-
/// <returns>The configuration parser to be used parsing load data from database.</returns>
98-
public static IConfigurationParser GetParsableConfigurationParser(this IConfigurationBuilder builder)
188+
/// <param name="builder">An <see cref="IConfigurationBuilder"/>.</param>
189+
/// <returns>A factory method that returns an instance of
190+
/// configuration parser to be used for parsing data being loaded from repository.</returns>
191+
public static Func<IConfigurationParser>? GetConfigurationParserFactory(this IConfigurationBuilder builder)
99192
{
100193
_ = builder ?? throw new ArgumentNullException(nameof(builder));
101194

102-
return (builder.Properties.TryGetValue(ParsableConfigurationParserKey, out object? parser)
103-
? (IConfigurationParser)parser : null) ?? new JsonConfigurationParser();
195+
return (builder.Properties.TryGetValue(ConfigurationParserFactoryKey, out object? parserFactory)
196+
? (Func<IConfigurationParser>)parserFactory : null) ?? (() => new JsonConfigurationParser());
104197
}
105198

106199
/// <summary>
107200
/// Sets connection string that will be used to connect to the database.
108201
/// </summary>
109-
/// <param name="builder">The <see cref="IConfigurationBuilder"/> to add to.</param>
110-
/// <param name="connectionString">The connection string to connect to the database.</param>
111-
/// <returns>The <see cref="IConfigurationBuilder"/>.</returns>
202+
/// <param name="builder">An <see cref="IConfigurationBuilder"/> to add to.</param>
203+
/// <param name="connectionString">A connection string to connect to the database.</param>
204+
/// <returns>An <see cref="IConfigurationBuilder"/>.</returns>
112205
public static IConfigurationBuilder SetDatabaseConnectionString(this IConfigurationBuilder builder,
113206
string connectionString)
114207
{
@@ -121,8 +214,8 @@ public static IConfigurationBuilder SetDatabaseConnectionString(this IConfigurat
121214
/// <summary>
122215
/// Gets connection string that will be used to connect to the database.
123216
/// </summary>
124-
/// <param name="builder">The <see cref="IConfigurationBuilder"/>.</param>
125-
/// <returns>The connection string to connect to the database.</returns>
217+
/// <param name="builder">An <see cref="IConfigurationBuilder"/>.</param>
218+
/// <returns>A connection string to connect to the database.</returns>
126219
public static string? GetDatabaseConnectionString(this IConfigurationBuilder builder)
127220
{
128221
_ = builder ?? throw new ArgumentNullException(nameof(builder));
@@ -134,8 +227,8 @@ public static IConfigurationBuilder SetDatabaseConnectionString(this IConfigurat
134227
/// <summary>
135228
/// Gets the <see cref="IRepository"/> that will be used to store configurations.
136229
/// </summary>
137-
/// <param name="builder">The <see cref="IConfigurationBuilder"/>.</param>
138-
/// <returns>The <see cref="IRepository"/>.</returns>
230+
/// <param name="builder">An <see cref="IConfigurationBuilder"/>.</param>
231+
/// <returns>An <see cref="IRepository"/>.</returns>
139232
public static IRepository? GetConfigurationRepository(this IConfigurationBuilder builder)
140233
{
141234
_ = builder ?? throw new ArgumentNullException(nameof(builder));
@@ -144,8 +237,8 @@ public static IConfigurationBuilder SetDatabaseConnectionString(this IConfigurat
144237
? (IRepository)repository : null;
145238
}
146239

147-
private const string RepositoryKey = "ConfigurationRepository:Repository";
148-
private const string RepositoryLoadExceptionHandlerKey = "ConfigurationRepository:Repository:LoadExceptionHandler";
149-
private const string ParsableConfigurationParserKey = "ConfigurationRepository:ParsableConfiguration:Parser";
150-
private const string RepositoryDatabaseConnectionStringKey = "ConfigurationRepository:Repository:DatabaseConnectionString";
240+
private const string RepositoryKey = "ConfigurationRepository:Key";
241+
private const string RepositoryLoadExceptionHandlerKey = "ConfigurationRepository:LoadExceptionHandler";
242+
private const string ConfigurationParserFactoryKey = "ConfigurationRepository:ParserFactory";
243+
private const string RepositoryDatabaseConnectionStringKey = "ConfigurationRepository:DatabaseConnectionString";
151244
}

src/ConfigurationRepository/README.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,40 @@ Install-Package ConfigurationRepository
1515
```
1616
dotnet add package ConfigurationRepository
1717
```
18+
```csharp
19+
using ConfigurationRepository;
20+
21+
var builder = WebApplication.CreateBuilder(args);
22+
23+
var configDictData = new Dictionary<string, string?>(StringComparer.OrdinalIgnoreCase)
24+
{ { "DICT KEY", "DICT VALUE" } };
25+
26+
builder.Configuration.AddDictionaryRepository(new InMemoryDictionaryRepository(configDictData));
27+
28+
var configJsonData = """{"JSON KEY":"JSON VALUE"}""";
29+
30+
builder.Configuration.AddParsableRepository(new InMemoryJsonRepository(configJsonData));
31+
32+
var app = builder.Build();
33+
34+
app.Run();
35+
36+
class InMemoryDictionaryRepository(IDictionary<string, string?> configData) : IRepository
37+
{
38+
public TData GetConfiguration<TData>()
39+
{
40+
return (TData)configData;
41+
}
42+
}
43+
44+
class InMemoryJsonRepository(string jsonConfig) : IRepository
45+
{
46+
public TData GetConfiguration<TData>()
47+
{
48+
return (TData)Convert.ChangeType(jsonConfig, typeof(TData));
49+
}
50+
}
51+
```
1852

1953
# [Dapper configuration repository](/src/ConfigurationRepository.Dapper/README.md)
2054

0 commit comments

Comments
 (0)