Skip to content

Commit

Permalink
Extended ILifetimeSelector with a new WithLifetime method that ac…
Browse files Browse the repository at this point in the history
…cepts a `Func<Type, ServiceLifetime>` parameter, i.e. a function that maps the implementation type to the service lifetime
  • Loading branch information
ripebananas authored and khellang committed Dec 23, 2024
1 parent 1109c88 commit e77e0f2
Show file tree
Hide file tree
Showing 4 changed files with 32 additions and 9 deletions.
8 changes: 7 additions & 1 deletion src/Scrutor/ILifetimeSelector.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Microsoft.Extensions.DependencyInjection;
using System;
using Microsoft.Extensions.DependencyInjection;

namespace Scrutor;

Expand All @@ -23,4 +24,9 @@ public interface ILifetimeSelector : IServiceTypeSelector
/// Registers each matching concrete type with the specified <paramref name="lifetime"/>.
/// </summary>
IImplementationTypeSelector WithLifetime(ServiceLifetime lifetime);

/// <summary>
/// Registers each matching concrete type with the specified <paramref name="lifetime"/>.
/// </summary>
IImplementationTypeSelector WithLifetime(Func<Type, ServiceLifetime> lifetime);
}
23 changes: 18 additions & 5 deletions src/Scrutor/LifetimeSelector.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public LifetimeSelector(ServiceTypeSelector inner, IEnumerable<TypeMap> typeMaps

private IEnumerable<TypeFactoryMap> TypeFactoryMaps { get; }

public ServiceLifetime? Lifetime { get; set; }
public Func<Type, ServiceLifetime>? Lifetime { get; set; }

public IImplementationTypeSelector WithSingletonLifetime()
{
Expand All @@ -37,11 +37,17 @@ public IImplementationTypeSelector WithTransientLifetime()
{
return WithLifetime(ServiceLifetime.Transient);
}

public IImplementationTypeSelector WithLifetime(ServiceLifetime lifetime)
{
Preconditions.IsDefined(lifetime, nameof(lifetime));

return WithLifetime(_ => lifetime);
}

public IImplementationTypeSelector WithLifetime(Func<Type, ServiceLifetime> lifetime)
{
Preconditions.NotNull(lifetime, nameof(lifetime));

Inner.PropagateLifetime(lifetime);

return this;
Expand Down Expand Up @@ -205,7 +211,7 @@ void ISelector.Populate(IServiceCollection services, RegistrationStrategy? strat
{
strategy ??= RegistrationStrategy.Append;

var lifetime = Lifetime ?? ServiceLifetime.Transient;
var serviceLifetimes = new Dictionary<Type, ServiceLifetime>();

foreach (var typeMap in TypeMaps)
{
Expand All @@ -218,7 +224,7 @@ void ISelector.Populate(IServiceCollection services, RegistrationStrategy? strat
throw new InvalidOperationException($@"Type ""{implementationType.ToFriendlyName()}"" is not assignable to ""${serviceType.ToFriendlyName()}"".");
}

var descriptor = new ServiceDescriptor(serviceType, implementationType, lifetime);
var descriptor = new ServiceDescriptor(serviceType, implementationType, GetOrAddLifetime(serviceLifetimes, implementationType));

strategy.Apply(services, descriptor);
}
Expand All @@ -228,10 +234,17 @@ void ISelector.Populate(IServiceCollection services, RegistrationStrategy? strat
{
foreach (var serviceType in typeFactoryMap.ServiceTypes)
{
var descriptor = new ServiceDescriptor(serviceType, typeFactoryMap.ImplementationFactory, lifetime);
var descriptor = new ServiceDescriptor(serviceType, typeFactoryMap.ImplementationFactory, GetOrAddLifetime(serviceLifetimes, typeFactoryMap.ImplementationType));

strategy.Apply(services, descriptor);
}
}
}

private ServiceLifetime GetOrAddLifetime(Dictionary<Type, ServiceLifetime> serviceLifetimes, Type implementationType)
{
return serviceLifetimes.TryGetValue(implementationType, out var lifetime)
? lifetime
: (serviceLifetimes[implementationType] = Lifetime?.Invoke(implementationType) ?? ServiceLifetime.Transient);
}
}
4 changes: 2 additions & 2 deletions src/Scrutor/ServiceTypeSelector.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ public ILifetimeSelector AsSelfWithInterfaces(Func<Type, bool> predicate)

return AddSelector(
Types.Select(t => new TypeMap(t, new[] { t })),
Types.Select(t => new TypeFactoryMap(x => x.GetRequiredService(t), Selector(t, predicate))));
Types.Select(t => new TypeFactoryMap(x => x.GetRequiredService(t), Selector(t, predicate), t)));

static IEnumerable<Type> Selector(Type type, Func<Type, bool> predicate)
{
Expand Down Expand Up @@ -209,7 +209,7 @@ public IServiceTypeSelector AddClasses(Action<IImplementationTypeFilter> action,

#endregion

internal void PropagateLifetime(ServiceLifetime lifetime)
internal void PropagateLifetime(Func<Type, ServiceLifetime> lifetime)
{
foreach (var selector in Selectors.OfType<LifetimeSelector>())
{
Expand Down
6 changes: 5 additions & 1 deletion src/Scrutor/TypeFactoryMap.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,17 @@ namespace Scrutor;

internal struct TypeFactoryMap
{
public TypeFactoryMap(Func<IServiceProvider, object> implementationFactory, IEnumerable<Type> serviceTypes)
public TypeFactoryMap(Func<IServiceProvider, object> implementationFactory, IEnumerable<Type> serviceTypes, Type implementationType)
{
ImplementationFactory = implementationFactory;
ServiceTypes = serviceTypes;
ImplementationType = implementationType;
}

public Func<IServiceProvider, object> ImplementationFactory { get; }

public IEnumerable<Type> ServiceTypes { get; }

public Type ImplementationType { get; }

}

0 comments on commit e77e0f2

Please sign in to comment.