Reputation: 807
How can I register a type which takes another type as part of it's constructed without passing an actual instance. Say I have two ISurface types registered. I want to register a Car but i don't want to pass in a brand new instance. I want to use one of the surfaces already defined.
Per the documentation they state :
Then why must I pass in an instance of Highway when registering a Car? Given that I have two Surfaces's registered how do I specify an existing surface?
var builder = new ContainerBuilder();
builder.RegisterType<Highway>().Named<ISurface>("Highway");
builder.RegisterType<Ocean>().Named<ISurface>("Ocean");
builder.RegisterType<Car>().Named<IVehicle>("Car").WithParameter("surface", new Highway());
Why do I need to pass a new Highway()?
Here's my models.
public interface IVehicle
{
void Move();
}
public interface ISurface
{
string SurfaceType { get; }
}
public class Highway : ISurface
{
public string SurfaceType => "Pavement";
}
public class Ocean : ISurface
{
public string SurfaceType => "Ocean"
}
public class Car : IVehicle
{
private ISurface _surface;
public Car(ISurface surface)
{
_surface = surface;
}
public void Move()
{
Console.WriteLine($"I'm traveling at high speeds across {_surface.SurfaceType}");
}
}
Upvotes: 3
Views: 1863
Reputation: 424
There's a couple things you can do here:
This keeps in line with what you already have. You can still use .WithParameter()
but instead pass in a ResolvedParameter
instance to explain the parameter to find and how to fulfill the parameter:
builder.RegisterType<Car>().Named<IVehicle>( "Car" )
.WithParameter(
new ResolvedParameter(
( pInfo, ctx ) => pInfo.Name == "surface",
( pInfo, ctx ) => ctx.ResolveNamed<ISurface>( "Highway" )
)
);
The first delegate passed to the ResolvedParameter
constructor provides a way to find the parameter to fulfill, and the second delegate uses the IComponentContext
to resolve the Highway
instance from the Autofac container.
Alternatively there is an overload to WithParameter()
that you can use without having to explicitly create a ResolvedParameter
:
builder.RegisterType<Car>().Named<IVehicle>( "Car" )
.WithParameter(
( pInfo, ctx ) => pInfo.Name == "surface",
( pInfo, ctx ) => ctx.ResolveNamed<ISurface>( "Highway" ) );
This option uses registration with a lambda expression:
builder.Register( ( ctx ) => new Car( ctx.ResolveNamed<ISurface>( "Highway" ) ) ).Named<IVehicle>( "Car" );
Here you can see that I'm passing a lambda that represents my factory function which will again use the IComponentContext
to resolve the Highway
from the Autofac container.
I also read from your question that you wanted the same instance of ISurface
each time you requested it. If this is what you want, then you'll need to update your ISurface
registrations to include .SingleInstance()
:
builder.RegisterType<Highway>().Named<ISurface>( "Highway" ).SingleInstance();
builder.RegisterType<Ocean>().Named<ISurface>( "Ocean" ).SingleInstance();
The default lifetime given to registrations is InstancePerDependency
which means that the Autofac resolver will give a new instance of the object every time it is requested. SingleInstance
is essentially a singleton. You'll only get one instance created and that instance will be returned on every request. Here's a link to that info
Upvotes: 7