Ответ 1
Невозможно выполнить условную регистрацию на уровне контейнера на основе содержимого контейнера. Проблема заключается в том, что вам нужно будет что-то разрешить в контейнере, чтобы определить, что регистрируется в контейнере, которые затем могут технически повлиять на то, хотите ли вы зарегистрировать вещь в первую очередь. Проблема зависимости курица/яйца.
Вы можете, однако, регистрировать события условно во вложенные области времени жизни. Большинство точек интеграции (например, ASP.NET) устраняют из вложенных областей времени жизни (например, продолжительность жизни по длине HTTP-запроса). Вы можете регистрировать вещи "на лету" во вложенные области жизни, и это может решить вашу проблему.
var builder = new ContainerBuilder();
builder.Register(ctx => LoadSettings()).As<ISettings>().SingleInstance();
builder.RegisterType<DefaultFoo>().As<IFoo>();
var container = builder.Build();
var settings = container.Resolve<ISettings>();
using(var scope =
container.BeginLifetimeScope(b => {
if(settings.ReallyUseSpecificFoo)
{
b.RegisterType<SpecificFoo>().As<IFoo>();
}
})
{
// Resolve things from the nested lifetime scope - it will
// use the overrides. This will get the SpecificFoo if the
// configuration setting is true.
var foo = scope.Resolve<IFoo>();
}
Другой вариант, который у вас есть, - сделать регистрацию лямбдой. Это может сделать регистрацию более сложной, но это вариант, который вы могли бы рассмотреть.
var builder = new ContainerBuilder();
builder.Register(ctx => {
var settings = ctx.Resolve<ISettings>();
if(settings.ReallyUseSpecificFoo)
{
return new SpecificFoo();
}
return new DefaultFoo();
}).As<IFoo>();
Если ручная конструкция не привлекательна, вы можете передать ее также через Autofac.
var builder = new ContainerBuilder();
// Register the IFoo types - but NOT "As<IFoo>"
builder.RegisterType<DefaultFoo>();
builder.RegisterType<SpecificFoo>();
// In the lambda use Resolve<T> to get the instances.
builder.Register(ctx => {
var settings = ctx.Resolve<ISettings>();
if(settings.ReallyUseSpecificFoo)
{
return ctx.Resolve<SpecificFoo>();
}
return ctx.Resolve<DefaultFoo>();
}).As<IFoo>();
Еще один вариант - обновить существующий контейнер после его создания. В этом случае вы избегаете сценария курица/яйцо, фактически создавая контейнер, используя его и меняя регистрацию после факта.
var builder = new ContainerBuilder();
builder.Register(ctx => LoadSettings()).As<ISettings>().SingleInstance();
builder.RegisterType<DefaultFoo>().As<IFoo>();
var container = builder.Build();
var settings = container.Resolve<ISettings>();
if(settings.ReallyUseSpecificFoo)
{
var updater = new ContainerBuilder();
updater.RegisterType<SpecificFoo>().As<IFoo>();
updater.Update(container);
}
Наконец, вы можете рассмотреть конфигурацию XML. Учитывая, что регистрация зависит от какого-либо параметра конфигурации, вы можете рассмотреть возможность использования поддержки конфигурации AutoFac XML. Таким образом, вместо того, чтобы пытаться решить что-то из неконструированного контейнера, чтобы условно зарегистрировать что-то еще, вы можете просто указать правильную вещь для регистрации с использованием конфигурации XML и зарегистрировать правильную вещь в первый раз.