RegistrationRepository
using Stashbox.Configuration;
using Stashbox.Exceptions;
using Stashbox.Registration.Extensions;
using Stashbox.Registration.SelectionRules;
using Stashbox.Resolution;
using Stashbox.Utils;
using Stashbox.Utils.Data;
using Stashbox.Utils.Data.Immutable;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Stashbox.Registration
{
internal class RegistrationRepository : IRegistrationRepository
{
private ImmutableTree<Type, ImmutableBucket<object, ServiceRegistration>> serviceRepository = ImmutableTree<Type, ImmutableBucket<object, ServiceRegistration>>.Empty;
private readonly ContainerConfiguration containerConfiguration;
private readonly IRegistrationSelectionRule[] filters = new IRegistrationSelectionRule[4] {
RegistrationSelectionRules.GenericFilter,
RegistrationSelectionRules.NameFilter,
RegistrationSelectionRules.ScopeNameFilter,
RegistrationSelectionRules.ConditionFilter
};
private readonly IRegistrationSelectionRule[] topLevelFilters = new IRegistrationSelectionRule[3] {
RegistrationSelectionRules.GenericFilter,
RegistrationSelectionRules.NameFilter,
RegistrationSelectionRules.ScopeNameFilter
};
private readonly IRegistrationSelectionRule[] enumerableFilters = new IRegistrationSelectionRule[3] {
RegistrationSelectionRules.GenericFilter,
RegistrationSelectionRules.ScopeNameFilter,
RegistrationSelectionRules.ConditionFilter
};
public RegistrationRepository(ContainerConfiguration containerConfiguration)
{
this.containerConfiguration = containerConfiguration;
}
public bool AddOrUpdateRegistration(ServiceRegistration registration, Type serviceType)
{
if (registration.RegistrationContext.ReplaceExistingRegistrationOnlyIfExists)
return Swap.SwapValue<ServiceRegistration, Type, byte, byte, ImmutableTree<Type, ImmutableBucket<object, ServiceRegistration>>>(ref serviceRepository, (ServiceRegistration reg, Type type, byte t3, byte t4, ImmutableTree<Type, ImmutableBucket<object, ServiceRegistration>> repo) => repo.UpdateIfExists(type, true, (ImmutableBucket<object, ServiceRegistration> regs) => regs.ReplaceIfExists(reg.RegistrationDiscriminator, reg, false, delegate(ServiceRegistration old, ServiceRegistration new) {
new.Replaces(old);
return new;
})), registration, serviceType, 0, 0);
return Swap.SwapValue(ref serviceRepository, (ServiceRegistration reg, Type type, ImmutableBucket<object, ServiceRegistration> newRepo, Rules.RegistrationBehavior regBehavior, ImmutableTree<Type, ImmutableBucket<object, ServiceRegistration>> repo) => repo.AddOrUpdate(type, newRepo, true, delegate(ImmutableBucket<object, ServiceRegistration> oldValue, ImmutableBucket<object, ServiceRegistration> newValue) {
bool allowUpdate = reg.RegistrationContext.ReplaceExistingRegistration || regBehavior == Rules.RegistrationBehavior.ReplaceExisting;
if (!allowUpdate && regBehavior == Rules.RegistrationBehavior.PreserveDuplications)
return oldValue.Add(reg.RegistrationDiscriminator, reg);
return oldValue.AddOrUpdate(reg.RegistrationDiscriminator, reg, false, delegate(ServiceRegistration old, ServiceRegistration new) {
if (!allowUpdate && regBehavior == Rules.RegistrationBehavior.ThrowException)
throw new ServiceAlreadyRegisteredException(old.ImplementationType, null);
if (!allowUpdate)
return old;
new.Replaces(old);
return new;
});
}), registration, serviceType, new ImmutableBucket<object, ServiceRegistration>(registration.RegistrationDiscriminator, registration), containerConfiguration.RegistrationBehavior);
}
public bool AddOrReMapRegistration(ServiceRegistration registration, Type serviceType)
{
if (!registration.RegistrationContext.ReplaceExistingRegistrationOnlyIfExists)
return Swap.SwapValue<Type, ImmutableBucket<object, ServiceRegistration>, byte, byte, ImmutableTree<Type, ImmutableBucket<object, ServiceRegistration>>>(ref serviceRepository, (Type type, ImmutableBucket<object, ServiceRegistration> newRepo, byte t3, byte t4, ImmutableTree<Type, ImmutableBucket<object, ServiceRegistration>> repo) => repo.AddOrUpdate(type, newRepo, true, true), serviceType, new ImmutableBucket<object, ServiceRegistration>(registration.RegistrationDiscriminator, registration), 0, 0);
return Swap.SwapValue<Type, ImmutableBucket<object, ServiceRegistration>, byte, byte, ImmutableTree<Type, ImmutableBucket<object, ServiceRegistration>>>(ref serviceRepository, (Type type, ImmutableBucket<object, ServiceRegistration> newRepo, byte t3, byte t4, ImmutableTree<Type, ImmutableBucket<object, ServiceRegistration>> repo) => repo.UpdateIfExists(type, newRepo, true), serviceType, new ImmutableBucket<object, ServiceRegistration>(registration.RegistrationDiscriminator, registration), 0, 0);
}
public bool ContainsRegistration(Type type, object name)
{
return serviceRepository.ContainsRegistration(type, name);
}
public IEnumerable<KeyValuePair<Type, ServiceRegistration>> GetRegistrationMappings()
{
return serviceRepository.Walk().SelectMany((KeyValue<Type, ImmutableBucket<object, ServiceRegistration>> reg) => from r in reg.Value
select new KeyValuePair<Type, ServiceRegistration>(reg.Key, r));
}
public ServiceRegistration GetRegistrationOrDefault(Type type, ResolutionContext resolutionContext, object name = null)
{
IEnumerable<ServiceRegistration> registrationsForType = GetRegistrationsForType(type);
if (registrationsForType == null)
return null;
return registrationsForType.SelectOrDefault(new TypeInformation(type, name), resolutionContext, topLevelFilters);
}
public ServiceRegistration GetRegistrationOrDefault(TypeInformation typeInfo, ResolutionContext resolutionContext)
{
IEnumerable<ServiceRegistration> registrationsForType = GetRegistrationsForType(typeInfo.Type);
if (registrationsForType == null)
return null;
return registrationsForType.SelectOrDefault(typeInfo, resolutionContext, filters);
}
public IEnumerable<ServiceRegistration> GetRegistrationsOrDefault(TypeInformation typeInfo, ResolutionContext resolutionContext)
{
IEnumerable<ServiceRegistration> registrationsForType = GetRegistrationsForType(typeInfo.Type);
if (registrationsForType == null)
return null;
IEnumerable<ServiceRegistration> enumerable = registrationsForType.FilterExclusiveOrDefault(typeInfo, resolutionContext, enumerableFilters);
if (enumerable == null)
return null;
return from reg in enumerable
orderby reg.RegistrationId
select reg;
}
private IEnumerable<ServiceRegistration> GetRegistrationsForType(Type type)
{
ImmutableBucket<object, ServiceRegistration> orDefault = serviceRepository.GetOrDefault(type, true);
if (!type.IsClosedGenericType())
return orDefault;
ImmutableBucket<object, ServiceRegistration> orDefault2 = serviceRepository.GetOrDefault(type.GetGenericTypeDefinition(), true);
if (orDefault2 == null)
return orDefault;
if (orDefault != null)
return orDefault2.Concat(orDefault);
return orDefault2;
}
}
}