Skip to content

Commit 5297fce

Browse files
committed
fix(NamedOptions): Switch to use KeyedService(Microsoft) instead of NamedServices (Uno) so the NamedOptions Resolution at runtime does Work finally
1 parent 81ba56e commit 5297fce

15 files changed

+65
-51
lines changed

src/DevTKSS.Uno.Samples.MvuxGallery/App.xaml.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ protected async override void OnLaunched(LaunchActivatedEventArgs args)
7474
//.Section<CodeSampleOptions>("DashboardSamples")
7575
.Section<CodeSampleOptions>("NavigationSamples")
7676
.Section<CodeSampleOptions>("ListboardSamples")
77-
.Section<CodeSampleOptions>("SimpleCardsSamples")
77+
.Section<CodeSampleOptions>("SimpleCardSamples")
7878
.Section<CodeSampleOptions>("CounterSamples");
7979

8080
})
@@ -87,10 +87,10 @@ protected async override void OnLaunched(LaunchActivatedEventArgs args)
8787
.AddSingleton<IGalleryImageService, GalleryImageService>()
8888

8989
//.AddNamedConfiguredSingletonCodeService("DashboardSamples")
90-
.AddNamedConfiguredSingletonCodeService("NavigationSamples")
91-
.AddNamedConfiguredSingletonCodeService("ListboardSamples")
92-
.AddNamedConfiguredSingletonCodeService("SimpleCardsSamples")
93-
.AddNamedConfiguredSingletonCodeService("CounterSamples")
90+
.AddKeyedSingletonCodeService("NavigationSamples")
91+
.AddKeyedSingletonCodeService("ListboardSamples")
92+
.AddKeyedSingletonCodeService("SimpleCardSamples")
93+
.AddKeyedSingletonCodeService("CounterSamples")
9494
)
9595
.UseNavigation(ReactiveViewModelMappings.ViewModelMappings, RegisterRoutes)
9696
.UseSerialization((context, services) =>

src/DevTKSS.Uno.Samples.MvuxGallery/DevTKSS.Uno.Samples.MvuxGallery.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<Project Sdk="Uno.Sdk">
22
<PropertyGroup>
3-
<TargetFrameworks>net9.0-desktop</TargetFrameworks> <!--;net9.0-windows10.0.26100-->
3+
<TargetFrameworks>net9.0-desktop;net9.0</TargetFrameworks> <!--;net9.0-windows10.0.26100-->
44
<!-- BUG: Windows target unable to build when using ResourcesDictionary imported in App.xaml see https://github.com/DevTKSS/DevTKSS.Uno.SampleApps/issues/15 for more information-->
55
<!-- <TargetPlatformMinVersion>10.0.17763.0</TargetPlatformMinVersion>-->
66
<OutputType>Exe</OutputType>

src/DevTKSS.Uno.Samples.MvuxGallery/Models/CodeSamples/CodeSample.cs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,12 @@ public record CodeSample
66
public string FilePath { get; init; } = string.Empty;
77
public Lines[] LineRanges { get; init; } = [];
88
}
9-
public record Lines
10-
{
11-
public int Start { get; init; }
12-
public int End { get; init; }
13-
}
149

1510
[JsonSerializable(typeof(CodeSampleOptions))]
1611
[JsonSerializable(typeof(CodeSample))]
1712
[JsonSerializable(typeof(Lines))]
1813
[JsonSerializable(typeof(CodeSample[]))]
14+
[JsonSerializable(typeof(IEnumerable<CodeSample>))]
1915
public partial class CodeSampleOptionsContext : JsonSerializerContext
2016
{
2117
}

src/DevTKSS.Uno.Samples.MvuxGallery/Models/CodeSamples/CodeSampleOptions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@ namespace DevTKSS.Uno.Samples.MvuxGallery.Models.CodeSamples;
22

33
public record CodeSampleOptions
44
{
5-
public CodeSample[] Samples { get; init; } = Array.Empty<CodeSample>();
5+
public CodeSample[] Samples { get; init; } = [];
66
}
77

src/DevTKSS.Uno.Samples.MvuxGallery/Models/CodeSamples/CodeSampleService.cs

