#c# #dependency-injection #autofac
#c# #внедрение зависимостей #autofac
Вопрос:
Как я могу зарегистрировать тип, который принимает другой тип как часть его построения, не передавая фактический экземпляр. Допустим, у меня зарегистрировано два типа ISurface. Я хочу зарегистрировать автомобиль, но я не хочу передавать совершенно новый экземпляр. Я хочу использовать одну из уже определенных поверхностей.
Согласно документации, в которой они указаны :
- Autofac видит, что IDateWriter сопоставляется с TodayWriter, поэтому начинает создавать TodayWriter.
- Autofac видит, что TodayWriter нуждается в IOutput в своем конструкторе.
- Autofac видит, что IOutput сопоставляется с ConsoleOutput, поэтому создает новый экземпляр ConsoleOutput.
Тогда почему я должен передавать экземпляр Highway при регистрации автомобиля? Учитывая, что у меня зарегистрированы две поверхности, как мне указать существующую поверхность?
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());
Зачем мне нужно передавать новую магистраль()?
Вот мои модели.
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:
Здесь есть пара вещей, которые вы можете сделать:
Вариант 1
Это соответствует тому, что у вас уже есть. Вы все равно можете использовать .WithParameter()
, но вместо этого передаете ResolvedParameter
экземпляр, чтобы объяснить, какой параметр нужно найти и как выполнить параметр:
builder.RegisterType<Car>().Named<IVehicle>( "Car" )
.WithParameter(
new ResolvedParameter(
( pInfo, ctx ) => pInfo.Name == "surface",
( pInfo, ctx ) => ctx.ResolveNamed<ISurface>( "Highway" )
)
);
Первый делегат, переданный ResolvedParameter
конструктору, предоставляет способ найти параметр для выполнения, а второй делегат использует IComponentContext
для разрешения Highway
экземпляра из контейнера Autofac.
В качестве альтернативы существует перегрузка WithParameter()
, которую вы можете использовать без необходимости явно создавать ResolvedParameter
:
builder.RegisterType<Car>().Named<IVehicle>( "Car" )
.WithParameter(
( pInfo, ctx ) => pInfo.Name == "surface",
( pInfo, ctx ) => ctx.ResolveNamed<ISurface>( "Highway" ) );
Вариант 2
Этот параметр использует регистрацию с помощью лямбда-выражения:
builder.Register( ( ctx ) => new Car( ctx.ResolveNamed<ISurface>( "Highway" ) ) ).Named<IVehicle>( "Car" );
Здесь вы можете видеть, что я передаю лямбда-выражение, представляющее мою заводскую функцию, которая снова будет использовать IComponentContext
для разрешения Highway
из контейнера Autofac.
ПРИМЕЧАНИЕ
Я также прочитал из вашего вопроса, что вы хотели один и тот же экземпляр ISurface
каждый раз, когда вы его запрашивали. Если это то, что вы хотите, вам нужно будет обновить свои ISurface
регистрации, чтобы включить .SingleInstance()
:
builder.RegisterType<Highway>().Named<ISurface>( "Highway" ).SingleInstance();
builder.RegisterType<Ocean>().Named<ISurface>( "Ocean" ).SingleInstance();
Время жизни по умолчанию, указанное для регистраций InstancePerDependency
, означает, что распознаватель Autofac будет предоставлять новый экземпляр объекта каждый раз, когда он запрашивается. SingleInstance
по сути, является синглтоном. Вы создадите только один экземпляр, и этот экземпляр будет возвращаться при каждом запросе. Вот ссылка на эту информацию
Комментарии:
1. Красивое объяснение.