Skip to content

Commit

Permalink
Merge pull request #1152 from alistairjevans/composites
Browse files Browse the repository at this point in the history
Support for the composite pattern.
  • Loading branch information
tillig committed Jun 23, 2020
2 parents c0af45c + bbe6bc3 commit 6043c08
Show file tree
Hide file tree
Showing 29 changed files with 1,203 additions and 169 deletions.
12 changes: 5 additions & 7 deletions src/Autofac/Builder/RegistrationBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -139,8 +139,7 @@ public static class RegistrationBuilder
builder.ActivatorData.Activator,
builder.ResolvePipeline,
builder.RegistrationData.Services.ToArray(),
builder.RegistrationStyle.Target,
builder.RegistrationStyle.IsAdapterForIndividualComponent);
builder.RegistrationStyle.Target);
}

/// <summary>
Expand Down Expand Up @@ -171,7 +170,6 @@ public static class RegistrationBuilder
/// <param name="pipelineBuilder">The component registration's resolve pipeline builder.</param>
/// <param name="services">Services provided by the registration.</param>
/// <param name="target">Optional; target registration.</param>
/// <param name="isAdapterForIndividualComponent">Optional; whether the registration is a 1:1 adapters on top of another component.</param>
/// <returns>An IComponentRegistration.</returns>
/// <exception cref="System.ArgumentNullException">
/// Thrown if <paramref name="activator" /> or <paramref name="data" /> is <see langword="null" />.
Expand All @@ -182,8 +180,7 @@ public static class RegistrationBuilder
IInstanceActivator activator,
IResolvePipelineBuilder pipelineBuilder,
Service[] services,
IComponentRegistration? target,
bool isAdapterForIndividualComponent = false)
IComponentRegistration? target)
{
if (activator == null) throw new ArgumentNullException(nameof(activator));
if (data == null) throw new ArgumentNullException(nameof(data));
Expand Down Expand Up @@ -224,7 +221,8 @@ public static class RegistrationBuilder
data.Ownership,
clonedPipelineBuilder,
services,
data.Metadata);
data.Metadata,
data.Options);
}
else
{
Expand All @@ -238,7 +236,7 @@ public static class RegistrationBuilder
services,
data.Metadata,
target,
isAdapterForIndividualComponent);
data.Options);
}

return registration;
Expand Down
5 changes: 5 additions & 0 deletions src/Autofac/Builder/RegistrationData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,11 @@ public IComponentLifetime Lifetime
/// </summary>
public IDictionary<string, object?> Metadata { get; }

/// <summary>
/// Gets or sets the options for the registration.
/// </summary>
public RegistrationOptions Options { get; set; }

/// <summary>
/// Gets or sets the callback used to register this component.
/// </summary>
Expand Down
7 changes: 1 addition & 6 deletions src/Autofac/Builder/SingleRegistrationStyle.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
using System;
using System.Collections.Generic;
using Autofac.Core;
using Autofac.Core.Registration;

namespace Autofac.Builder
{
Expand Down Expand Up @@ -55,11 +56,5 @@ public class SingleRegistrationStyle
/// Gets or sets the component upon which this registration is based.
/// </summary>
public IComponentRegistration? Target { get; set; }

/// <summary>
/// Gets or sets a value indicating whether the registration is a 1:1 adapters on top
/// of another component (e.g., Meta, Func, or Owned).
/// </summary>
public bool IsAdapterForIndividualComponent { get; set; }
}
}
5 changes: 2 additions & 3 deletions src/Autofac/Core/IComponentRegistration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -83,10 +83,9 @@ public interface IComponentRegistration : IDisposable
IResolvePipeline ResolvePipeline { get; }

/// <summary>
/// Gets a value indicating whether the registration is a 1:1 adapter on top
/// of another component (e.g., Meta, Func, or Owned).
/// Gets the options for the registration.
/// </summary>
bool IsAdapterForIndividualComponent { get; }
RegistrationOptions Options { get; }

