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

Support for the composite pattern. #1152

Merged
merged 7 commits into from
Jun 23, 2020
3 changes: 2 additions & 1 deletion src/Autofac/Builder/RegistrationBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,8 @@ public static class RegistrationBuilder
data.Ownership,
clonedPipelineBuilder,
services,
data.Metadata);
data.Metadata,
data.IsServiceOverride);
}
else
{
Expand Down
8 changes: 8 additions & 0 deletions src/Autofac/Builder/RegistrationData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,14 @@ public IComponentLifetime Lifetime
/// </summary>
public IDictionary<string, object?> Metadata { get; }

/// <summary>
/// Gets or sets a value indicating whether this registration forms a 'Service Override', which forces itself as the default
/// registration for all service it provides.
/// Registrations with <see cref="IsServiceOverride"/> set to true will not be included when resolving a collection of a service
/// to get all implementations.
/// </summary>
public bool IsServiceOverride { get; set; }

/// <summary>
/// Gets or sets the callback used to register this component.
/// </summary>
Expand Down
8 changes: 8 additions & 0 deletions src/Autofac/Core/IComponentRegistration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,14 @@ public interface IComponentRegistration : IDisposable
/// </summary>
bool IsAdapterForIndividualComponent { get; }

/// <summary>
/// Gets a value indicating whether this registration forms a 'Service Override', which forces itself as the default
/// registration for all service it provides.
/// Registrations with <see cref="IsServiceOverride"/> set to true will not be included when resolving a collection of a service
/// to get all implementations.
/// </summary>
bool IsServiceOverride { get; }
tillig marked this conversation as resolved.
Show resolved Hide resolved

/// <summary>
/// Provides an event that will be invoked just before a pipeline is built, and can be used to add additional middleware
/// at that point.
Expand Down
15 changes: 12 additions & 3 deletions src/Autofac/Core/Registration/ComponentRegistration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -80,15 +80,17 @@ public class ComponentRegistration : Disposable, IComponentRegistration
/// <param name="ownership">Whether the component instances are disposed at the end of their lifetimes.</param>
/// <param name="services">Services the component provides.</param>
/// <param name="metadata">Data associated with the component.</param>
/// <param name="isServiceOverride">Indicates whether this registration is a service override.</param>
public ComponentRegistration(
Guid id,
IInstanceActivator activator,
IComponentLifetime lifetime,
InstanceSharing sharing,
InstanceOwnership ownership,
IEnumerable<Service> services,
IDictionary<string, object?> metadata)
: this(id, activator, lifetime, sharing, ownership, new ResolvePipelineBuilder(PipelineType.Registration), services, metadata)
IDictionary<string, object?> metadata,
bool isServiceOverride = false)
: this(id, activator, lifetime, sharing, ownership, new ResolvePipelineBuilder(PipelineType.Registration), services, metadata, isServiceOverride)
{
}

Expand All @@ -103,6 +105,7 @@ public class ComponentRegistration : Disposable, IComponentRegistration
/// <param name="pipelineBuilder">The resolve pipeline builder for the registration.</param>
/// <param name="services">Services the component provides.</param>
/// <param name="metadata">Data associated with the component.</param>
/// <param name="isServiceOverride">Indicates whether this registration is a service override.</param>
public ComponentRegistration(
Guid id,
IInstanceActivator activator,
Expand All @@ -111,7 +114,8 @@ public class ComponentRegistration : Disposable, IComponentRegistration
InstanceOwnership ownership,
IResolvePipelineBuilder pipelineBuilder,
IEnumerable<Service> services,
IDictionary<string, object?> metadata)
IDictionary<string, object?> metadata,
bool isServiceOverride = false)
tillig marked this conversation as resolved.
Show resolved Hide resolved
{
if (activator == null) throw new ArgumentNullException(nameof(activator));
if (lifetime == null) throw new ArgumentNullException(nameof(lifetime));
Expand All @@ -129,6 +133,7 @@ public class ComponentRegistration : Disposable, IComponentRegistration
Services = Enforce.ArgumentElementNotNull(services, nameof(services));
Metadata = metadata;
IsAdapterForIndividualComponent = false;
IsServiceOverride = isServiceOverride;
}

/// <summary>
Expand Down Expand Up @@ -160,6 +165,7 @@ public class ComponentRegistration : Disposable, IComponentRegistration
if (target == null) throw new ArgumentNullException(nameof(target));
_target = target;
IsAdapterForIndividualComponent = isAdapterForIndividualComponents;
IsServiceOverride = _target.IsServiceOverride;
}

/// <summary>
Expand Down Expand Up @@ -204,6 +210,9 @@ public class ComponentRegistration : Disposable, IComponentRegistration
/// </summary>
public IDictionary<string, object?> Metadata { get; }

/// <inheritdoc/>
public bool IsServiceOverride { get; }

