Asp.Net Core: registreer implementatie met meerdere interfaces en lifestyle Singleton

Rekening houdend met de volgende interface- en klassedefinities:

public interface IInterface1 { }
public interface IInterface2 { }
public class MyClass : IInterface1, IInterface2 { }

is er een manier om één instantie van MyClasste registreren met meerdere interfaces zoals deze:

...
services.AddSingleton<IInterface1, IInterface2, MyClass>();
...

en los dit enkele exemplaar van MyClassop met verschillende interfaces zoals deze:

IInterface1 interface1 = app.ApplicationServices.GetService<IInterface1>();
IInterface2 interface2 = app.ApplicationServices.GetService<IInterface2>();

Antwoord 1, autoriteit 100%

De serviceverzameling is per definitie een verzameling ServiceDescriptors, die paren zijn van servicetype en implementatietype.

Je kunt dit echter omzeilen door je eigen providerfunctie te creëren, zoiets als dit (bedankt gebruiker7224827):

services.AddSingleton<IInterface1>();
services.AddSingleton<IInterface2>(x => x.GetService<IInterface1>());

Meer opties hieronder:

private static MyClass ClassInstance;
public void ConfigureServices(IServiceCollection services)
{
    ClassInstance = new MyClass();
    services.AddSingleton<IInterface1>(provider => ClassInstance);
    services.AddSingleton<IInterface2>(provider => ClassInstance);
}

Een andere manier zou zijn:

public void ConfigureServices(IServiceCollection services)
{
    ClassInstance = new MyClass();
    services.AddSingleton<IInterface1>(ClassInstance);
    services.AddSingleton<IInterface2>(ClassInstance);
}

Waar we gewoon dezelfde instantie leveren.


Antwoord 2, autoriteit 24%

Je kunt het antwoord van user7224827 inpakken om een ​​mooie uitbreidingsmethode te maken die overeenkomt met je oorspronkelijke gewenste API:

   public static class ServiceCollectionExt
    {
        public static void AddSingleton<I1, I2, T>(this IServiceCollection services) 
            where T : class, I1, I2
            where I1 : class
            where I2 : class
        {
            services.AddSingleton<I1, T>();
            services.AddSingleton<I2, T>(x => (T) x.GetService<I1>());
        }
    }

Antwoord 3, autoriteit 11%

Een andere optie om het DI-mechanisme te behouden is als volgt:

services.AddSingleton<MyClass>();
services.AddSingleton<Interface1>(p => p.GetRequiredService<MyClass>());
services.AddSingleton<Interface2>(x => x.GetRequiredService<MyClass>());

Antwoord 4, autoriteit 4%

De bovenstaande antwoorden zijn cool, met dat als inspiratiebron heb ik het veranderd om gebruik te maken van de typebeperkingen die bij het framework horen, waardoor casten en de meest nuttige compilerfouten worden vermeden wanneer ik klassen en interfaces gebruik die niet compatibel zijn . Compilerfouten zijn gemakkelijker op te lossen dan een “wat de f** waarom is dit null” tijdens runtime;.)

[TestClass()]
public class ServiceCollectionExtensionTests
{
    interface MyInterface
    {
        Guid Id { get; }
    }
    class MyClas : MyInterface
    {
        Guid id = Guid.NewGuid();
        public Guid Id => id;
    }
    [TestMethod()]
    public void AddSingletonTest()
    {
        var service = new ServiceCollection()
                            .AddSingleton<MyClas>()
                            .ReUseSingleton<MyClas,MyInterface>()
                            .BuildServiceProvider();
        var foo1 = service.GetService<MyClas>();
        var foo2 = service.GetService<MyInterface>();
        Assert.AreEqual(foo1.Id, foo2.Id);
        Assert.AreSame(foo1, foo2);
    }
}

De code voor de “ReUseXYZ” is hier:

namespace Microsoft.Extensions.DependencyInjection
{
    /// <summary>
    /// Class ServiceCollectionExtension allowing to registered 
    /// derived implementations of a already registered service 
    /// to re-use the same service without having to register 
    /// the same class 2x ending up with 2 instances of the 
    /// same type in the same scope.
    /// </summary>
    public static class ServiceCollectionExtension
    {
        /// <summary>
        /// Adds a singleton service of the type specified in TBase with a factory based on the registered type T that has been specified in implementation factory to the specified <see cref="Microsoft.Extensions.DependencyInjection.IServiceCollection"/>.
        /// </summary>
        /// <typeparam name="T">The registered type</typeparam>
        /// <typeparam name="TBase">The type that T is derived from, can be the base class or base interface.</typeparam>
        /// <param name="services">The services.</param>
        /// <returns>the IServiceCollection used to register the interface with.</returns>
        public static IServiceCollection ReUseSingleton<T, TBase>(this IServiceCollection services)
            where T : TBase
            where TBase : class
        {
            services.AddSingleton<TBase>(a => a.GetRequiredService<T>());
            return services;
        }
        /// <summary>
        /// Adds a transient service of the type specified in TBase with a factory based on the registered type T that has been specified in implementation factory to the specified <see cref="Microsoft.Extensions.DependencyInjection.IServiceCollection"/>.
        /// </summary>
        /// <typeparam name="T">The registered type</typeparam>
        /// <typeparam name="TBase">The type that T is derived from, can be the base class or base interface.</typeparam>
        /// <typeparam name="TS">The IServiceCollection instance to extend.</typeparam>
        /// <param name="services">The services.</param>
        /// <returns>the IServiceCollection used to register the interface with.</returns>
        public static IServiceCollection ReUseTransient<T, TBase>(this IServiceCollection services)
            where T : TBase
            where TBase : class
        {
            services.AddTransient<TBase>(a => a.GetRequiredService<T>());
            return services;
        }
        /// <summary>
        /// Adds a scoped service of the type specified in TBase with a factory based on the registered type T that has been specified in implementation factory to the specified <see cref="Microsoft.Extensions.DependencyInjection.IServiceCollection"/>.
        /// </summary>
        /// <typeparam name="T">The registered type</typeparam>
        /// <typeparam name="TBase">The type that T is derived from, can be the base class or base interface.</typeparam>
        /// <typeparam name="TS">The IServiceCollection instance to extend.</typeparam>
        /// <param name="services">The services.</param>
        /// <returns>the IServiceCollection used to register the interface with.</returns>
        public static IServiceCollection ReUseScoped<T, TBase>(this IServiceCollection services)
            where T : TBase
            where TBase : class
        {
            services.AddScoped<TBase>(a => a.GetRequiredService<T>());
            return services;
        }
    }
}

Other episodes