Skip to content

Commit 3fac7b8

Browse files
committed
Add GetRegistrationId extension method for single-component registrations (#1327)
Adds a GetRegistrationId<TLimit, TActivatorData> extension method on IRegistrationBuilder<TLimit, TActivatorData, SingleRegistrationStyle> that returns the stable Guid identity of a registration before the container is built. The generic constraint to SingleRegistrationStyle ensures the method is only callable on RegisterType/Register/RegisterInstance builders — attempting to call it on open-generic, scanning, adapter, or decorator registrations is a compile-time error.
1 parent 0e724c2 commit 3fac7b8

2 files changed

Lines changed: 133 additions & 0 deletions

File tree

src/Autofac/RegistrationExtensions.cs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -780,4 +780,51 @@ public static IRegistrationBuilder<TLimit, TActivatorData, TStyle>
780780
var tags = new[] { MatchingScopeLifetimeTags.RequestLifetimeScopeTag }.Concat(lifetimeScopeTags).ToArray();
781781
return registration.InstancePerMatchingLifetimeScope(tags);
782782
}
783+
784+
/// <summary>
785+
/// Gets the unique <see cref="Guid"/> identity assigned to a single-component registration.
786+
/// </summary>
787+
/// <typeparam name="TLimit">Registration limit type.</typeparam>
788+
/// <typeparam name="TActivatorData">Activator data type.</typeparam>
789+
/// <param name="registration">The registration whose identity to retrieve.</param>
790+
/// <returns>
791+
/// The <see cref="Guid"/> that uniquely identifies this registration. This is the same value
792+
/// that will appear on the resulting <see cref="Core.IComponentRegistration.Id"/> after the
793+
/// container is built.
794+
/// </returns>
795+
/// <exception cref="ArgumentNullException">
796+
/// Thrown if <paramref name="registration" /> is <see langword="null" />.
797+
/// </exception>
798+
/// <remarks>
799+
/// <para>
800+
/// Each single-component registration (created via <see cref="RegisterType{TImplementer}(ContainerBuilder)"/>,
801+
/// <see cref="Register{T}(ContainerBuilder, Func{IComponentContext, T})"/>, or
802+
/// <see cref="RegisterInstance{T}(ContainerBuilder, T)"/>) is assigned a stable <see cref="Guid"/>
803+
/// at the time the registration builder is created. This ID flows through unchanged to
804+
/// <see cref="Core.IComponentRegistration.Id"/> when the container is built.
805+
/// </para>
806+
/// <para>
807+
/// A common use case is to capture the registration ID before building the container and use
808+
/// it as a key in <see cref="ContainerBuilder.Properties"/> to associate per-registration
809+
/// state or metadata, then look that state up at resolve time via
810+
/// <see cref="Core.IComponentRegistration.Id"/>.
811+
/// </para>
812+
/// <para>
813+
/// This method is only available for single-component registrations
814+
/// (<see cref="Builder.SingleRegistrationStyle"/>). Multi-component registrations such as
815+
/// open-generic registrations (via <c>RegisterGeneric</c>), assembly-scanning registrations,
816+
/// adapters, and decorators do not have a single stable identity and this method is not
817+
/// callable on them — attempting to do so results in a compile-time error.
818+
/// </para>
819+
/// </remarks>
820+
public static Guid GetRegistrationId<TLimit, TActivatorData>(
821+
this IRegistrationBuilder<TLimit, TActivatorData, SingleRegistrationStyle> registration)
822+
{
823+
if (registration == null)
824+
{
825+
throw new ArgumentNullException(nameof(registration));
826+
}
827+
828+
return registration.RegistrationStyle.Id;
829+
}
783830
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
// Copyright (c) Autofac Project. All rights reserved.
2+
// Licensed under the MIT License. See LICENSE in the project root for license information.
3+
4+
using Autofac.Builder;
5+
using Autofac.Core;
6+
7+
namespace Autofac.Test.Builder;
8+
9+
public class GetRegistrationIdTests
10+
{
11+
[Fact]
12+
public void GetRegistrationId_NullRegistration_ThrowsArgumentNullException()
13+
{
14+
// Issue #1327
15+
IRegistrationBuilder<object, SimpleActivatorData, SingleRegistrationStyle> registration = null;
16+
Assert.Throws<ArgumentNullException>(() => registration.GetRegistrationId());
17+
}
18+
19+
[Fact]
20+
public void GetRegistrationId_RegisterType_MatchesBuiltComponentRegistrationId()
21+
{
22+
// Issue #1327 - The ID returned before build matches IComponentRegistration.Id after build.
23+
var cb = new ContainerBuilder();
24+
var rb = cb.RegisterType<MyComponent>();
25+
var capturedId = rb.GetRegistrationId();
26+
var container = cb.Build();
27+
28+
Assert.True(container.ComponentRegistry.TryGetRegistration(new TypedService(typeof(MyComponent)), out var cr));
29+
Assert.Equal(capturedId, cr.Id);
30+
}
31+
32+
[Fact]
33+
public void GetRegistrationId_RegisterDelegate_MatchesBuiltComponentRegistrationId()
34+
{
35+
// Issue #1327 - The ID returned before build matches IComponentRegistration.Id after build.
36+
var cb = new ContainerBuilder();
37+
var rb = cb.Register(_ => new MyComponent());
38+
var capturedId = rb.GetRegistrationId();
39+
var container = cb.Build();
40+
41+
Assert.True(container.ComponentRegistry.TryGetRegistration(new TypedService(typeof(MyComponent)), out var cr));
42+
Assert.Equal(capturedId, cr.Id);
43+
}
44+
45+
[Fact]
46+
public void GetRegistrationId_RegisterInstance_MatchesBuiltComponentRegistrationId()
47+
{
48+
// Issue #1327 - The ID returned before build matches IComponentRegistration.Id after build.
49+
var instance = new MyComponent();
50+
var cb = new ContainerBuilder();
51+
var rb = cb.RegisterInstance(instance);
52+
var capturedId = rb.GetRegistrationId();
53+
var container = cb.Build();
54+
55+
Assert.True(container.ComponentRegistry.TryGetRegistration(new TypedService(typeof(MyComponent)), out var cr));
56+
Assert.Equal(capturedId, cr.Id);
57+
}
58+
59+
[Fact]
60+
public void GetRegistrationId_CalledTwice_ReturnsSameGuid()
61+
{
62+
// Issue #1327 - The ID is stable across multiple calls on the same builder.
63+
var cb = new ContainerBuilder();
64+
var rb = cb.RegisterType<MyComponent>();
65+
66+
var id1 = rb.GetRegistrationId();
67+
var id2 = rb.GetRegistrationId();
68+
69+
Assert.Equal(id1, id2);
70+
}
71+
72+
[Fact]
73+
public void GetRegistrationId_TwoDistinctRegistrations_ReturnDifferentGuids()
74+
{
75+
// Issue #1327 - Each registration gets its own unique ID.
76+
var cb = new ContainerBuilder();
77+
var rb1 = cb.RegisterType<MyComponent>();
78+
var rb2 = cb.Register(_ => new MyComponent());
79+
80+
Assert.NotEqual(rb1.GetRegistrationId(), rb2.GetRegistrationId());
81+
}
82+
83+
private class MyComponent
84+
{
85+
}
86+
}

0 commit comments

Comments
 (0)