autofac / Autofac.Extensions.DependencyInjection

Autofac implementation of the interfaces in Microsoft.Extensions.DependencyInjection.Abstractions, the .NET Core dependency injection abstraction.
MIT License
194 stars 47 forks source link

FromKeyedServices is not working #121

Closed LechAtAtlasCopco closed 1 month ago

LechAtAtlasCopco commented 1 month ago

Describe the bug

Service use Microsoft.Extensions.DependencyInjection

public Service([FromKeyedServices("whatever")] Channel whateverChannel) should work

Issue mentioed here before: https://github.com/autofac/Autofac/issues/1422

To reproduce

https://github.com/LechAtAtlasCopco/AutoFacKeyedDemo ClassLibrary use Microsoft.Extensions.DependencyInjection

ConsoleApp use Autofac

when you change the ctor of Worker from FromKeyedServices to KeyFilter is working again.

LechAtAtlasCopco commented 1 month ago

I loooke into WithAttributeFiltering extentions method. And modify.

` public static class RegistrationExtensions { public static IRegistrationBuilder<TLimit, TReflectionActivatorData, TRegistrationStyle> WithFromKeyedServices<TLimit, TReflectionActivatorData, TRegistrationStyle>( this IRegistrationBuilder<TLimit, TReflectionActivatorData, TRegistrationStyle> builder) where TReflectionActivatorData : ReflectionActivatorData { if (builder == null) { throw new ArgumentNullException(nameof(builder)); }

    return builder.WithParameter(
        (parameterInfo, componentContext) =>
        {
            var filter = parameterInfo.GetCustomAttributes<FromKeyedServicesAttribute>(true).FirstOrDefault();
            if (filter == null)
            {
                throw new ArgumentNullException(nameof(filter), "Filter cannot be null");
            }

            var isRegistered = componentContext.ComponentRegistry.IsRegistered(new Autofac.Core.KeyedService(filter.Key, parameterInfo.ParameterType));
            return isRegistered;
        },
        (parameterInfo, context) =>
        {
            var filter = parameterInfo.GetCustomAttributes<FromKeyedServicesAttribute>(true).First();
            if (filter.Key is string key && context.TryResolveKeyed(key, parameterInfo.ParameterType, out var value))
            {
                return value;
            }

            throw new InvalidOperationException($"No service registered with key: {filter.Key}");
        });
}

}`

Should i add this in repo?

tillig commented 1 month ago

The problem is you can't mix-and-match. If you're registering things with Autofac, you need to use Autofac constructs. If you're registering them with Microsoft things (like Microsoft filter attributes) you need to use Microsoft constructs.

Which is to say:

If you want FromKeyedServices to work, you need to register it using the Microsoft mechanism and then push it into your Autofac container.

// Register the Microsoft-using stuff via Microsoft registrations.
var services = new ServiceCollection();
services.AddTransient<Worker>();

// Bring that into the Autofac container.
var cb = new ContainerBuilder();
cb.Populate(services);

// You should be able to register the keyed service using Autofac, but you could stick with the MS format if you want.
cb.RegisterType<Dependency>().Keyed<Dependency>("the-key");

Again, the problem really is the attempt to mix-and-match. Autofac core doesn't "just understand" all the Microsoft stuff - this Autofac.Extensions.DependencyInjection library is a sort of "translator" to convert it. However, it's not a bi-directional bit of magic that suddenly bridges the gaps. For example, you'll probably notice there's special crazy middleware that wraps all the Microsoft-based keyed things that get registered because it's so different than how Autofac works and adding that translation layer is expensive performance-wise - so it only gets added to the exact set of things that need it.