Scrutor icon indicating copy to clipboard operation
Scrutor copied to clipboard

Difference between AsSelfWithInterfaces and AsImplementedInterfaces?

Open vdurante opened this issue 4 years ago • 4 comments

I am trying to understand how Scrutor works and I got quite confused regarding the differences between AsSelfWithInterfaces and AsImplementedInterfaces. Besides the Self part, I noticed AsImplementedInterfaces registers the interfaces directly to a ImplementationType, such as IRepository -> Repository, but the AsSelfWithInterfaces doesn't, since it registers the interfaces to the class using a ImplementationFactory.

I understand de implementation differences between using a factory and a type, but I don't seem to understand the actual difference between both, specially regarding use cases. When should I use each?

Would appreciate if someone could help me out. Thanks!

vdurante avatar Sep 13 '21 11:09 vdurante

Hi,

Most likely, these methods do what they actually say.

Consider next pseudo-code:

#region fakes

public interface IA { } 
public interface IB { } 
public class AB : IA, IB { }

#endrigion

#region main

var sp1 = new SeviceCollection()
    .Scan(x => x.FromAssemblyOf<AB>()
                        .AddClasses(classes => classes.AssignableTo<AB>())
                        .AsImplementedInterfaces()
                        .WithTransientLifetime())
    .BuildServiceProvider();

var a = sp1.GetService<IA>(); // NOT NULL
var b = sp1.GetSerivce<IB>(); // NOT NULL
var ab= sp1.GetService<AB>(); // NULL

var sp2 = new SeviceCollection()
    .Scan(x => x.FromAssemblyOf<AB>()
                        .AddClasses(classes => classes.AssignableTo<AB>())
                        .AsSelfWithInterfaces()
                        .WithTransientLifetime())
    .BuildServiceProvider();

a = sp2.GetService<IA>(); // NOT NULL
b = sp2.GetSerivce<IB>(); // NOT NULL
ab= sp2.GetService<AB>(); // NOT NULL

#endregion 

So, AsSelfWithInterfaces means that every type that satisfies your predicate in AddClasses will be registered as every interface that it implements + as itself. In other words, without the usages of Scrutor these can look like:

sp2 = new ServiceCollection()
    .AddTransient<AB>()
    .AddTransient<IA>(sp => sp.GetService<AB>())
    .AddTransient<IB>(sp => sp.GetService<AB>())
    .BuildServiceProvider();

a = sp2.GetService<IA>(); // NOT NULL
b = sp2.GetSerivce<IB>(); // NOT NULL
ab= sp2.GetService<AB>(); // NOT NULL

Mariachi1231 avatar Sep 13 '21 18:09 Mariachi1231

If I remember correctly there's a difference in instance reuse for longer lifetimes as well.

With AsImplementedInterfaces, if you have singleton registrations for IA and IB where both are implemented by AB, there will be two separate singletons - one for each registration.

But with AsSelfWithInterfaces, the interface registrations will resolve the self-registered singleton, which means each interface registration will share one singleton instance.

Does that make sense?

khellang avatar Sep 13 '21 21:09 khellang

Yeah, seems according to ScanShouldCreateSeparateRegistrationsPerInterface and AsSelfWithInterfacesShouldForwardRegistrationsToClass you remember correctly 😅

I think, from one point of view it does, from another no. Mostly because it works slightly differently in comparison with scanning in other DI containers. Since in past I worked mostly with Autofac, their AsImplementedInterfaces works as your AsSelfWithInterfaces (for sure, if we talk about types lifecycle), the same in the SimpleInjector, and if I correctly remember in the Windsor as well.

BTW, from Microsoft.Extensions.DependencyInejction standpoint it makes sense.

Mariachi1231 avatar Sep 13 '21 22:09 Mariachi1231

Right. As I look at it, this is a limitation in the MS.Ext.DI container 😞

khellang avatar Sep 14 '21 06:09 khellang