/// <summary>
/// Provides an event that will be invoked just before a pipeline is built, and can be used to add additional middleware
Expand Down
2 changes: 1 addition & 1 deletion src/Autofac/Core/ImplicitRegistrationSource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ private IComponentRegistration CreateRegistration<T>(Service providedService, Se

var rb = BuildRegistration(registrationDelegate)
.As(providedService)
.Targeting(serviceRegistration.Registration, IsAdapterForIndividualComponents)
.Targeting(serviceRegistration.Registration)
.InheritRegistrationOrderFrom(serviceRegistration.Registration);

return rb.CreateRegistration();
Expand Down
43 changes: 29 additions & 14 deletions src/Autofac/Core/Registration/ComponentRegistration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,13 @@ public class ComponentRegistration : Disposable, IComponentRegistration
private readonly IResolvePipelineBuilder _lateBuildPipeline;
private IResolvePipeline? _builtComponentPipeline;

/// <summary>
/// Defines the options copied from a target registration onto this one.
/// </summary>
private const RegistrationOptions OptionsCopiedFromTargetRegistration = RegistrationOptions.Fixed |
RegistrationOptions.ExcludeFromCollections |
RegistrationOptions.DisableDecoration;

/// <summary>
/// Initializes a new instance of the <see cref="ComponentRegistration"/> class.
/// </summary>
Expand All @@ -55,7 +62,7 @@ public class ComponentRegistration : Disposable, IComponentRegistration
/// <param name="services">The set of services provided by the registration.</param>
/// <param name="metadata">Any metadata associated with the registration.</param>
/// <param name="target">The target/inner registration.</param>
/// <param name="isAdapterForIndividualComponents">Indicates whether this registration is an adapter for individual components (Meta, Func, etc).</param>
/// <param name="options">Contains options for the registration.</param>
public ComponentRegistration(
Guid id,
IInstanceActivator activator,
Expand All @@ -65,8 +72,8 @@ public class ComponentRegistration : Disposable, IComponentRegistration
IEnumerable<Service> services,
IDictionary<string, object?> metadata,
IComponentRegistration target,
bool isAdapterForIndividualComponents)
: this(id, activator, lifetime, sharing, ownership, new ResolvePipelineBuilder(PipelineType.Registration), services, metadata, target, isAdapterForIndividualComponents)
RegistrationOptions options = RegistrationOptions.None)
: this(id, activator, lifetime, sharing, ownership, new ResolvePipelineBuilder(PipelineType.Registration), services, metadata, target, options)
{
}

Expand All @@ -80,15 +87,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="options">Contains options for the registration.</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,
RegistrationOptions options = RegistrationOptions.None)
: this(id, activator, lifetime, sharing, ownership, new ResolvePipelineBuilder(PipelineType.Registration), services, metadata, options)
{
}

Expand All @@ -103,6 +112,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="options">The additional registration options.</param>
public ComponentRegistration(
Guid id,
IInstanceActivator activator,
Expand All @@ -111,7 +121,8 @@ public class ComponentRegistration : Disposable, IComponentRegistration
InstanceOwnership ownership,
IResolvePipelineBuilder pipelineBuilder,
IEnumerable<Service> services,
IDictionary<string, object?> metadata)
IDictionary<string, object?> metadata,
RegistrationOptions options = RegistrationOptions.None)
{
if (activator == null) throw new ArgumentNullException(nameof(activator));
if (lifetime == null) throw new ArgumentNullException(nameof(lifetime));
Expand All @@ -128,7 +139,7 @@ public class ComponentRegistration : Disposable, IComponentRegistration

Services = Enforce.ArgumentElementNotNull(services, nameof(services));
Metadata = metadata;
IsAdapterForIndividualComponent = false;
Options = options;
}

/// <summary>
Expand All @@ -143,7 +154,7 @@ public class ComponentRegistration : Disposable, IComponentRegistration
/// <param name="services">Services the component provides.</param>
/// <param name="metadata">Data associated with the component.</param>
/// <param name="target">The component registration upon which this registration is based.</param>
/// <param name="isAdapterForIndividualComponents">Whether the registration is a 1:1 adapters on top of another component.</param>
/// <param name="options">Registration options.</param>
public ComponentRegistration(
Guid id,
IInstanceActivator activator,
Expand All @@ -154,12 +165,14 @@ public class ComponentRegistration : Disposable, IComponentRegistration
IEnumerable<Service> services,
IDictionary<string, object?> metadata,
IComponentRegistration target,
bool isAdapterForIndividualComponents)
: this(id, activator, lifetime, sharing, ownership, pipelineBuilder, services, metadata)
RegistrationOptions options = RegistrationOptions.None)
: this(id, activator, lifetime, sharing, ownership, pipelineBuilder, services, metadata, options)
{
if (target == null) throw new ArgumentNullException(nameof(target));
_target = target;
IsAdapterForIndividualComponent = isAdapterForIndividualComponents;

// Certain flags carry over from the target.
Options = options | (_target.Options & OptionsCopiedFromTargetRegistration);
}

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

