diff --git a/src/Scrutor/RegistrationStrategy.cs b/src/Scrutor/RegistrationStrategy.cs index 4894384..adff036 100644 --- a/src/Scrutor/RegistrationStrategy.cs +++ b/src/Scrutor/RegistrationStrategy.cs @@ -1,14 +1,27 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; +using System.Linq; namespace Scrutor; public abstract class RegistrationStrategy { /// - /// Skips registrations for services that already exists. + /// Skips registrations for services that already exist using . /// - public static readonly RegistrationStrategy Skip = new SkipRegistrationStrategy(); + public static RegistrationStrategy Skip() + { + return new SkipRegistrationStrategy(SkipBehavior.Default); + } + + /// + /// Skips registrations for services that already exist based on the specified . + /// + /// The behavior to use when replacing services. + public static RegistrationStrategy Skip(SkipBehavior behavior) + { + return new SkipRegistrationStrategy(behavior); + } /// /// Appends a new registration for existing services. @@ -38,7 +51,7 @@ public static RegistrationStrategy Replace(ReplacementBehavior behavior) } /// - /// Applies the the to the . + /// Applies the selected for the to the . /// /// The service collection. /// The descriptor to apply. @@ -46,7 +59,31 @@ public static RegistrationStrategy Replace(ReplacementBehavior behavior) private sealed class SkipRegistrationStrategy : RegistrationStrategy { - public override void Apply(IServiceCollection services, ServiceDescriptor descriptor) => services.TryAdd(descriptor); + public SkipRegistrationStrategy(SkipBehavior behavior) + { + Behavior = behavior; + } + + private SkipBehavior Behavior { get; } + + public override void Apply(IServiceCollection services, ServiceDescriptor descriptor) + { + if (Behavior is SkipBehavior.Default or SkipBehavior.ServiceType) + { + services.TryAdd(descriptor); + } + else if (Behavior is SkipBehavior.ImplementationType) + { + if (!services.Any((ServiceDescriptor d) => d.ImplementationType == descriptor.ImplementationType)) + { + services.Add(descriptor); + } + } + else + { + services.TryAddEnumerable(descriptor); + } + } } private sealed class AppendRegistrationStrategy : RegistrationStrategy @@ -110,4 +147,4 @@ public override void Apply(IServiceCollection services, ServiceDescriptor descri services.Add(descriptor); } } -} \ No newline at end of file +} diff --git a/src/Scrutor/ReplacementBehavior.cs b/src/Scrutor/ReplacementBehavior.cs index 23aaff4..543110c 100644 --- a/src/Scrutor/ReplacementBehavior.cs +++ b/src/Scrutor/ReplacementBehavior.cs @@ -21,7 +21,7 @@ public enum ReplacementBehavior ImplementationType = 2, /// - /// Replace existing services by either service- or implementation type. + /// Replace existing services by either service or implementation type. /// All = ServiceType | ImplementationType -} \ No newline at end of file +} diff --git a/src/Scrutor/SkipBehavior.cs b/src/Scrutor/SkipBehavior.cs new file mode 100644 index 0000000..e2da658 --- /dev/null +++ b/src/Scrutor/SkipBehavior.cs @@ -0,0 +1,28 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; + +namespace Scrutor; + +public enum SkipBehavior +{ + /// + /// Skip registration if the service type has already been registered. Same as . + /// + Default = 0, + + /// + /// Skip registration if the service type has already been registered (default). Same as . + /// + ServiceType = 1, + + /// + /// Skip registration if the implementation type has already been registered. + /// + ImplementationType = 2, + + /// + /// Skip registration if a descriptor with the same and implementation has already been registered. + /// + Exact = 3, +} + diff --git a/test/Scrutor.Tests/ScanningTests.cs b/test/Scrutor.Tests/ScanningTests.cs index 3627a6d..306b4cc 100644 --- a/test/Scrutor.Tests/ScanningTests.cs +++ b/test/Scrutor.Tests/ScanningTests.cs @@ -48,21 +48,86 @@ public void UsingRegistrationStrategy_None() } [Fact] - public void UsingRegistrationStrategy_SkipIfExists() + public void UsingRegistrationStrategy_SkipDefault() { Collection.Scan(scan => scan .FromAssemblyOf() .AddClasses(classes => classes.AssignableTo()) + .UsingRegistrationStrategy(RegistrationStrategy.Skip()) .AsImplementedInterfaces() .WithTransientLifetime() .AddClasses(classes => classes.AssignableTo()) - .UsingRegistrationStrategy(RegistrationStrategy.Skip) + .UsingRegistrationStrategy(RegistrationStrategy.Skip()) .AsImplementedInterfaces() .WithSingletonLifetime()); var services = Collection.GetDescriptors(); - Assert.Equal(4, services.Count(x => x.ServiceType == typeof(ITransientService))); + Assert.Equal(1, services.Count(x => x.Lifetime == ServiceLifetime.Transient)); + Assert.Equal(0, services.Count(x => x.Lifetime == ServiceLifetime.Singleton)); + } + + [Fact] + public void UsingRegistrationStrategy_SkipServiceTypes() + { + Collection.Scan(scan => scan + .FromAssemblyOf() + .AddClasses(classes => classes.AssignableTo()) + .UsingRegistrationStrategy(RegistrationStrategy.Skip(SkipBehavior.ServiceType)) + .AsImplementedInterfaces() + .WithTransientLifetime() + .AddClasses(classes => classes.AssignableTo()) + .UsingRegistrationStrategy(RegistrationStrategy.Skip(SkipBehavior.ServiceType)) + .AsImplementedInterfaces() + .WithSingletonLifetime()); + + var services = Collection.GetDescriptors(); + + Assert.Equal(1, services.Count(x => x.Lifetime == ServiceLifetime.Transient)); + Assert.Equal(0, services.Count(x => x.Lifetime == ServiceLifetime.Singleton)); + } + + [Fact] + public void UsingRegistrationStrategy_SkipImplementationTypes() + { + Collection.Scan(scan => scan + .FromAssemblyOf() + .AddClasses(classes => classes.AssignableTo()) + .UsingRegistrationStrategy(RegistrationStrategy.Skip(SkipBehavior.ImplementationType)) + .AsImplementedInterfaces() + .WithTransientLifetime() + .AddClasses(classes => classes.AssignableTo()) + .UsingRegistrationStrategy(RegistrationStrategy.Skip(SkipBehavior.ImplementationType)) + .AsImplementedInterfaces() + .WithSingletonLifetime()); + + + var services = Collection.GetDescriptors(); + + Assert.Equal(4, services.Count(x => x.Lifetime == ServiceLifetime.Transient)); + Assert.Equal(0, services.Count(x => x.Lifetime == ServiceLifetime.Singleton)); + Assert.Equal(0, services.Count(x => x.ServiceType == typeof(IOtherInheritance))); + } + + [Fact] + public void UsingRegistrationStrategy_SkipExactTypes() + { + Collection.Scan(scan => scan + .FromAssemblyOf() + .AddClasses(classes => classes.AssignableTo()) + .UsingRegistrationStrategy(RegistrationStrategy.Skip(SkipBehavior.Exact)) + .AsImplementedInterfaces() + .WithTransientLifetime() + .AddClasses(classes => classes.AssignableTo()) + .UsingRegistrationStrategy(RegistrationStrategy.Skip(SkipBehavior.Exact)) + .AsImplementedInterfaces() + .WithSingletonLifetime()); + + var services = Collection.GetDescriptors(); + + Assert.Equal(4, services.Count(x => x.Lifetime == ServiceLifetime.Transient)); + Assert.Equal(0, services.Count(x => x.Lifetime == ServiceLifetime.Singleton)); + Assert.Equal(1, Collection.Count(x => x.ServiceType == typeof(IOtherInheritance))); } [Fact] @@ -558,7 +623,7 @@ public interface IOtherInheritance { } [ServiceDescriptor(typeof(IDuplicateInheritance))] [ServiceDescriptor(typeof(IDuplicateInheritance))] public class DuplicateInheritance : IDuplicateInheritance, IOtherInheritance { } - + public interface IDefault1 { } public interface IDefault2 { } @@ -573,7 +638,7 @@ public class DefaultAttributes : IDefault3Level2, IDefault1, IDefault2 { } [CompilerGenerated] public class CompilerGenerated { } - public class CombinedService2: IDefault1, IDefault2, IDefault3Level2 { } + public class CombinedService2 : IDefault1, IDefault2, IDefault3Level2 { } } namespace Scrutor.Tests.ChildNamespace