Lines changed: 24 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,31 +3,37 @@ public partial record CodeSampleService : ICodeSampleService
33
{
44
private readonly IStorage _storage;
55
private readonly ILogger<CodeSampleService> _logger;
6-
private readonly string _serviceName;
6+
77
private CodeSampleOptions _options;
8-
public CodeSampleService(string serviceName,
8+
9+
public string Name { get; init; }
10+
11+
public CodeSampleService(
12+
string name,
913
IOptionsMonitor<CodeSampleOptions> options,
1014
ILogger<CodeSampleService> logger,
1115
IStorage storage)
1216
{
13-
_serviceName = serviceName;
14-
_options = options.Get(_serviceName);
15-
_logger = logger;
16-
_storage = storage;
17+
_logger = logger;
18+
_storage = storage;
19+
Name = name;
20+
_options = options.Get(Name);
21+
if (_logger.IsEnabled(LogLevel.Information))
22+
{
23+
_logger.LogInformation("CodeSampleService created for '{ServiceName}', loaded {SampleCount} samples", Name, _options.Samples.Length);
24+
}
1725

1826
options.OnChange(UpdateOptions);
19-
2027
}
21-
22-
public void UpdateOptions(CodeSampleOptions newOptions, string? changedOption)
28+
public void UpdateOptions(CodeSampleOptions newOptions, string? changedOption)
2329
{
2430
if (_logger.IsEnabled(LogLevel.Debug))
2531
{
2632
_logger.LogDebug("CodeSampleOptions changed for {name}", changedOption);
2733
}
28-
if (changedOption == _serviceName)
34+
if (changedOption == Name)
2935
{
30-
_logger.LogInformation("Updating CodeSampleOptions for {serviceName}", _serviceName);
36+
_logger.LogInformation("Updating CodeSampleOptions for {serviceName}", Name);
3137
_options = newOptions;
3238
}
3339
}
@@ -48,19 +54,19 @@ public void UpdateOptions(CodeSampleOptions newOptions, string? changedOption)
4854
/// <returns>An awaitable <see cref="ValueTask{TResult}"/> providing a <see cref="ImmutableList{T}"/> of <see langword="string"/> with the Sample Names to select from</returns>
4955
public async ValueTask<IImmutableList<string>> GetCodeSampleOptionsAsync(CancellationToken ct = default)
5056
{
51-
await Task.Delay(1);
52-
_logger.LogTrace("Collecting available code sample options from appsettings.sampledata.json...");
57+
await Task.Delay(1, ct);
58+
_logger.LogTrace("Collecting available code sample options for '{ServiceName}' from configuration...", Name );
5359
var sampleOptions = _options.Samples.Select(sample => sample.SampleID).ToImmutableList();
5460

5561
if (_logger.IsEnabled(LogLevel.Debug))
5662
{
5763
// Log available options
58-
_logger.LogDebug("Available Options:\n{options}", sampleOptions.JoinBy("," + Environment.NewLine));
64+
_logger.LogDebug("Available Options for '{ServiceName}':\n{options}", Name, sampleOptions.JoinBy("," + Environment.NewLine));
5965
}
6066
else if (_logger.IsEnabled(LogLevel.Trace))
6167
{
6268
// Log available options
63-
_logger.LogTrace("Gathered {count} Options", sampleOptions.Count);
69+
_logger.LogTrace("Gathered {count} Options for '{ServiceName}'", sampleOptions.Count, Name);
6470
}
6571

6672
return sampleOptions;
@@ -72,7 +78,8 @@ public async ValueTask<string> GetCodeSampleAsync(string? sampleID, Cancellation
7278
{
7379
if(_logger.IsEnabled(LogLevel.Trace))
7480
{
75-
_logger.LogTrace("Fetching Storage Data for SampleID: {sampleID},\nDescription: {description},\nFilePath: {filePath},\nLineRanges: {lineRanges}",
81+
_logger.LogTrace("Fetching Storage Data for Service '{service}', SampleID: {sampleID},\nDescription: {description},\nFilePath: {filePath},\nLineRanges: {lineRanges}",
82+
Name,
7683
sampleOption.SampleID,
7784
sampleOption.Description,
7885
sampleOption.FilePath,
@@ -82,7 +89,7 @@ public async ValueTask<string> GetCodeSampleAsync(string? sampleID, Cancellation
8289
return await _storage.ReadLinesFromPackageFile(sampleOption.FilePath,sampleOption.LineRanges.Select(lr => (lr.Start, lr.End)));
8390
}
8491

85-
_logger.LogError("Code sample with ID {sampleID} not found", sampleID);
92+
_logger.LogError("Code sample with ID {sampleID} not found for service '{service}'", sampleID, Name);
8693
return string.Empty;
8794
}
8895
}

src/DevTKSS.Uno.Samples.MvuxGallery/Models/CodeSamples/ICodeSampleService.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ namespace DevTKSS.Uno.Samples.MvuxGallery.Models.CodeSamples;
33

44
public interface ICodeSampleService
55
{
6+
public string Name { get; }
67
/// <summary>
78
/// Get the content of a specific code sample asynchronously.
89
/// </summary>
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
namespace DevTKSS.Uno.Samples.MvuxGallery.Models.CodeSamples;
2+
3+
public record Lines
4+
{
5+
public int Start { get; init; }
6+
public int End { get; init; }
7+
}

src/DevTKSS.Uno.Samples.MvuxGallery/Models/CodeSamples/ServiceCollectionExtension.cs

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,18 @@ public static class ServiceCollectionExtension
1313
/// <param name="sectionName">The optional configuration section name. If not provided, <paramref name="serviceName"/> is used.</param>
1414
/// <returns>The <see cref="IServiceCollection"/> for chaining.</returns>
1515
/// <remarks>
16-
/// This method binds <see cref="CodeSampleOptions"/> to the configuration section "CodeSamples:{sectionName}" and registers
17-
/// a named singleton <see cref="ICodeSampleService"/> using the configured options.
16+
/// This method assumes configuration for the named <see cref="CodeSampleOptions"/> is provided (e.g., via UseConfiguration().Section<T>()).
17+
/// It registers a keyed singleton <see cref="ICodeSampleService"/> that consumes those named options.
1818
/// </remarks>
19-
public static IServiceCollection AddNamedConfiguredSingletonCodeService(this IServiceCollection services, string serviceName, string? sectionName = null)
19+
public static IServiceCollection AddKeyedSingletonCodeService(this IServiceCollection services, string serviceName, string? sectionName = null)
2020
{
21-
Console.WriteLine($"ServiceName: {serviceName}");
22-
Console.WriteLine($"SectionName: {sectionName}");
23-
// services.AddOptions<CodeSampleOptions>(sectionName ?? serviceName).BindConfiguration<CodeSampleOptions>(sectionName ?? serviceName);
24-
services.AddNamedSingleton<ICodeSampleService, CodeSampleService>(serviceName, sp => sp
25-
.ConfigureCodeSampleService(sectionName ?? serviceName));
21+
var name = sectionName ?? serviceName;
22+
Console.WriteLine($"ServiceName (registration): {serviceName}");
23+
Console.WriteLine($"Effective section/name: {name}");
24+
25+
// Register the keyed service instance that will consume the named options
26+
services.AddKeyedSingleton<ICodeSampleService>(serviceName, (serviceProvider,_) =>
27+
serviceProvider.ConfigureCodeSampleService(name));
2628

2729
return services;
2830
}

src/DevTKSS.Uno.Samples.MvuxGallery/Models/CodeSamples/ServiceProviderExtensions.cs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,21 @@ public static class ServiceProviderExtensions
88
/// Creates and configures an instance of <see cref="CodeSampleService"/> for the specified section.
99
/// </summary>
1010
/// <param name="serviceProvider">The <see cref="IServiceProvider"/> instance used to resolve required services.</param>
11-
/// <param name="sectionName">The name of the section for which the <see cref="CodeSampleService"/> should be configured.</param>
11+
/// <param name="serviceName">The name of the section for which the <see cref="CodeSampleService"/> should be configured.</param>
1212
/// <returns>A configured instance of <see cref="CodeSampleService"/>.</returns>
1313
/// <remarks>
1414
/// This extension method retrieves the necessary dependencies from the service provider and constructs a <see cref="CodeSampleService"/>
1515
/// instance tailored for the given section. It is useful for scenarios where code samples are organized by sections and require
1616
/// contextual configuration.
1717
/// </remarks>
18-
public static CodeSampleService ConfigureCodeSampleService(this IServiceProvider serviceProvider, string sectionName)
18+
public static CodeSampleService ConfigureCodeSampleService(this IServiceProvider serviceProvider, string serviceName)
1919
{
20-
Console.WriteLine($"ServiceName: {sectionName}");
20+
2121
var options = serviceProvider.GetRequiredService<IOptionsMonitor<CodeSampleOptions>>();
2222
var logger = serviceProvider.GetRequiredService<ILogger<CodeSampleService>>();
2323
var storage = serviceProvider.GetRequiredService<IStorage>();
24-
return new (sectionName, options, logger, storage);
24+
logger.LogInformation("ConfigureCodeSampleService -> Name: {serviceName}", serviceName);
25+
return new (serviceName, options, logger, storage);
2526

2627
}
2728
}

src/DevTKSS.Uno.Samples.MvuxGallery/Presentation/ViewModels/CounterModel.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
21
namespace DevTKSS.Uno.Samples.MvuxGallery.Presentation.ViewModels;
32

43
public partial record CounterModel
@@ -12,7 +11,7 @@ public CounterModel(
1211
ILogger<CounterModel> logger)
1312
{
1413
_logger = logger;
15-
_sampleService = serviceProvider.GetRequiredNamedService<ICodeSampleService>("CounterSamples");
14+
_sampleService = serviceProvider.GetRequiredKeyedService<ICodeSampleService>("CounterSamples");
1615
_stringLocalizer = stringLocalizer;
1716
}
1817

0 commit comments

Comments
 (0)