Add observability hooks to IServiceCollection (OnAdded, OnReplaced, OnRemoved) #128166
Replies: 2 comments
-
|
In this case, a class ObservableServiceCollection : Collection<ServiceDescriptor>, IServiceCollection
{
public event Action<ServiceDescriptor>? OnAdded;
public event Action<ServiceDescriptor, ServiceDescriptor>? OnReplaced;
public event Action<ServiceDescriptor>? OnRemoved;
protected override void InsertItem(int index, ServiceDescriptor item)
{
OnAdded?.Invoke(item);
base.InsertItem(index, item);
}
protected override void SetItem(int index, ServiceDescriptor item)
{
OnReplaced?.Invoke(this[index], item);
base.SetItem(index, item);
}
protected override void RemoveItem(int index)
{
OnRemoved?.Invoke(this[index]);
base.RemoveItem(index);
}
} |
Beta Was this translation helpful? Give feedback.
-
|
Do you have an example of what you've done so far that has led you to want a feature like this? Working with an /// <summary>
/// A service provider factory that validates service registrations to ensure no captive dependencies exist.
/// </summary>
/// <remarks>
/// This factory is designed to enforce best practices in dependency injection by
/// detecting and preventing the use of captive dependencies, which can lead to
/// unintended behavior in applications.
/// </remarks>
public class ValidatingServiceProviderFactory : IServiceProviderFactory<IServiceCollection>
{
public IServiceProvider CreateServiceProvider(IServiceCollection services)
{
ThrowIfCaptiveDependencies(services);
return new DefaultServiceProviderFactory().CreateServiceProvider(services);
}
private static void ThrowIfCaptiveDependencies(IServiceCollection services)
{
foreach (var descriptor in services.Where(s => s.ImplementationType is not null && s.Lifetime == ServiceLifetime.Singleton))
{
var q = (from p in descriptor.ImplementationType!
.GetConstructors()
.SelectMany(p => p.GetParameters())
join s in services on p.ParameterType equals s.ServiceType
select new {s.Lifetime, s.ServiceType, s.ImplementationType}).ToArray();
if (q.Length != 0)
{
throw new InvalidOperationException($"{descriptor.ImplementationType} with a Singleton lifetime depends on "
+ $"{string.Join(" and ", q.Select(g => $"{g.ImplementationType} with a {g.Lifetime} lifetime"))}.");
}
}
}
IServiceCollection IServiceProviderFactory<IServiceCollection>.CreateBuilder(IServiceCollection services)
=> services;
} |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
Hi everyone,
I'd like to open a discussion about introducing observability features to
IServiceCollectionto allow developers to hook into service registration mutations.The Problem
When building comprehensive, modular frameworks or custom enterprise architectures, it is often necessary to react dynamically to dependency injection registrations. Currently, if we want to intercept when a
ServiceDescriptoris added, replaced, or removed, we have to create a custom wrapper aroundIServiceCollection(by implementingIList<ServiceDescriptor>). While this workaround functions, it can be cumbersome, introduces unnecessary allocations, and a standardized, low-overhead approach within the base abstraction would be significantly cleaner.Proposed Solution
It would be highly beneficial to have built-in hooks or events—such as
OnAdded,OnReplaced, andOnRemoved—that trigger during the container configuration phase (before theIServiceProvideris built).Having native observability would allow framework authors to easily:
Caveats and Considerations
I am not entirely sure if there are strict dependency injection specifications, backward-compatibility constraints (since
IServiceCollectionis heavily relied upon as a standardIList), or underlying architectural rules that limit this kind of API addition. I am just putting this concept forward to see if it aligns with the runtime's design philosophy and to gather feedback from the team and community.Would love to hear your thoughts on the feasibility of this, or if there is already an endorsed pattern for achieving this without wrapping the collection!
Beta Was this translation helpful? Give feedback.
All reactions