Skip to content

Commit

Permalink
Merge pull request #1121 from alistairjevans/pipelines
Browse files Browse the repository at this point in the history
Pipelines support.
  • Loading branch information
alistairjevans committed May 27, 2020
2 parents b083148 + c558977 commit ff22873
Show file tree
Hide file tree
Showing 101 changed files with 4,571 additions and 981 deletions.
12 changes: 12 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,18 @@ insert_final_newline = true
indent_size = 4
charset = utf-8-bom

; Force VS to recommend underscore at the start of created private fields.
[*.{cs,vb}]
dotnet_naming_rule.private_members_with_underscore.symbols = private_fields
dotnet_naming_rule.private_members_with_underscore.style = prefix_underscore
dotnet_naming_rule.private_members_with_underscore.severity = suggestion

dotnet_naming_symbols.private_fields.applicable_kinds = field
dotnet_naming_symbols.private_fields.applicable_accessibilities = private

dotnet_naming_style.prefix_underscore.capitalization = camel_case
dotnet_naming_style.prefix_underscore.required_prefix = _

; .NET project files and MSBuild - match defaults for VS
[*.{csproj,nuspec,proj,projitems,props,shproj,targets,vbproj,vcxproj,vcxproj.filters,vsixmanifest,vsct}]
indent_size = 2
Expand Down
39 changes: 33 additions & 6 deletions src/Autofac/Autofac.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,10 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="2.9.8">
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.0.0">
<PrivateAssets>All</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.8">
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="3.0.0">
<PrivateAssets>All</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" Condition="Exists('$(MSBuildThisFileDirectory)../../.git')">
Expand Down Expand Up @@ -129,6 +129,11 @@
<AutoGen>True</AutoGen>
<DependentUpon>ContainerResources.resx</DependentUpon>
</Compile>
<Compile Update="Core\Diagnostics\TracerMessages.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>TracerMessages.resx</DependentUpon>
</Compile>
<Compile Update="Core\Lifetime\LifetimeScopeResources.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
Expand All @@ -154,16 +159,26 @@
<AutoGen>True</AutoGen>
<DependentUpon>ServiceRegistrationInfoResources.resx</DependentUpon>
</Compile>
<Compile Update="Core\Resolving\CircularDependencyDetectorResources.Designer.cs">
<Compile Update="Core\Resolving\Middleware\CircularDependencyDetectorMessages.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>CircularDependencyDetectorResources.resx</DependentUpon>
<DependentUpon>CircularDependencyDetectorMessages.resx</DependentUpon>
</Compile>
<Compile Update="Core\Resolving\ComponentActivationResources.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>ComponentActivationResources.resx</DependentUpon>
</Compile>
<Compile Update="Core\Resolving\Middleware\MiddlewareMessages.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>MiddlewareMessages.resx</DependentUpon>
</Compile>
<Compile Update="Core\Resolving\Pipeline\ResolvePipelineBuilderMessages.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>ResolvePipelineBuilderMessages.resx</DependentUpon>
</Compile>
<Compile Update="Core\Resolving\ResolveOperationResources.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
Expand Down Expand Up @@ -330,6 +345,10 @@
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>ContainerResources.Designer.cs</LastGenOutput>
</EmbeddedResource>
<EmbeddedResource Update="Core\Diagnostics\TracerMessages.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>TracerMessages.Designer.cs</LastGenOutput>
</EmbeddedResource>
<EmbeddedResource Update="Core\Lifetime\LifetimeScopeResources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>LifetimeScopeResources.Designer.cs</LastGenOutput>
Expand All @@ -350,14 +369,22 @@
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>ServiceRegistrationInfoResources.Designer.cs</LastGenOutput>
</EmbeddedResource>
<EmbeddedResource Update="Core\Resolving\CircularDependencyDetectorResources.resx">
<EmbeddedResource Update="Core\Resolving\Middleware\CircularDependencyDetectorMessages.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>CircularDependencyDetectorResources.Designer.cs</LastGenOutput>
<LastGenOutput>CircularDependencyDetectorMessages.Designer.cs</LastGenOutput>
</EmbeddedResource>
<EmbeddedResource Update="Core\Resolving\ComponentActivationResources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>ComponentActivationResources.Designer.cs</LastGenOutput>
</EmbeddedResource>
<EmbeddedResource Update="Core\Resolving\Middleware\MiddlewareMessages.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>MiddlewareMessages.Designer.cs</LastGenOutput>
</EmbeddedResource>
<EmbeddedResource Update="Core\Resolving\Pipeline\ResolvePipelineBuilderMessages.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>ResolvePipelineBuilderMessages.Designer.cs</LastGenOutput>
</EmbeddedResource>
<EmbeddedResource Update="Core\Resolving\ResolveOperationResources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>ResolveOperationResources.Designer.cs</LastGenOutput>
Expand Down
12 changes: 8 additions & 4 deletions src/Autofac/Builder/IRegistrationBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
using System.Collections.Generic;
using System.ComponentModel;
using Autofac.Core;
using Autofac.Core.Resolving.Pipeline;

