I haven't been able to determine this from the documentation.
Given a registration:
builder.RegisterType<ExampleComponent>().As<IComponent>().InstancePerLifetimeScope();
What LifetimeScope will be assumed by the following registration?
builder.Register(ctx =>
{
var component = ctx.Resolve<IComponent>();
return new SomeService(component);
}).As<ISomeService>();
Note: This is just an example. Obviously in this case you'd just resolve ISomeService and allow Autofac to instantiate an instance of SomeService with the IComponent dependency. I have a more complex registration need for which this approach is necessary, but whose details aren't really pertinent to the question.
Given IComponent is registered as InstancePerLifetimeScope (which I understand to mean that it inherits the scope used to resolve this directly or the scope used to resolve a component for which this is a dependency), and that the delegate registered for ISomeService will be the default lifetime scope of InstancePerDependency, I would expect that the resolve of IComponent within the delegate would be with lifetime scope InstancePerDependency.
Is this correct?
When you resolve ISomeService, this will be done per dependency as expected, which means that you will get a new instance of SomeService on every resolve (by calling the delegate).
However, the call to get component eg:
var component = ctx.Resolve<IComponent>();
Will return one instance of component shared per lifetime scope (so if you do not create any child/nested lifetime scope(s), it will be more or less a singleton).
Here is a small example to demonstrate it.
First here is a simple implementation for IComponent:
public interface IComponent
{
int GetId();
}
public class ExampleComponent : IComponent
{
private static int id = 0;
private int instanceId;
public ExampleComponent()
{
this.instanceId = id;
id++;
}
public int GetId()
{
return this.instanceId;
}
}
Then for ISomeService :
public interface ISomeService
{
int GetServiceID();
int GetComponentId();
}
public class SomeService : ISomeService
{
private static int id = 0;
private int instanceId;
private readonly IComponent component;
public SomeService(IComponent component)
{
if (component == null)
throw new ArgumentNullException("component");
this.component = component;
this.instanceId = id;
id++;
}
public int GetComponentId()
{
return this.component.GetId();
}
public int GetServiceID()
{
return this.instanceId;
}
}
I kept registration as you wrote:
ContainerBuilder builder = new ContainerBuilder();
builder.RegisterType<ExampleComponent>().As<IComponent>().InstancePerLifetimeScope();
builder.Register(ctx =>
{
var component = ctx.Resolve<IComponent>();
return new SomeService(component);
}).As<ISomeService>();
IContainer rootContainer = builder.Build();
And now let's do some resolve :
ISomeService service1 = rootContainer.Resolve<ISomeService>();
ISomeService service2 = rootContainer.Resolve<ISomeService>();
Console.WriteLine(string.Format("Service 1: {0} , Component : {1} ", service1.GetServiceID(), service1.GetComponentId()));
Console.WriteLine(string.Format("Service 2: {0} , Component : {1} ", service2.GetServiceID(), service2.GetComponentId()));
You will get:
Service 1: 0 , Component : 0 Service 2: 1 , Component : 0
Since Component is shared by the main lifetime scope.
Now of course, if you create child scopes:
IContainer rootContainer = builder.Build();
ILifetimeScope scope1 = rootContainer.BeginLifetimeScope();
ILifetimeScope scope2 = rootContainer.BeginLifetimeScope();
ISomeService service1 = scope1.Resolve<ISomeService>();
ISomeService service2 = scope2.Resolve<ISomeService>();
Console.WriteLine(string.Format("Service 1: {0} , Component : {1} ", service1.GetServiceID(), service1.GetComponentId()));
Console.WriteLine(string.Format("Service 2: {0} , Component : {1} ", service2.GetServiceID(), service2.GetComponentId()));
Service 1: 0 , Component : 0 Service 2: 1 , Component : 1
In that case, you have 2 different scopes, so each resolve to component will provide a different one.