diff --git a/Coder.Test/ServiceCollectionExtensionsTests.cs b/Coder.Test/ServiceCollectionExtensionsTests.cs index 0f90ce0..5707c51 100644 --- a/Coder.Test/ServiceCollectionExtensionsTests.cs +++ b/Coder.Test/ServiceCollectionExtensionsTests.cs @@ -200,7 +200,15 @@ public void AddCoder_ShouldBeIdempotent() YamlSerializer? serializer = serviceProvider.GetService(); YamlDeserializer? deserializer = serviceProvider.GetService(); - Assert.IsTrue(generators.Count > 0, "Generators should still be registered"); + Assert.HasCount(7, generators, "Each generator should be registered once however often AddCoder is called"); + foreach (string languageId in new[] { "python", "csharp", "javascript", "cpp", "c", "rust", "go" }) + { + ILanguageGenerator generator = generators.Single(g => g.LanguageId == languageId); + Assert.IsNotNull(generator, $"{languageId} should resolve to exactly one generator"); + } + + Assert.HasCount(1, serviceProvider.GetServices(), "Serializer should be registered once"); + Assert.HasCount(1, serviceProvider.GetServices(), "Deserializer should be registered once"); Assert.IsNotNull(serializer, "Serializer should still be registered"); Assert.IsNotNull(deserializer, "Deserializer should still be registered"); } diff --git a/Coder/ServiceCollectionExtensions.cs b/Coder/ServiceCollectionExtensions.cs index ead643d..843ec0f 100644 --- a/Coder/ServiceCollectionExtensions.cs +++ b/Coder/ServiceCollectionExtensions.cs @@ -4,6 +4,7 @@ namespace ktsu.Coder; using ktsu.Coder.Languages; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; /// /// Extension methods for configuring Coder services in dependency injection containers. @@ -19,14 +20,15 @@ public static IServiceCollection AddLanguageGenerators(this IServiceCollection s { Ensure.NotNull(services); - // Register all available language generators - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); + // Register all available language generators. TryAddEnumerable skips a generator that is + // already registered, so calling this twice still resolves one of each. + services.TryAddEnumerable(ServiceDescriptor.Singleton()); + services.TryAddEnumerable(ServiceDescriptor.Singleton()); + services.TryAddEnumerable(ServiceDescriptor.Singleton()); + services.TryAddEnumerable(ServiceDescriptor.Singleton()); + services.TryAddEnumerable(ServiceDescriptor.Singleton()); + services.TryAddEnumerable(ServiceDescriptor.Singleton()); + services.TryAddEnumerable(ServiceDescriptor.Singleton()); return services; } @@ -40,8 +42,8 @@ public static IServiceCollection AddCoderSerialization(this IServiceCollection s { Ensure.NotNull(services); - services.AddSingleton(); - services.AddSingleton(); + services.TryAddSingleton(); + services.TryAddSingleton(); return services; }