Skip to content

Commit

Permalink
Add RegistrationStrategy to skip, append or replace existing services. (
Browse files Browse the repository at this point in the history
  • Loading branch information
adamhathcock authored and khellang committed Apr 7, 2017
1 parent 0ea37ce commit 451c124
Show file tree
Hide file tree
Showing 12 changed files with 212 additions and 16 deletions.
4 changes: 2 additions & 2 deletions src/Scrutor/AssemblySelector.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ private IImplementationTypeSelector InternalFromAssemblies(IEnumerable<Assembly>
return AddSelector(assemblies.SelectMany(asm => asm.DefinedTypes).Select(x => x.AsType()));
}

void ISelector.Populate(IServiceCollection services)
void ISelector.Populate(IServiceCollection services, RegistrationStrategy registrationStrategy)
{
if (services == null)
{
Expand All @@ -79,7 +79,7 @@ void ISelector.Populate(IServiceCollection services)

foreach (var selector in Selectors)
{
selector.Populate(services);
selector.Populate(services, registrationStrategy);
}
}

Expand Down
8 changes: 5 additions & 3 deletions src/Scrutor/AttributeSelector.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
Expand All @@ -15,13 +15,15 @@ public AttributeSelector(IEnumerable<Type> types)

private IEnumerable<Type> Types { get; }

void ISelector.Populate(IServiceCollection services)
void ISelector.Populate(IServiceCollection services, RegistrationStrategy registrationStrategy)
{
if (services == null)
{
throw new ArgumentNullException(nameof(services));
}

var strategy = registrationStrategy ?? RegistrationStrategy.Append;

foreach (var type in Types)
{
var typeInfo = type.GetTypeInfo();
Expand All @@ -44,7 +46,7 @@ void ISelector.Populate(IServiceCollection services)
{
var descriptor = new ServiceDescriptor(serviceType, type, attribute.Lifetime);

services.Add(descriptor);
strategy.Apply(services, descriptor);
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/Scrutor/ISelector.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ namespace Scrutor
{
internal interface ISelector
{
void Populate(IServiceCollection services);
void Populate(IServiceCollection services, RegistrationStrategy options);
}
}
2 changes: 2 additions & 0 deletions src/Scrutor/IServiceTypeSelector.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,5 +59,7 @@ public interface IServiceTypeSelector : IImplementationTypeSelector
/// Registers each matching concrete type according to their <see cref="ServiceDescriptorAttribute"/>.
/// </summary>
IImplementationTypeSelector UsingAttributes();

IServiceTypeSelector UsingRegistrationStrategy(RegistrationStrategy registrationStrategy);
}
}
4 changes: 2 additions & 2 deletions src/Scrutor/ImplementationTypeSelector.cs
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ public IServiceTypeSelector AddClasses(Action<IImplementationTypeFilter> action,
return AddSelector(filter.Types);
}

void ISelector.Populate(IServiceCollection services)
void ISelector.Populate(IServiceCollection services, RegistrationStrategy registrationStrategy)
{
if (services == null)
{
Expand All @@ -96,7 +96,7 @@ void ISelector.Populate(IServiceCollection services)

foreach (var selector in Selectors)
{
selector.Populate(services);
selector.Populate(services, registrationStrategy);
}
}

Expand Down
6 changes: 4 additions & 2 deletions src/Scrutor/LifetimeSelector.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public IImplementationTypeSelector WithLifetime(ServiceLifetime lifetime)
return this;
}

void ISelector.Populate(IServiceCollection services)
void ISelector.Populate(IServiceCollection services, RegistrationStrategy registrationStrategy)
{
if (services == null)
{
Expand All @@ -52,6 +52,8 @@ void ISelector.Populate(IServiceCollection services)
Lifetime = ServiceLifetime.Transient;
}

registrationStrategy = registrationStrategy ?? RegistrationStrategy.Append;

foreach (var typeMap in TypeMaps)
{
foreach (var serviceType in typeMap.ServiceTypes)
Expand All @@ -65,7 +67,7 @@ void ISelector.Populate(IServiceCollection services)

var descriptor = new ServiceDescriptor(serviceType, implementationType, Lifetime.Value);

services.Add(descriptor);
registrationStrategy.Apply(services, descriptor);
}
}
}
Expand Down
57 changes: 57 additions & 0 deletions src/Scrutor/RegistrationStrategy.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;

namespace Scrutor
{
public abstract class RegistrationStrategy
{
public static readonly RegistrationStrategy Skip = new SkipRegistrationStrategy();

public static readonly RegistrationStrategy Append = new AppendRegistrationStrategy();

public static RegistrationStrategy Replace(ReplacementBehavior behavior = ReplacementBehavior.Default) => new ReplaceRegistrationStrategy(behavior);

public abstract void Apply(IServiceCollection services, ServiceDescriptor descriptor);

private sealed class SkipRegistrationStrategy : RegistrationStrategy
{
public override void Apply(IServiceCollection services, ServiceDescriptor descriptor) => services.TryAdd(descriptor);
}

private sealed class AppendRegistrationStrategy : RegistrationStrategy
{
public override void Apply(IServiceCollection services, ServiceDescriptor descriptor) => services.Add(descriptor);
}

private sealed class ReplaceRegistrationStrategy : RegistrationStrategy
{
public ReplaceStrategy(ReplacementBehavior behavior)
{
Behavior = behavior;
}

private ReplacementBehavior Behavior { get; }

public override void Apply(IServiceCollection services, ServiceDescriptor descriptor)
{
var behavior = Behavior;

if (behavior == ReplacementBehavior.Default)
{
behavior = ReplacementBehavior.ServiceType;
}

for (var i = services.Count - 1; i >= 0; i--)
{
if ((behavior.HasFlag(ReplacementBehavior.ServiceType) && services[i].ServiceType == descriptor.ServiceType)
|| (behavior.HasFlag(ReplacementBehavior.ImplementationType) && services[i].ImplementationType == descriptor.ImplementationType))
{
services.RemoveAt(i);
}
}

services.Add(descriptor);
}
}
}
}
22 changes: 22 additions & 0 deletions src/Scrutor/ReplacementBehavior.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using System;

namespace Scrutor
{
[Flags]
public enum ReplacementBehavior
{
/// <summary>
/// ServiceType only is the default
/// </summary>
Default = 0,
/// <summary>
/// Replace by ServiceType (default)
/// </summary>
ServiceType = 1,
/// <summary>
/// Replace by ImplementationType.
/// </summary>
ImplementationType = 2,
All = ServiceType | ImplementationType
}
}
6 changes: 3 additions & 3 deletions src/Scrutor/ServiceCollectionExtensions.Scanning.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,12 @@ public static IServiceCollection Scan(this IServiceCollection services, Action<I

action(selector);

return services.Populate(selector);
return services.Populate(selector, RegistrationStrategy.Append);
}

private static IServiceCollection Populate(this IServiceCollection services, ISelector selector)
private static IServiceCollection Populate(this IServiceCollection services, ISelector selector, RegistrationStrategy registrationStrategy)
{
selector.Populate(services);
selector.Populate(services, registrationStrategy);
return services;
}
}
Expand Down
14 changes: 11 additions & 3 deletions src/Scrutor/ServiceTypeSelector.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ namespace Scrutor
{
internal class ServiceTypeSelector : ImplementationTypeSelector, IServiceTypeSelector, ISelector
{
private RegistrationStrategy registrationStrategy;

public ServiceTypeSelector(IEnumerable<Type> types) : base(types)
{
}
Expand Down Expand Up @@ -85,7 +87,7 @@ public IImplementationTypeSelector UsingAttributes()
return this;
}

void ISelector.Populate(IServiceCollection services)
void ISelector.Populate(IServiceCollection services, RegistrationStrategy registrationStrategy)
{
if (services == null)
{
Expand All @@ -96,13 +98,19 @@ void ISelector.Populate(IServiceCollection services)
{
AsSelf();
}

var r = this.registrationStrategy ?? registrationStrategy;
foreach (var selector in Selectors)
{
selector.Populate(services);
selector.Populate(services, r);
}
}

public IServiceTypeSelector UsingRegistrationStrategy(RegistrationStrategy registrationStrategy)
{
this.registrationStrategy = registrationStrategy;
return this;
}

private ILifetimeSelector AddSelector(IEnumerable<TypeMap> types)
{
var selector = new LifetimeSelector(Types, types);
Expand Down
99 changes: 99 additions & 0 deletions test/Scrutor.Tests/ScanningTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,105 @@ public class ScanningTests
{
private IServiceCollection Collection { get; } = new ServiceCollection();

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


Collection.Scan(scan => scan.FromAssemblyOf<ITransientService>()
.AddClasses(classes => classes.AssignableTo<ITransientService>())
.AsImplementedInterfaces()
.WithSingletonLifetime());

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

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

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


Collection.Scan(scan => scan.FromAssemblyOf<ITransientService>()
.AddClasses(classes => classes.AssignableTo<ITransientService>())
.UsingRegistrationStrategy(RegistrationStrategy.Skip)
.AsImplementedInterfaces()
.WithSingletonLifetime());

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

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

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


Collection.Scan(scan => scan.FromAssemblyOf<ITransientService>()
.AddClasses(classes => classes.AssignableTo<ITransientService>())
.UsingRegistrationStrategy(RegistrationStrategy.Replace(ReplacementBehavior.Default))
.AsImplementedInterfaces()
.WithSingletonLifetime());

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

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

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


Collection.Scan(scan => scan.FromAssemblyOf<ITransientService>()
.AddClasses(classes => classes.AssignableTo<ITransientService>())
.UsingRegistrationStrategy(RegistrationStrategy.Replace(ReplacementBehavior.ServiceType))
.AsImplementedInterfaces()
.WithSingletonLifetime());

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

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

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


Collection.Scan(scan => scan.FromAssemblyOf<ITransientService>()
.AddClasses(classes => classes.AssignableTo<ITransientService>())
.UsingRegistrationStrategy(RegistrationStrategy.Replace(ReplacementBehavior.ImplementationType))
.AsImplementedInterfaces()
.WithSingletonLifetime());

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

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

[Fact]
public void CanFilterTypesToScan()
{
Expand Down
4 changes: 4 additions & 0 deletions test/Scrutor.Tests/Scrutor.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,8 @@
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="1.1.0" />
<PackageReference Include="xunit" Version="2.2.0" />
</ItemGroup>

<ItemGroup>
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
</ItemGroup>
</Project>

0 comments on commit 451c124

Please sign in to comment.