namespace Autofac.Builder
{
Expand All @@ -39,6 +40,12 @@ namespace Autofac.Builder
/// <typeparam name="TRegistrationStyle">Registration style type.</typeparam>
public interface IRegistrationBuilder<out TLimit, out TActivatorData, out TRegistrationStyle>
{
/// <summary>
/// Gets the registration data.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
RegistrationData RegistrationData { get; }

/// <summary>
/// Gets the activator data.
/// </summary>
Expand All @@ -51,11 +58,8 @@ public interface IRegistrationBuilder<out TLimit, out TActivatorData, out TRegis
[EditorBrowsable(EditorBrowsableState.Never)]
TRegistrationStyle RegistrationStyle { get; }

/// <summary>
/// Gets the registration data.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
RegistrationData RegistrationData { get; }
IResolvePipelineBuilder ResolvePipeline { get; }

/// <summary>
/// Configure the component so that instances are never disposed by the container.
Expand Down
26 changes: 16 additions & 10 deletions src/Autofac/Builder/RegistrationBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@
using System.Reflection;
using Autofac.Core;
using Autofac.Core.Activators.Delegate;
using Autofac.Core.Pipeline;
using Autofac.Core.Registration;
using Autofac.Core.Resolving.Pipeline;

namespace Autofac.Builder
{
Expand Down Expand Up @@ -135,6 +137,7 @@ public static class RegistrationBuilder
builder.RegistrationStyle.Id,
builder.RegistrationData,
builder.ActivatorData.Activator,
builder.ResolvePipeline,
builder.RegistrationData.Services.ToArray(),
builder.RegistrationStyle.Target,
builder.RegistrationStyle.IsAdapterForIndividualComponent);
Expand All @@ -146,15 +149,17 @@ public static class RegistrationBuilder
/// <param name="id">Id of the registration.</param>
/// <param name="data">Registration data.</param>
/// <param name="activator">Activator.</param>
/// <param name="pipelineBuilder">The component registration's resolve pipeline builder.</param>
/// <param name="services">Services provided by the registration.</param>
/// <returns>An IComponentRegistration.</returns>
public static IComponentRegistration CreateRegistration(
Guid id,
RegistrationData data,
IInstanceActivator activator,
IResolvePipelineBuilder pipelineBuilder,
Service[] services)
{
return CreateRegistration(id, data, activator, services, null);
return CreateRegistration(id, data, activator, pipelineBuilder, services, null);
}

/// <summary>
Expand All @@ -163,6 +168,7 @@ public static class RegistrationBuilder
/// <param name="id">Id of the registration.</param>
/// <param name="data">Registration data.</param>
/// <param name="activator">Activator.</param>
/// <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>
Expand All @@ -174,12 +180,14 @@ public static class RegistrationBuilder
Guid id,
RegistrationData data,
IInstanceActivator activator,
IResolvePipelineBuilder pipelineBuilder,
Service[] services,
IComponentRegistration? target,
bool isAdapterForIndividualComponent = false)
{
if (activator == null) throw new ArgumentNullException(nameof(activator));
if (data == null) throw new ArgumentNullException(nameof(data));
if (pipelineBuilder is null) throw new ArgumentNullException(nameof(pipelineBuilder));
if (services == null) throw new ArgumentNullException(nameof(services));

var limitType = activator.LimitType;
Expand All @@ -200,7 +208,12 @@ public static class RegistrationBuilder
}
}

// The pipeline builder fed into the registration is a copy, so that the original builder cannot be edited after the registration has been created,
// and the original does not contain any auto-added items.
var clonedPipelineBuilder = pipelineBuilder.Clone();

IComponentRegistration registration;

if (target == null)
{
registration = new ComponentRegistration(
Expand All @@ -209,6 +222,7 @@ public static class RegistrationBuilder
data.Lifetime,
data.Sharing,
data.Ownership,
clonedPipelineBuilder,
services,
data.Metadata);
}
Expand All @@ -220,21 +234,13 @@ public static class RegistrationBuilder
data.Lifetime,
data.Sharing,
data.Ownership,
clonedPipelineBuilder,
services,
data.Metadata,
target,
isAdapterForIndividualComponent);
}

foreach (var p in data.PreparingHandlers)
registration.Preparing += p;

foreach (var ac in data.ActivatingHandlers)
registration.Activating += ac;

foreach (var ad in data.ActivatedHandlers)
registration.Activated += ad;

return registration;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@
using Autofac.Core;
using Autofac.Core.Activators.Reflection;
using Autofac.Core.Lifetime;
using Autofac.Core.Resolving;
using Autofac.Core.Resolving.Pipeline;
using Autofac.Features.OwnedInstances;

namespace Autofac.Builder
Expand All @@ -46,6 +48,7 @@ public RegistrationBuilder(Service defaultService, TActivatorData activatorData,
ActivatorData = activatorData;
RegistrationStyle = style;
RegistrationData = new RegistrationData(defaultService);
ResolvePipeline = new ResolvePipelineBuilder();
}

