Ответ 1
Да, прохождение контейнера повсюду - это анти-шаблон.
Вы можете избежать этого, используя factory следующим образом:
(примечание: весь код в этом ответе непроверен, я пишу это в текстовом редакторе на машине без Visual Studio)
public interface IServiceHelperFactory
{
IServiceHelper CreateServiceHelper(string serviceName);
}
public class ServiceHelperFactory : IServiceHelperFactory
{
private IContainer container;
public ServiceHelperFactory(IContainer container)
{
this.container = container;
}
public IServiceHelper CreateServiceHelper(string serviceName)
{
return container.Resolve<ServiceHelper>(new NamedParameter("serviceName", serviceName));
}
}
При запуске вы регистрируете ServiceHelperFactory
в Autofac, как и все остальное:
builder.RegisterType<ServiceHelperFactory>().As<IServiceHelperFactory>();
Затем, когда вам нужно ServiceHelper
где-то еще, вы можете получить factory через инсталляцию конструктора:
public class SomeClass : ISomeClass
{
private IServiceHelperFactory factory;
public SomeClass(IServiceHelperFactory factory)
{
this.factory = factory;
}
public void ThisMethodCreatesTheServiceHelper()
{
var helper = this.factory.CreateServiceHelper("some service name");
}
}
Создав сам factory с помощью встраивания конструктора с помощью Autofac, вы убедитесь, что factory знает о контейнере, без необходимости самостоятельно передавать контейнер.
Я признаю, на первый взгляд это решение выглядит не совсем иначе, чем прямое обращение контейнера. Но преимущество в том, что ваше приложение все еще отключено от контейнера - единственное место, где известно контейнер (кроме запуска), находится внутри factory.
EDIT:
ОК, я забыл. Как я уже сказал выше, я пишу это на машине без Visual Studio, поэтому я не могу проверить свой примерный код.
Теперь, когда я прочитал ваш комментарий, я помню, что у меня была аналогичная проблема, когда я использовал Autofac и пытался зарегистрировать сам контейнер.
Моя проблема заключалась в том, что мне нужно было зарегистрировать контейнер в построителе.
Но чтобы экземпляр контейнера зарегистрировался, мне нужно было вызвать builder.Build()
... который создает контейнер, а это значит, что я не могу зарегистрировать материал в строителе впоследствии.
Я не помню сообщение об ошибке, которое у меня есть, но, похоже, у вас такая же проблема.
Решением, которое я нашел, было создание второго построителя, зарегистрировать там контейнер, , а затем использовать второй строитель для обновления одного и только контейнера.
Вот мой рабочий код из одного из моих проектов с открытым исходным кодом:
При запуске я регистрирую контейнер::
var builder = new ContainerBuilder();
// register stuff here
var container = builder.Build();
// register the container
var builder2 = new ContainerBuilder();
builder2.RegisterInstance<IContainer>(container);
builder2.Update(container);
..., который затем используется с помощью WindowService
для создания новых окон WPF:
public class WindowService : IWindowService
{
private readonly IContainer container;
public WindowService(IContainer container)
{
this.container = container;
}
public T GetWindow<T>() where T : MetroWindow
{
return (T)this.container.Resolve<T>();
}
}