/// <inheritdoc />
public event EventHandler<IResolvePipelineBuilder>? PipelineBuilding;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,9 @@ public ComponentRegistrationLifetimeDecorator(IComponentRegistration inner, ICom
/// <inheritdoc/>
public IResolvePipeline ResolvePipeline => _inner.ResolvePipeline;

/// <inheritdoc/>
public bool IsServiceOverride => _inner.IsServiceOverride;

/// <inheritdoc/>
public event EventHandler<IResolvePipelineBuilder> PipelineBuilding
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -183,13 +183,7 @@ public bool TryGetServiceRegistration(Service service, out ServiceRegistration s
{
var info = GetInitializedServiceInfo(service);

// There is a 'virtual' registration; use it.
if (info.RedirectionTargetRegistration is object)
{
serviceData = new ServiceRegistration(info.ServicePipeline, info.RedirectionTargetRegistration);
return true;
}
else if (info.TryGetRegistration(out var registration))
if (info.TryGetRegistration(out var registration))
{
serviceData = new ServiceRegistration(info.ServicePipeline, registration);
return true;
Expand Down
38 changes: 18 additions & 20 deletions src/Autofac/Core/Registration/ServiceRegistrationInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ internal class ServiceRegistrationInfo : IResolvePipelineBuilder
[SuppressMessage("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields", Justification = "The _service field is useful in debugging and diagnostics.")]
private readonly Service _service;

private IComponentRegistration? _serviceOverride = null;

/// <summary>
/// List of implicit default service implementations. Overriding default implementations are appended to the end,
/// so the enumeration should begin from the end too, and the most default implementation comes last.
Expand Down Expand Up @@ -111,7 +113,15 @@ public IEnumerable<IComponentRegistration> Implementations
{
RequiresInitialization();

return _registeredImplementations!.Value;
if (_serviceOverride is object)
{
yield return _serviceOverride;
}

foreach (var item in _registeredImplementations!.Value)
{
yield return item;
}
}
}

Expand Down Expand Up @@ -170,7 +180,11 @@ public bool IsRegistered
/// <param name="originatedFromSource">Whether the registration originated from a dynamic source.</param>
public void AddImplementation(IComponentRegistration registration, bool preserveDefaults, bool originatedFromSource)
{
if (preserveDefaults)
if (registration.IsServiceOverride)
{
_serviceOverride = registration;
}
else if (preserveDefaults)
{
if (originatedFromSource)
{
Expand Down Expand Up @@ -218,23 +232,6 @@ public void UseServiceMiddleware(IResolveMiddleware middleware, MiddlewareInsert
}

_customPipelineBuilder.Use(middleware, insertionMode);

if (middleware is IRedirectingMiddleware redirectMiddleware)
{
var target = redirectMiddleware.TargetRegistration;

if (target is null)
{
throw new InvalidOperationException(
string.Format(
CultureInfo.CurrentCulture,
ServiceRegistrationInfoResources.RedirectingMiddlewareHasNullRegistration,
redirectMiddleware.ToString()));
}

// This middleware provides a redirect registration for the registration. Store it.
RedirectionTargetRegistration = redirectMiddleware.TargetRegistration;
}
}

/// <summary>
Expand Down Expand Up @@ -266,7 +263,8 @@ public bool TryGetRegistration([NotNullWhen(returnValue: true)] out IComponentRe
{
RequiresInitialization();

registration = _defaultImplementation ??= _defaultImplementations.LastOrDefault() ??
registration = _defaultImplementation ??= _serviceOverride ??
_defaultImplementations.LastOrDefault() ??
_sourceImplementations?.First() ??
_preserveDefaultImplementations?.First();

Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -123,9 +123,6 @@
<data name="NotInitialized" xml:space="preserve">
<value>The operation is not valid until the object is initialized.</value>
</data>
<data name="RedirectingMiddlewareHasNullRegistration" xml:space="preserve">
<value>Service middleware '{0}' implements IRedirectingMiddleware but returns a null value for TargetRegistration.</value>
</data>
<data name="ServicePipelineCannotBeBuilt" xml:space="preserve">
<value>The service pipeline cannot be built until the service has finished initialization.</value>
</data>
Expand Down
51 changes: 0 additions & 51 deletions src/Autofac/Core/Resolving/Pipeline/IRedirectingMiddleware.cs

This file was deleted.

6 changes: 6 additions & 0 deletions src/Autofac/Core/ServiceRegistration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,12 @@ public ServiceRegistration(IResolvePipeline servicePipeline, IComponentRegistrat
/// </summary>
public IComponentRegistration Registration { get; }

/// <summary>
/// Gets a value indicating whether this registration is a service override registrations.
/// Service override registrations should generally be skipped when resolving a collection of any type of service.
/// </summary>
public bool IsServiceOverride => Registration.IsServiceOverride;

/// <summary>
/// Gets additional data associated with the component.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ public IEnumerable<IComponentRegistration> RegistrationsFor(Service service, Fun
{
var itemRegistrations = c.ComponentRegistry
.ServiceRegistrationsFor(elementTypeService)
.Where(cr => !cr.IsServiceOverride)
.OrderBy(cr => cr.Registration.GetRegistrationOrder())
.ToList();

Expand Down
6 changes: 6 additions & 0 deletions src/Autofac/Features/Decorators/DecoratorMiddleware.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,12 @@ public void Execute(ResolveRequestContextBase context, Action<ResolveRequestCont
return;
}

if (context.Registration.IsServiceOverride)
{
// We don't decorate service overrides.
return;
}

// This middleware is only ever added to IServiceWithType pipelines, so this cast will always succeed.
var swt = (IServiceWithType)context.Service;

Expand Down