Nesting dependency with "design area" - autofac
I am using autofac 3.5.x and I have the same setup:
public class ServiceA : IServiceA { }
public class ServiceB : IServiceB { public ServiceB(IServiceA sa) { } }
public class ServiceC : IServiceC { public ServiceC(IServiceA sa) { } }
public class ServiceD : IServiceD { public ServiceD(IServiceA sa, IServiceB sb, IServiceC sc) {} }
In my container, I have the following registrations:
builder.RegisterType<ServiceA>.As<IServiceA>();
builder.RegisterType<ServiceB>.As<IServiceB>();
builder.RegisterType<ServiceC>.As<IServiceC>();
builder.RegisterType<ServiceD>.As<IServiceD>();
Here's what I want: When I request a new one IServiceD
from the container, I want the same instance to IServiceA
be injected into IServiceD
and its dependencies IServiceB
and IServiceC
. However, I'm not looking for the global singles area. The next time I ask for an instance IServiceD
, it should be instantiated with a new instance IServiceA
(I know you cannot instantiate an interface, but I think you get the point).
To illustrate; when i ask the autofac container for IServiceD
i want the following:
public class ServiceD : IServiceD
{
public ServiceD(IServiceA sa, IServiceB sb, IServiceC sc)
{
// SA should be the same (but it is not)
sa.GetHashCode() == sb.GetServiceA().GetHashCode() == sc.GetServiceA().GetHashCode()
}
}
Please note that the method is GetServiceA()
included only to illustrate my point.
So, I think I am looking for a way to tell autofac that when it resolves IServiceD
, it should create a singleton from ServiceA
, but only for the design area ServiceD
.
I am currently using autofacs support for delegate factories and ask:
public ServiceD(IServiceA sa, Func<IServiceA, IServiceB> fb, Func<IServiceA, IServiceC> fc) : IServiceD
{
var sb = fb(sa); // Manually inject the same instance of ServiceA
var sc = fc(sa); // Manually inject the same instance of ServiceA
// ServiceA is now the same instance
sa.GetHashCode() == sb.GetServiceA().GetHashCode() == sc.GetServiceA().GetHashCode()
}
It keeps me going, but I have a feeling it could be done better - which is why I am reaching out to the experts now.
Thanks in advance.
source to share
InstancePerLifetimeScope
can meet your needs. It allows you to have one copy for the entire life span.
builder.RegisterType<ServiceA>().As<IServiceA>().InstancePerLifetimeScope();
builder.RegisterType<ServiceB>().As<IServiceB>().InstancePerLifetimeScope();
builder.RegisterType<ServiceC>().As<IServiceC>().InstancePerLifetimeScope();
builder.RegisterType<ServiceD>().As<IServiceD>().InstancePerLifetimeScope();
Then, each time you resolve IServiceD
in a new scope, you will have one instanceIServiceA
using (ILifetimeScope scope = container.BeginLifetimeScope())
{
// only an instance of IServiceA will be created for this scope
scope.Resolve<IServiceD>();
}
If you cannot create a new one ILifetimeScope
, you can use a type Owned<T>
that is a lightweight lifetimecope.
using (Owned<IServiceD> ownedD = container.Resolve<Owned<IServiceD>>())
{
IServiceD serviceD = ownedD.Value;
}
Each time you allow Owned<IserviceD>
a new one ILifetimeScope
will be created and only one will be created IServiceA
.
If that's not enough, you can play with InstancePerMatchingLifetimeScope
, but it will make your code and logging rather difficult to read and debug. InstancePerLifetimeScope
should be enough.
Another solution is to use this type of registration.
builder.Register(ctx =>
{
IServiceA serviceA = ctx.Resolve<IServiceA>();
IServiceB serviceB = ctx.Resolve<IServiceB>(TypedParameter.From(serviceA));
IServiceC serviceC = ctx.Resolve<IServiceC>(TypedParameter.From(serviceA));
IServiceD serviceD = ctx.Resolve<IServiceD>(TypedParameter.From(serviceA), TypedParameter.From(serviceB), TypedParameter.From(serviceC));
return serviceD;
}).As<IServiceD>();
But actually not very elegant.
source to share