How to register class with multiple interfaces?
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>());
}
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()
{
}
}
}
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)
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?