При использовании внедрения зависимостей (DI) и инверсии управления (IoC) объекты обычно имеют конструктор, который принимает набор зависимостей, необходимых для правильного функционирования объекта.
Например, если у меня есть форма, которая требует, чтобы служба заполняла поле со списком, вы можете увидеть что-то вроде этого:
// my files
public interface IDataService {
IList<MyData> GetData();
}
public interface IComboDataService {
IList<MyComboData> GetComboData();
}
public partial class PopulatedForm : BaseForm {
private IDataService service;
public PopulatedForm(IDataService service) {
//...
InitializeComponent();
}
}
Это нормально работает на верхнем уровне, я просто использую свой контейнер IoC для разрешения зависимостей:
var form = ioc.Resolve<PopulatedForm>();
Но на лицо сгенерированного кода становится сложнее. В winforms создается второй файл, составляющий остальную часть частичного класса. Этот файл ссылается на другие компоненты, такие как пользовательские элементы управления, и использует конструкторы без аргументов для создания таких элементов управления:
// generated file: PopulatedForm.Designer.cs
public partial class PopulatedForm {
private void InitializeComponent() {
this.customComboBox = new UserCreatedComboBox();
// customComboBox has an IComboDataService dependency
}
}
Поскольку это сгенерированный код, я не могу передать зависимости, и нет простого способа автоматически получить мой контейнер IoC ввести все зависимости.
Одним из решений является передача зависимостей каждого дочернего компонента в PopulatedForm
, даже если они могут не понадобиться напрямую, например, с IComboDataService
, требуемым UserCreatedComboBox
. Затем я должен убедиться, что зависимости предоставляются через различные свойства или методы установки. Тогда мой конструктор PopulatedForm
может выглядеть следующим образом:
public PopulatedForm(IDataService service, IComboDataService comboDataService) {
this.service = service;
InitializeComponent();
this.customComboBox.ComboDataService = comboDataService;
}
Другое возможное решение - использовать конструктор no-args для выполнения необходимого разрешения:
public class UserCreatedComboBox {
private IComboDataService comboDataService;
public UserCreatedComboBox() {
if (!DesignMode && IoC.Instance != null) {
comboDataService = Ioc.Instance.Resolve<IComboDataService>();
}
}
}
Ни одно из решений не является особенно хорошим. Какие шаблоны и альтернативы доступны для более эффективной обработки внедрения зависимостей перед лицом сгенерированного кода? Мне бы хотелось увидеть как общие решения, такие как шаблоны, так и специфические для C #, Winforms и Autofac.