Reputation: 10325
I am using autofac 3.5.x and I have a setup similar to this:
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 is what I want: When I request a new IServiceD
from the container, I want the same IServiceA
instance to be injected into IServiceD
and its dependencies IServiceB
and IServiceC
. I am not looking for a global singleton scope though. Next time I ask for an IServiceD
instance it must be created with a new instance of IServiceA
(I know you cannot create an instance of an interface, but I think you get the point).
To illustrate; when I ask the autofac container for an IServiceD
I want the following to happen:
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 GetServiceA()
method is only included to illustrate my point.
So yeah, I guess I am looking for some way to tell autofac that when it resolves IServiceD
it should create a singleton of ServiceA
but only for the scope of ServiceD
's constructor.
Right now, I use autofacs support for delegate factories, and ask for:
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()
}
This gets me going, but I have a feeling it can be done better - which is why I am turning to the experts now.
Thanks in advance.
Upvotes: 4
Views: 2088
Reputation: 16192
The InstancePerLifetimeScope
may suit your need. It allows you to have a single instance per lifetime scope.
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 a single instance of IServiceA
using (ILifetimeScope scope = container.BeginLifetimeScope())
{
// only an instance of IServiceA will be created for this scope
scope.Resolve<IServiceD>();
}
If you can't create a new ILifetimeScope
you can use the Owned<T>
type which is a lightweight lifetimescope.
using (Owned<IServiceD> ownedD = container.Resolve<Owned<IServiceD>>())
{
IServiceD serviceD = ownedD.Value;
}
Each time you resolve a Owned<IserviceD>
a new ILifetimeScope
will be created and only a single IServiceA
will be created.
If it is not enough you can play with InstancePerMatchingLifetimeScope
but it will make your code and registration quite difficult to read and debug. InstancePerLifetimeScope
should be enough.
Another solution is to use this kind 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 is not really elegant.
Upvotes: 3