jab icon indicating copy to clipboard operation
jab copied to clipboard

Add support for registering a service implementing multiple interfaces

Open sensslen opened this issue 1 year ago • 3 comments

Unfortunately Jab does not currently allow the registration of services implementing multiple interfaces (similar to how MEDI allows for registering a service that implements multiple interfaces).

sensslen avatar Oct 30 '24 17:10 sensslen

I would very much like if this option was added. I have multiple instances of where a single service class implements multiple interfaces that I am using

retroandchill avatar May 14 '25 01:05 retroandchill

As far as I know, the way you would do it with MEDI is the same as Jab with a common instance or factory for registration. It is more difficult comparatively to extend Jab for more complex registration scenarios though. I wonder if it would be possible to extend it at the source generation level without spending too much time.

public class MyClass : IMyInterface1, IMyInterface2;

public interface IMyInterface1;

public interface IMyInterface2;

public interface IMyInterface3;

public interface IMyInterface4;

public class MyClass2 : IMyInterface3, IMyInterface4;

public interface IMyInterface5;

public interface IMyInterface6;

public class MyClass3 : IMyInterface5, IMyInterface6;


public static class MediExtensions
{
    public static IServiceCollection AddSingletonAs<TImpl>(
        this IServiceCollection services,
        IEnumerable<Type> interfaces
    ) where TImpl : class
    {
        services.AddSingleton<TImpl>();
        foreach (var interf in interfaces)
        {
            services.AddSingleton(interf, sp => sp.GetRequiredService<TImpl>());
        }

        return services;
    }
}


[ServiceProvider]
[Singleton<MyClass>]
[Singleton<IMyInterface1>(Factory = nameof(GetMyClass))]
[Singleton<IMyInterface2>(Factory = nameof(GetMyClass))]
[Singleton<IMyInterface3>(Instance = nameof(GetMyClass2))]
[Singleton<IMyInterface4>(Instance = nameof(GetMyClass2))]
[Singleton<MyClass3>]
[Singleton<IMyInterface5, MyClass3>]
[Singleton<IMyInterface6, MyClass3>]
public partial class MyContainer
{
    public MyClass GetMyClass(MyClass myClass) => myClass;
    public MyClass2 GetMyClass2 = new MyClass2();
}

public class Sample
{
    [Fact]
    public void JabTest_FactoryAndInstance()
    {
        var container = new MyContainer();
        var instance1 = container.GetRequiredService<IMyInterface1>();
        var instance2 = container.GetRequiredService<IMyInterface2>();
        Assert.True(instance1 == instance2);

        var instance3 = container.GetRequiredService<IMyInterface3>();
        var instance4 = container.GetRequiredService<IMyInterface4>();
        Assert.True(instance3 == instance4);
    }

    [Fact]
    public void MediTest_FactoryAndInstance()
    {
        var services = new ServiceCollection();

        MyClass MyClassFactory(IServiceProvider sp) => sp.GetRequiredService<MyClass>();

        services
            .AddSingleton<MyClass>()
            .AddSingleton<IMyInterface1>(MyClassFactory)
            .AddSingleton<IMyInterface2>(MyClassFactory);

        var myClass2 = new MyClass2();
        services
            .AddSingleton<IMyInterface3>(myClass2)
            .AddSingleton<IMyInterface4>(myClass2);


        var provider = services.BuildServiceProvider();
        var instance1 = provider.GetRequiredService<IMyInterface1>();
        var instance2 = provider.GetRequiredService<IMyInterface2>();
        Assert.True(instance1 == instance2);

        var instance3 = provider.GetRequiredService<IMyInterface3>();
        var instance4 = provider.GetRequiredService<IMyInterface4>();
        Assert.True(instance3 == instance4);
    }

    [Fact]
    public void MediTest_IntuitiveButWrongSyntax()
    {
        var services = new ServiceCollection();
        services
            .AddSingleton<MyClass>()
            .AddSingleton<IMyInterface1,MyClass>()
            .AddSingleton<IMyInterface2,MyClass>();


        var provider = services.BuildServiceProvider();
        var instance1 = provider.GetRequiredService<IMyInterface1>();
        var instance2 = provider.GetRequiredService<IMyInterface2>();
        Assert.False(instance1 == instance2);
    }

    [Fact]
    public void JabTest_IntuitiveButWrongSyntax()
    {
        var container = new MyContainer();
        var instance5 = container.GetRequiredService<IMyInterface5>();
        var instance6 = container.GetRequiredService<IMyInterface6>();
        Assert.False(instance5 == instance6);

    }
    
    [Fact]
    public void MediTest_WithExtension()
    {
        var services = new ServiceCollection();
        services.AddSingletonAs<MyClass>([typeof(IMyInterface1), typeof(IMyInterface2)]);


        var provider = services.BuildServiceProvider();
        var instance1 = provider.GetRequiredService<IMyInterface1>();
        var instance2 = provider.GetRequiredService<IMyInterface2>();
        Assert.True(instance1 == instance2);
    }
}

tbui17 avatar Jul 07 '25 20:07 tbui17

I requested a similar feature in #133 a long time ago. Then, went on and wrote my own source generator which has this feature. Here is the relevant README section if you want to check out.

notanaverageman avatar Sep 19 '25 11:09 notanaverageman