Не удалось выполнить инжекцию микроструктуры Caliburn
Я изучаю Caliburn Micro и пытаюсь использовать EventAggregator
на официальном сайте .
Однако я получил исключение
"Для этого объекта не определен конструктор без параметров".
Само сообщение ясно, но пример не включает конструктор без параметров. Если я добавлю один, конструктор с параметром не будет удален, а IEventAggregator
все равно не будет введен правильно.
Вот моя издательская VM после добавления безпараметрического конструктора (без него будет выведено исключение):
public MainViewModel() { }
public MainViewModel(IEventAggregator ea) : this()
{
eventAggregator = ea;
}
И вот загрузчик, который я использую:
public class AppBootstrapper : Bootstrapper<MainViewModel>
{
private readonly SimpleContainer container = new SimpleContainer();
protected override void Configure()
{
container.Singleton<IEventAggregator, EventAggregator>();
}
}
Вот пример из CM:
// Creating the EventAggregator as a singleton.
public class Bootstrapper : BootstrapperBase {
private readonly SimpleContainer _container =
new SimpleContainer();
// ... Other Bootstrapper Config
protected override void Configure(){
_container.Singleton<IEventAggregator, EventAggregator>();
}
// ... Other Bootstrapper Config
}
// Acquiring the EventAggregator in a viewModel.
public class FooViewModel {
private readonly IEventAggregator _eventAggregator;
public FooViewModel(IEventAggregator eventAggregator) {
_eventAggregator = eventAggregator;
}
}
Я проверил этот пост (Inject EventAggregator в ViewModel с Caliburn Micro), но он просто ничего не сказал, почему CM не вызывает конструктор с инъекцией.
Я также проверил решение выборки CM, но использует MEF в качестве решения DI.
Что мне не хватает?
Ответы
Ответ 1
// ... Other Bootstrapper Config
Это важная конфигурация другого загрузочного файла.
Лучшим вариантом является установка Caliburn.Micro с помощью пакета Caliburn.Micro.Start NuGet и ознакомление с предоставленным загрузчиком, который также использует предоставленный SimpleContainer
по Caliburn.Micro.
Здесь это полностью:
public class AppBootstrapper : BootstrapperBase
{
SimpleContainer container;
public AppBootstrapper()
{
Start();
}
protected override void Configure()
{
container = new SimpleContainer();
container.Singleton<IWindowManager, WindowManager>();
container.Singleton<IEventAggregator, EventAggregator>();
container.PerRequest<IShell, ShellViewModel>();
}
protected override object GetInstance(Type service, string key)
{
var instance = container.GetInstance(service, key);
if (instance != null)
return instance;
throw new InvalidOperationException("Could not locate any instances.");
}
protected override IEnumerable<object> GetAllInstances(Type service)
{
return container.GetAllInstances(service);
}
protected override void BuildUp(object instance)
{
container.BuildUp(instance);
}
protected override void OnStartup(object sender, System.Windows.StartupEventArgs e)
{
DisplayRootViewFor<IShell>();
}
}
Ответ 2
devdigital правильный. Он сказал
важна другая конфигурация загрузчика.
Это абсолютно правильно.
Если вы собираетесь использовать DI (который SimpleContainer
делает для вас), вы должны переопределять методы GetInstance(), GetAllInstances(), BuildUp()
в вашем загрузчике, потому что CM вызывает эти методы, когда он хочет что-то из контейнера.
То, что происходит в вашей ситуации, выглядит следующим образом:
- CM пытается показать ваш
MainViewModel
, потому что вы указали его как общий параметр в Bootstrapper.
- CM замечает, что ваш
MainViewModel
имеет один конструктор, который принимает IEventAggregator
.
- CM вызывает
GetInstance()
, чтобы получить то, что зарегистрировано как IEventAggregator
.
- CM показывает, что вы не переопределили
GetInstance()
, поэтому он пытается создать экземпляр MainViewModel
с помощью Activator.CreateInstance()
.
-
Activator.CreateInstance()
не знает, как создать IEventAggregator
, поэтому он генерирует исключение (тот, который вы получаете).
Чтобы исправить все это, вам нужно переопределить методы, о которых я вам рассказывал. Вам не нужно устанавливать пакет Nuget Caliburn.Start(вы можете использовать его, если вам не нравится вводить текст, и вы хотите сохранить некоторые нажатия клавиш).
По сути, ваше окончательное решение должно выглядеть следующим образом:
public class Bootstrapper : Bootstrapper<MainViewModel>
{
private readonly SimpleContainer _container = new SimpleContainer();
protected override void Configure()
{
_container.Instance<IWindowManager>(new WindowManager());
_container.Singleton<IEventAggregator, EventAggregator>();
_container.PerRequest<MainViewModel>();
}
protected override object GetInstance(Type service, string key)
{
return _container.GetInstance(service, key);
}
protected override IEnumerable<object> GetAllInstances(Type service)
{
return _container.GetAllInstances(service);
}
protected override void BuildUp(object instance)
{
_container.BuildUp(instance);
}
}