Autofac Resolve constructor instance from Container?

倖福魔咒の 提交于 2019-12-10 10:15:54

问题


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 :

  • Autofac sees that IDateWriter maps to TodayWriter so starts creating a TodayWriter.
  • Autofac sees that the TodayWriter needs an IOutput in its constructor.
  • Autofac sees that IOutput maps to ConsoleOutput so creates a new ConsoleOutput instance.

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

回答1:


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



来源:https://stackoverflow.com/questions/39985987/autofac-resolve-constructor-instance-from-container

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!