/// <summary>
/// Gets the options for the registration.
/// </summary>
public RegistrationOptions Options { get; }

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

Expand All @@ -214,9 +232,6 @@ public IResolvePipeline ResolvePipeline
protected set => _builtComponentPipeline = value;
}

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

/// <inheritdoc />
public void BuildResolvePipeline(IComponentRegistryServices registryServices)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,10 @@ public ComponentRegistrationLifetimeDecorator(IComponentRegistration inner, ICom
public IComponentRegistration Target => _inner.IsAdapting() ? _inner.Target : this;

/// <inheritdoc/>
public bool IsAdapterForIndividualComponent => _inner.IsAdapterForIndividualComponent;
public IResolvePipeline ResolvePipeline => _inner.ResolvePipeline;

/// <inheritdoc/>
public IResolvePipeline ResolvePipeline => _inner.ResolvePipeline;
public RegistrationOptions Options => _inner.Options;

/// <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
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ internal class ExternalComponentRegistration : ComponentRegistration
/// <param name="service">The service to register for.</param>
/// <param name="target">The target registration ID.</param>
public ExternalComponentRegistration(Service service, IComponentRegistration target)
: base(target.Id, new NoOpActivator(target.Activator.LimitType), target.Lifetime, target.Sharing, target.Ownership, new[] { service }, target.Metadata, target, false)
: base(target.Id, new NoOpActivator(target.Activator.LimitType), target.Lifetime, target.Sharing, target.Ownership, new[] { service }, target.Metadata, target)
{
}

Expand Down
63 changes: 63 additions & 0 deletions src/Autofac/Core/Registration/RegistrationOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// This software is part of the Autofac IoC container
// Copyright © 2020 Autofac Contributors
// https://autofac.org
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.

using System;
using System.Collections.Generic;

namespace Autofac.Core.Registration
{
/// <summary>
/// Defines options for a registration.
/// </summary>
[Flags]
public enum RegistrationOptions
{
/// <summary>
/// No special options; default behaviour.
/// </summary>
None = 0,

/// <summary>
/// Indicates that this registration is 'fixed' as the default, ignoring all other registrations when determining the default registration for
/// a service.
/// </summary>
Fixed = 2,

/// <summary>
/// Registrations with this flag will not be decorated.
/// </summary>
DisableDecoration = 4,

/// <summary>
/// Registrations with this flag will not be included in any collection resolves (i.e. <see cref="IEnumerable{TService}" /> and other collection types).
/// </summary>
ExcludeFromCollections = 8,

/// <summary>
/// Flag combination for composite registrations.
/// </summary>
Composite = Fixed | DisableDecoration | ExcludeFromCollections,
}
}
45 changes: 45 additions & 0 deletions src/Autofac/Core/Registration/RegistrationOptionsExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// This software is part of the Autofac IoC container
// Copyright © 2020 Autofac Contributors
// https://autofac.org
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.

namespace Autofac.Core.Registration
{
/// <summary>
/// Extension methods for registration options.
/// </summary>
public static class RegistrationOptionsExtensions
{
/// <summary>
/// Tests whether a given flag (or combined set of flags) is present in the specified
/// options enumeration.
/// </summary>
/// <param name="options">The option to test.</param>
/// <param name="flag">The flag (or flags) to test for.</param>
/// <returns>True if the specified flag (or flags) are enabled for the registration.</returns>
public static bool HasOption(this RegistrationOptions options, RegistrationOptions flag)
{
return (options & flag) == flag;
}
}
}

0 comments on commit 6043c08

Please sign in to comment.