Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
dotnetjunkie committed Nov 15, 2023
2 parents 563a2dc + 65d30d0 commit f050f1b
Showing 1 changed file with 47 additions and 37 deletions.
84 changes: 47 additions & 37 deletions src/SimpleInjector/Internals/GenericRegistrationEntry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ namespace SimpleInjector.Internals

internal sealed class GenericRegistrationEntry : IRegistrationEntry
{
private readonly List<IProducerProvider> providers = new List<IProducerProvider>();
private readonly List<IProducerProvider> providers = new();
private readonly Container container;

internal GenericRegistrationEntry(Container container)
Expand All @@ -21,7 +21,8 @@ private interface IProducerProvider
{
bool IsConditional { get; }

bool AppliesToAllClosedServiceTypes { get; }
// The first call to this method can be costly to perform.
bool GetAppliesToAllClosedServiceTypes();

Type ServiceType { get; }

Expand Down Expand Up @@ -70,9 +71,12 @@ public void AddGeneric(

this.ThrowWhenProviderToRegisterOverlapsWithExistingProvider(provider);

if (provider.AppliesToAllClosedServiceTypes && this.container.Options.AllowOverridingRegistrations)
if (this.container.Options.AllowOverridingRegistrations)
{
this.providers.RemoveAll(p => p.AppliesToAllClosedServiceTypes);
if (provider.GetAppliesToAllClosedServiceTypes())
{
this.providers.RemoveAll(p => p.GetAppliesToAllClosedServiceTypes());
}
}

this.providers.Add(provider);
Expand Down Expand Up @@ -178,21 +182,19 @@ private void ThrowWhenConditionalIsRegisteredInOverridingMode(InstanceProducer p
private void ThrowWhenConditionalIsRegisteredInOverridingMode(
OpenGenericToInstanceProducerProvider provider)
{
if (!provider.AppliesToAllClosedServiceTypes
&& this.container.Options.AllowOverridingRegistrations)
if (this.providers.Count > 0
&& this.container.Options.AllowOverridingRegistrations
&& !provider.GetAppliesToAllClosedServiceTypes())
{
if (this.providers.Count > 0)
if (provider.Predicate != null)
{
if (provider.Predicate != null)
{
throw new NotSupportedException(
StringResources.MakingConditionalRegistrationsInOverridingModeIsNotSupported());
}
else
{
throw new NotSupportedException(
StringResources.MakingRegistrationsWithTypeConstraintsInOverridingModeIsNotSupported());
}
throw new NotSupportedException(
StringResources.MakingConditionalRegistrationsInOverridingModeIsNotSupported());
}
else
{
throw new NotSupportedException(
StringResources.MakingRegistrationsWithTypeConstraintsInOverridingModeIsNotSupported());
}
}
}
Expand All @@ -201,14 +203,14 @@ private void ThrowWhenProviderToRegisterOverlapsWithExistingProvider(
OpenGenericToInstanceProducerProvider providerToRegister)
{
bool providerToRegisterIsSuperset =
providerToRegister.AppliesToAllClosedServiceTypes && this.providers.Count > 0;
this.providers.Count > 0 && providerToRegister.GetAppliesToAllClosedServiceTypes();

// A provider with AppliesToAllClosedServiceTypes true will always have an ImplementationType,
// because the property will always be false for providers with a factory.
Type providerImplementationType = providerToRegister.ImplementationType!;

bool isReplacement = providerToRegister.AppliesToAllClosedServiceTypes
&& this.container.Options.AllowOverridingRegistrations;
bool isReplacement = this.container.Options.AllowOverridingRegistrations
&& providerToRegister.GetAppliesToAllClosedServiceTypes();

// A provider is a superset of the providerToRegister when it can be applied to ALL generic
// types that the providerToRegister can be applied to as well.
Expand All @@ -230,13 +232,11 @@ private IEnumerable<IProducerProvider> GetSupersetProvidersFor(Type implementati
from provider in this.providers
where implementationType != null
where provider.ImplementationType != null
where provider.AppliesToAllClosedServiceTypes
|| provider.ImplementationType == implementationType
where provider.ImplementationType == implementationType || provider.GetAppliesToAllClosedServiceTypes()
select provider;

private static InvalidOperationException GetAnOverlappingGenericRegistrationExistsException(
IProducerProvider providerToRegister, IProducerProvider overlappingProvider) =>
new InvalidOperationException(
IProducerProvider providerToRegister, IProducerProvider overlappingProvider) => new(
StringResources.AnOverlappingRegistrationExists(
providerToRegister.ServiceType,
// ImplementationType will never be null, because providers can never be overlapping when they
Expand Down Expand Up @@ -287,7 +287,7 @@ public ClosedToInstanceProducerProvider(InstanceProducer producer)
}

public bool IsConditional => this.producer.IsConditional;
public bool AppliesToAllClosedServiceTypes => false;
public bool GetAppliesToAllClosedServiceTypes() => false;
public Type ServiceType => this.producer.ServiceType;
public Type? ImplementationType => this.producer.Registration.ImplementationType;
public IEnumerable<InstanceProducer> CurrentProducers => Enumerable.Repeat(this.producer, 1);
Expand All @@ -311,14 +311,14 @@ private sealed class OpenGenericToInstanceProducerProvider : IProducerProvider
{
internal readonly Predicate<PredicateContext>? Predicate;

private readonly Dictionary<object, InstanceProducer> cache =
new Dictionary<object, InstanceProducer>();
private readonly Dictionary<Type, Registration> registrationCache =
new Dictionary<Type, Registration>();
private readonly Dictionary<object, InstanceProducer> cache = new();
private readonly Dictionary<Type, Registration> registrationCache = new();

private readonly Lifestyle lifestyle;
private readonly Container container;

private bool? appliesToAllClosedServiceTypes;

internal OpenGenericToInstanceProducerProvider(
Type serviceType,
Type implementationType,
Expand All @@ -332,11 +332,6 @@ internal OpenGenericToInstanceProducerProvider(
this.lifestyle = lifestyle;
this.Predicate = predicate;
this.container = container;

// We cache the result of this method, because this is a really heavy operation.
// Not caching it can dramatically influence the performance of the registration process.
this.AppliesToAllClosedServiceTypes =
this.RegistrationAppliesToAllClosedServiceTypes(implementationType);
}

internal OpenGenericToInstanceProducerProvider(
Expand All @@ -351,11 +346,26 @@ internal OpenGenericToInstanceProducerProvider(
this.lifestyle = lifestyle;
this.Predicate = predicate;
this.container = container;
this.AppliesToAllClosedServiceTypes = false;
this.appliesToAllClosedServiceTypes = false;
}

public bool IsConditional => this.Predicate != null;
public bool AppliesToAllClosedServiceTypes { get; }

// I turned this former property into a method call to make it more obvious that this is can be
// a very costly operation (which can also throw first-chance exceptions).
public bool GetAppliesToAllClosedServiceTypes()
{
if (this.appliesToAllClosedServiceTypes is null)
{
// We cache the result of this method. Not caching it can dramatically influence the
// performance of the registration process.
this.appliesToAllClosedServiceTypes =
this.RegistrationAppliesToAllClosedServiceTypes(this.ImplementationType!);
}

return this.appliesToAllClosedServiceTypes.Value;
}

public Type ServiceType { get; }
public Type? ImplementationType { get; }
public Func<TypeFactoryContext, Type> ImplementationTypeFactory { get; }
Expand Down Expand Up @@ -462,7 +472,7 @@ private InstanceProducer GetProducer(PredicateContext context)
}

private InstanceProducer CreateNewProducerFor(PredicateContext context) =>
new InstanceProducer(context.ServiceType, this.GetRegistration(context), this.Predicate);
new(context.ServiceType, this.GetRegistration(context), this.Predicate);

private Registration GetRegistration(PredicateContext context)
{
Expand Down

0 comments on commit f050f1b

Please sign in to comment.