Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 26 additions & 1 deletion src/Scrutor/RegistrationStrategy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,15 @@ namespace Scrutor;
public abstract class RegistrationStrategy
{
/// <summary>
/// Skips registrations for services that already exists.
/// Appends a new registration when no registration exists for the same Service type.
/// </summary>
public static readonly RegistrationStrategy Skip = new SkipRegistrationStrategy();

/// <summary>
/// Appends a new registration when no registration exists for the same Service and Implementation type.
/// </summary>
public static readonly RegistrationStrategy Distinct = new DistinctRegistrationStrategy();

/// <summary>
/// Appends a new registration for existing services.
/// </summary>
Expand Down Expand Up @@ -49,6 +54,26 @@ private sealed class SkipRegistrationStrategy : RegistrationStrategy
public override void Apply(IServiceCollection services, ServiceDescriptor descriptor) => services.TryAdd(descriptor);
}

private sealed class DistinctRegistrationStrategy : RegistrationStrategy {
/// <summary>
/// Adds the service descriptor if the service collection does not contain a desriptor with the same Service and Implementation type.
/// </summary>
/// <param name="services">The service collection.</param>
/// <param name="descriptor">The descriptor to apply.</param>
/// <remarks>
/// Unable to use
/// <see href="https://source.dot.net/#Microsoft.Extensions.DependencyInjection.Abstractions/Extensions/ServiceCollectionDescriptorExtensions.cs,c2d39606abcd4e54,references">TryAddEnumerable()</see>
/// since it would throw an ArgumentException when used with AsSelf().
/// </remarks>
public override void Apply(IServiceCollection services, ServiceDescriptor descriptor)
{
if (services.HasRegistration(descriptor)) {
return;
}
services.Add(descriptor);
}
}

private sealed class AppendRegistrationStrategy : RegistrationStrategy
{
public override void Apply(IServiceCollection services, ServiceDescriptor descriptor) => services.Add(descriptor);
Expand Down
13 changes: 12 additions & 1 deletion src/Scrutor/ServiceCollectionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,15 @@ public static bool HasRegistration(this IServiceCollection services, Type servic
{
return services.Any(x => x.ServiceType == serviceType);
}
}

/// <summary>
/// Determines whether the service collection has a descriptor with the same Service and Implementation types.
/// </summary>
/// <param name="services">The service collection.</param>
/// <param name="descriptor">The service descriptor.</param>
/// <returns><c>true</c> if the service collection contains the specified service descriptor; otherwise, <c>false</c>.</returns>
public static bool HasRegistration(this IServiceCollection services, ServiceDescriptor descriptor)
{
return services.Any(x => x.ServiceType == descriptor.ServiceType && x.ImplementationType == descriptor.ImplementationType);
}
}
95 changes: 94 additions & 1 deletion test/Scrutor.Tests/ScanningTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,30 @@ public void UsingRegistrationStrategy_None()
}

[Fact]
public void UsingRegistrationStrategy_SkipIfExists()
public void UsingRegistrationStrategy_Skip()
{
Collection.Scan(scan => scan
.FromAssemblyOf<ITransientService>()
.AddClasses(classes => classes.AssignableTo<ITransientService>())
.UsingRegistrationStrategy(RegistrationStrategy.Skip)
.AsImplementedInterfaces()
.WithTransientLifetime());

var services = Collection.GetDescriptors<ITransientService>();

Assert.Equal(1, services.Count(x => x.ServiceType == typeof(ITransientService)));
}

[Fact]
public void UsingRegistrationStrategy_SkipAfterNone()
{
Collection.Scan(scan => scan
.FromAssemblyOf<ITransientService>()
// registers 4
.AddClasses(classes => classes.AssignableTo<ITransientService>())
.AsImplementedInterfaces()
.WithTransientLifetime()
// no new registrations
.AddClasses(classes => classes.AssignableTo<ITransientService>())
.UsingRegistrationStrategy(RegistrationStrategy.Skip)
.AsImplementedInterfaces()
Expand All @@ -65,6 +82,82 @@ public void UsingRegistrationStrategy_SkipIfExists()
Assert.Equal(4, services.Count(x => x.ServiceType == typeof(ITransientService)));
}

[Fact]
public void UsingRegistrationStrategy_Distinct()
{
Collection.Scan(scan => scan
.FromAssemblyOf<ITransientService>()
.AddClasses(classes => classes.AssignableTo<ITransientService>())
.UsingRegistrationStrategy(RegistrationStrategy.Distinct)
.AsImplementedInterfaces()
.WithTransientLifetime());

var services = Collection.GetDescriptors<ITransientService>();

Assert.Equal(4, services.Count(x => x.ServiceType == typeof(ITransientService)));
}

[Fact]
public void UsingRegistrationStrategy_DistinctAfterSkip()
{
Collection.Scan(scan => scan
.FromAssemblyOf<ITransientService>()
// registers 1
.AddClasses(classes => classes.AssignableTo<ITransientService>())
.UsingRegistrationStrategy(RegistrationStrategy.Skip)
.AsImplementedInterfaces()
.WithTransientLifetime()
// registers the other three
.AddClasses(classes => classes.AssignableTo<ITransientService>())
.UsingRegistrationStrategy(RegistrationStrategy.Distinct)
.AsImplementedInterfaces()
.WithSingletonLifetime());

var services = Collection.GetDescriptors<ITransientService>();

Assert.Equal(4, services.Count(x => x.ServiceType == typeof(ITransientService)));
}

[Fact]
public void UsingRegistrationStrategy_DistinctAfterNone()
{
Collection.Scan(scan => scan
.FromAssemblyOf<ITransientService>()
// register 4
.AddClasses(classes => classes.AssignableTo<ITransientService>())
.AsImplementedInterfaces()
.WithTransientLifetime()
// no new registrations
.AddClasses(classes => classes.AssignableTo<ITransientService>())
.UsingRegistrationStrategy(RegistrationStrategy.Distinct)
.AsImplementedInterfaces()
.WithSingletonLifetime());

var services = Collection.GetDescriptors<ITransientService>();

Assert.Equal(4, services.Count(x => x.ServiceType == typeof(ITransientService)));
}

[Fact]
public void UsingRegistrationStrategy_DistinctWithSelf()
{
Collection.Scan(scan => scan
.FromAssemblyOf<ITransientService>()
// registers 9
.AddClasses(classes => classes.AssignableTo<ITransientService>())
.AsImplementedInterfaces()
.AsSelf()
.WithTransientLifetime()
// no new registrations, and does not throw due to not using TryAddEnumerable() with AsSelf()
.AddClasses(classes => classes.AssignableTo<ITransientService>())
.UsingRegistrationStrategy(RegistrationStrategy.Distinct)
.AsImplementedInterfaces()
.AsSelf()
.WithTransientLifetime());

Assert.Equal(9, Collection.Count);
}

[Fact]
public void UsingRegistrationStrategy_ReplaceDefault()
{
Expand Down