Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added the Distinct registration strategy. This strategy will add serv… #196

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
Added the Distinct registration strategy. This strategy will add serv…
…ices if there is no registration with the same Service+Implementation types.
sirphilliptubell committed Mar 31, 2023
commit a20f4aaa7c4140b0c1a398ca9fd1f639db0969b0
27 changes: 26 additions & 1 deletion src/Scrutor/RegistrationStrategy.cs
Original file line number Diff line number Diff line change
@@ -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>
@@ -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);
13 changes: 12 additions & 1 deletion src/Scrutor/ServiceCollectionExtensions.cs
Original file line number Diff line number Diff line change
@@ -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
@@ -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()
@@ -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()
{