Mike
Mike

Reputation: 807

Autofac Resolve constructor instance from Container?

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

Answers (1)

yeah-buddy
yeah-buddy

Reputation: 424

There's a couple things you can do here:

Option 1

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" ) );

Option 2

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.

NOTE

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

Related Questions