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

WIP: Pipelines #1121

Merged
merged 11 commits into from
May 27, 2020
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
44 changes: 38 additions & 6 deletions src/Autofac/Autofac.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -40,16 +40,21 @@
<PropertyGroup Condition="'$(TargetFramework)' == 'netstandard2.0' or '$(TargetFramework)' == 'net461'">
<NoWarn>$(NoWarn);8600;8601;8602;8603;8604</NoWarn>
</PropertyGroup>
<ItemGroup>
alistairjevans marked this conversation as resolved.
Show resolved Hide resolved
<Compile Remove="Core\Pipeline\**" />
<EmbeddedResource Remove="Core\Pipeline\**" />
<None Remove="Core\Pipeline\**" />
</ItemGroup>

<ItemGroup>
<None Include="..\..\build\icon.png" Pack="true" PackagePath="\" />
</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 +134,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 +164,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 +350,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 +374,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 IComponentRegistration CreateRegistration<TLimit, TActivatorData,
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 IComponentRegistration CreateRegistration<TLimit, TActivatorData,
/// <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 IComponentRegistration CreateRegistration(
/// <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 IComponentRegistration CreateRegistration(
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 IComponentRegistration CreateRegistration(
}
}

// 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 IComponentRegistration CreateRegistration(
data.Lifetime,
data.Sharing,
data.Ownership,
clonedPipelineBuilder,
services,
data.Metadata);
}
Expand All @@ -220,21 +234,13 @@ public static IComponentRegistration CreateRegistration(
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 IRegistrationBuilder<TLimit, TActivatorData, TRegistrationStyle> OnPrepar
{
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 IRegistrationBuilder<TLimit, TActivatorData, TRegistrationStyle> OnActiva
{
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 IRegistrationBuilder<TLimit, TActivatorData, TRegistrationStyle> OnActiva
{
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 IRegistrationBuilder<TLimit, TActivatorData, TRegistrationStyle> OnActiva
/// <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
Loading