LightInject icon indicating copy to clipboard operation
LightInject copied to clipboard

How to register class with multiple interfaces?

Open hubaxis opened this issue 10 years ago • 3 comments

I tried to register class with multiple interfaces but i noticed strange behaviour. Test below failed. How i can correctly register class with multiple interfaces?

    public static class ContainerHelper
    {

        public static void RegisterMultiple<TService, TInterface1, TInterface2>(this IContainer container, ILifetime lifetime = null)
            where TService : TInterface1, TInterface2
        {
            container.Register<TService>(lifetime);
            container.Register(f => (TInterface1)f.GetInstance<TService>());
            container.Register(f => (TInterface2)f.GetInstance<TService>());
        }

        public static void RegisterMultiple<TService, TInterface1, TInterface2, TInterface3>(this IContainer container, ILifetime lifetime = null)
    where TService : TInterface1, TInterface2, TInterface3
        {
            container.Register<TService>(lifetime);
            container.Register(f => (TInterface1)f.GetInstance<TService>());
            container.Register(f => (TInterface2)f.GetInstance<TService>());
            container.Register(f => (TInterface3)f.GetInstance<TService>());
        }

        public static void RegisterMultiple<TService, TInterface1, TInterface2, TInterface3, TInterface4>(this IContainer container, ILifetime lifetime = null)
where TService : TInterface1, TInterface2, TInterface3, TInterface4
        {
            container.Register<TService>(lifetime);
            container.Register(f => (TInterface1)f.GetInstance<TService>());
            container.Register(f => (TInterface2)f.GetInstance<TService>());
            container.Register(f => (TInterface3)f.GetInstance<TService>());
            container.Register(f => (TInterface4)f.GetInstance<TService>());
        }
    public interface IInterfaceThird
    {
        void Method3();
    }

    public interface IInterfaceOne
    {
        void Method2();
    }

    public interface IInterfaceTwo
    {
        void Method1();
    }
    public interface IInterfaceFourth
    {
        void Method4();
    }
    public class SomeService2 : IInterfaceFourth, IRunnable
    {
        public void Method4() { }

        public void Dispose() { }

        public void Start() { }

        public void Stop() { }
    }
    public class SomeService1 : IInterfaceOne, IInterfaceTwo, IInterfaceThird, IRunnable
    {
        public void Method1() { }
        public void Method2() { }
        public void Method3() { }
        public void Dispose()
        {

        }

        public void Start()
        {

        }

        public void Stop()
        {

        }
    }

        public void OneService_MultipleInterface_Test()
        {
            var cont = new Container();
            cont.RegisterMultiple<SomeService1, IInterfaceOne, IInterfaceTwo, IInterfaceThird,IRunnable>(new PerContainerLifetime());
            cont.RegisterMultiple<SomeService2, IInterfaceFourth, IRunnable>(new PerContainerLifetime());

            var serInt1 = cont.GetInstance<IInterfaceOne>();
            Assert.IsNotNull(serInt1);
            var serInt2 = cont.GetInstance<IInterfaceTwo>();
            Assert.IsNotNull(serInt2);

            var serInt3 = cont.GetInstance<IInterfaceThird>();
            Assert.IsNotNull(serInt3);
            Assert.IsTrue(ReferenceEquals(serInt1, serInt2));


            var runnbles=cont.GetAllInstances<IRunnable>().ToArray();
            // FAIL HERE 3 instead of 2
            Assert.AreEqual(2,runnbles.Length);
            CollectionAssert.Contains(runnbles, serInt1);
            CollectionAssert.Contains(runnbles, cont.GetInstance<IInterfaceFourth>());
        }

hubaxis avatar Jun 22 '15 07:06 hubaxis

Hi!

There is a couple of things happening here that explains the behaviour.

First of all, the default in LightInject is to apply variance when resolving multiple instances. This means that you will get all services that is compatible with the IRunnable interface including the concrete instance registered in the beginning of each RegisterMultiple method.

This behaviour can be changed like this:

 var cont = new ServiceContainer(new ContainerOptions() {EnableVariance = false});

In addition, if you register the same interface twice, LightInject will simply overwrite the existing registration. To make the registration unique and hence prevent the registration to be overwritten, we can use the type name of the implementing type.

Take a look at the following example

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using LightInject;

namespace ConsoleApplication3
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            var cont = new ServiceContainer(new ContainerOptions() {EnableVariance = false});
            cont.RegisterMultiple<SomeService1, IInterfaceOne, IInterfaceTwo, IInterfaceThird, IRunnable>(new PerContainerLifetime());
            cont.RegisterMultiple<SomeService2, IInterfaceFourth, IRunnable>(new PerContainerLifetime());




            var runnbles = cont.GetAllInstances<IRunnable>().ToArray();
            //// FAIL HERE 3 instead of 2
            //Assert.AreEqual(2, runnbles.Length);
        }
    }

    public static class ContainerHelper
    {

        public static void RegisterMultiple<TService, TInterface1, TInterface2>(this IServiceContainer container,
            ILifetime lifetime = null)
            where TService : TInterface1, TInterface2
        {
            container.Register<TService>(lifetime);
            container.Register(f => (TInterface1) f.GetInstance<TService>(),typeof(TService).FullName);
            container.Register(f => (TInterface2) f.GetInstance<TService>(), typeof(TService).FullName);
        }

        public static void RegisterMultiple<TService, TInterface1, TInterface2, TInterface3>(
            this IServiceContainer container, ILifetime lifetime = null)
            where TService : TInterface1, TInterface2, TInterface3
        {
            container.Register<TService>(lifetime);
            container.Register(f => (TInterface1) f.GetInstance<TService>(), typeof(TService).FullName);
            container.Register(f => (TInterface2) f.GetInstance<TService>(), typeof(TService).FullName);
            container.Register(f => (TInterface3) f.GetInstance<TService>(), typeof(TService).FullName);
        }

        public static void RegisterMultiple<TService, TInterface1, TInterface2, TInterface3, TInterface4>(
            this IServiceContainer container, ILifetime lifetime = null)
            where TService : TInterface1, TInterface2, TInterface3, TInterface4
        {
            container.Register<TService>(lifetime);
            container.Register(f => (TInterface1) f.GetInstance<TService>(), typeof(TService).FullName);
            container.Register(f => (TInterface2) f.GetInstance<TService>(), typeof(TService).FullName);
            container.Register(f => (TInterface3) f.GetInstance<TService>(), typeof(TService).FullName);
            container.Register(f => (TInterface4) f.GetInstance<TService>(), typeof(TService).FullName);
        }
    }

    public interface IInterfaceThird
    {
        void Method3();
    }

    public interface IInterfaceOne
    {
        void Method2();
    }

    public interface IInterfaceTwo
    {
        void Method1();
    }
    public interface IInterfaceFourth
    {
        void Method4();
    }

    public interface IRunnable
    {
        void Start();

        void Stop();
    }

    public class SomeService2 : IInterfaceFourth, IRunnable
    {
        public void Method4() { }

        public void Dispose() { }

        public void Start() { }

        public void Stop() { }
    }
    public class SomeService1 : IInterfaceOne, IInterfaceTwo, IInterfaceThird, IRunnable
    {
        public void Method1() { }
        public void Method2() { }
        public void Method3() { }
        public void Dispose()
        {

        }

        public void Start()
        {

        }

        public void Stop()
        {

        }
    }
}

seesharper avatar Jun 22 '15 08:06 seesharper

In addition, if you register the same interface twice, LightInject will simply overwrite the existing registration.

I've been bitten by this in another scenario, maybe LightInject should throw an exception warning you about this when it happens.

There could always be a config option to turn it off if you have a valid reason for wanting to overwrite existing registrations (i.e. AllowOverwrite = true)

mattwarren avatar Nov 05 '15 18:11 mattwarren

Hi, I have a similar problem and solved it by:

container.Register<TService>(new PerContainerLifetime());
container.Register<TInterface>(f => f.GetInstance<TService>());

It works OK, just container.GetAllInstances<TInterface>() returns Enumerable of count 2, but contains same instance twice.

It is possible to override TInterface registration without global option EnableVariance = false?

xmedeko avatar Feb 05 '16 20:02 xmedeko