/// <summary>
Expand All @@ -66,6 +69,9 @@ public RegistrationBuilder(Service defaultService, TActivatorData activatorData,
[EditorBrowsable(EditorBrowsableState.Never)]
public RegistrationData RegistrationData { get; }

[EditorBrowsable(EditorBrowsableState.Never)]
public IResolvePipelineBuilder ResolvePipeline { get; }

/// <summary>
/// Configure the component so that instances are never disposed by the container.
/// </summary>
Expand Down Expand Up @@ -378,7 +384,18 @@ public RegistrationBuilder(Service defaultService, TActivatorData activatorData,
{
if (handler == null) throw new ArgumentNullException(nameof(handler));

RegistrationData.PreparingHandlers.Add((s, e) => handler(e));
ResolvePipeline.Use(nameof(OnPreparing), PipelinePhase.ParameterSelection, (ctxt, next) =>
{
var args = new PreparingEventArgs(ctxt, ctxt.Service, ctxt.Registration, ctxt.Parameters);
handler(args);
ctxt.ChangeParameters(args.Parameters);
// Go down the pipeline now.
next(ctxt);
});

return this;
}

Expand All @@ -391,12 +408,18 @@ public RegistrationBuilder(Service defaultService, TActivatorData activatorData,
{
if (handler == null) throw new ArgumentNullException(nameof(handler));

RegistrationData.ActivatingHandlers.Add((s, e) =>
// Activation events have to run at the start of the phase, to make sure
// that the event handlers run in the same order as they were added to the registration.
ResolvePipeline.Use(nameof(OnActivating), PipelinePhase.Activation, MiddlewareInsertionMode.StartOfPhase, (ctxt, next) =>
{
var args = new ActivatingEventArgs<TLimit>(e.Context, e.Component, e.Parameters, (TLimit)e.Instance, e.Service);
next(ctxt);
var args = new ActivatingEventArgs<TLimit>(ctxt, ctxt.Service, ctxt.Registration, ctxt.Parameters, (TLimit)ctxt.Instance!);
handler(args);
e.Instance = args.Instance;
ctxt.Instance = args.Instance;
});

return this;
}

Expand All @@ -409,8 +432,32 @@ public RegistrationBuilder(Service defaultService, TActivatorData activatorData,
{
if (handler == null) throw new ArgumentNullException(nameof(handler));

RegistrationData.ActivatedHandlers.Add(
(s, e) => handler(new ActivatedEventArgs<TLimit>(e.Context, e.Component, e.Parameters, (TLimit)e.Instance, e.Service)));
// Need to insert OnActivated at the start of the phase, to ensure we attach to RequestCompleting in the same order
// as calls to OnActivated.
ResolvePipeline.Use(nameof(OnActivated), PipelinePhase.Activation, MiddlewareInsertionMode.StartOfPhase, (ctxt, next) =>
{
// Go down the pipeline first.
next(ctxt);
if (!ctxt.NewInstanceActivated)
{
return;
}
// Make sure we use the instance at this point, before it is replaced by any decorators.
var newInstance = (TLimit)ctxt.Instance!;
// In order to behave in the same manner as the original activation handler,
// we need to attach to the RequestCompleting event so these run at the end after everything else.
ctxt.RequestCompleting += (sender, evArgs) =>
{
var ctxt = evArgs.RequestContext;
var args = new ActivatedEventArgs<TLimit>(ctxt, ctxt.Service, ctxt.Registration, ctxt.Parameters, newInstance);
handler(args);
};
});

return this;
}

Expand All @@ -423,10 +470,30 @@ public RegistrationBuilder(Service defaultService, TActivatorData activatorData,
/// <returns>A registration builder allowing further configuration of the component.</returns>
public IRegistrationBuilder<TLimit, TActivatorData, TRegistrationStyle> PropertiesAutowired(IPropertySelector propertySelector, bool allowCircularDependencies)
{
if (allowCircularDependencies)
RegistrationData.ActivatedHandlers.Add((s, e) => AutowiringPropertyInjector.InjectProperties(e.Context, e.Instance, propertySelector, e.Parameters));
else
RegistrationData.ActivatingHandlers.Add((s, e) => AutowiringPropertyInjector.InjectProperties(e.Context, e.Instance, propertySelector, e.Parameters));
ResolvePipeline.Use(nameof(PropertiesAutowired), PipelinePhase.Activation, (ctxt, next) =>
{
// Continue down the pipeline.
next(ctxt);
if (!ctxt.NewInstanceActivated)
{
return;
}
if (allowCircularDependencies)
{
// If we are allowing circular deps, then we need to run when all requests have completed (similar to Activated).
ctxt.RequestCompleting += (o, args) =>
{
var evCtxt = args.RequestContext;
AutowiringPropertyInjector.InjectProperties(evCtxt, evCtxt.Instance!, propertySelector, evCtxt.Parameters);
};
}
else
{
AutowiringPropertyInjector.InjectProperties(ctxt, ctxt.Instance!, propertySelector, ctxt.Parameters);
}
});

return this;
}
Expand Down

0 comments on commit ff22873

Please sign in to comment.