Старайтесь не иметь дизайн класса, который имеет примитивные или трудно разрешаемые типы в конструкторе. Как вы уже видели из ответа Тавареса, ваша конфигурация становится очень хрупкой ( обновление: Таварес, похоже, удалил свой ответ по причинам, которые мне не ясны ). Вы теряете поддержку времени компиляции, и каждое изменение в этом конструкторе заставляет вас изменять конфигурацию DI.
Существует несколько способов изменить дизайн, чтобы предотвратить это. Какой из них применим для вас, зависит от вас, но вот несколько идей:
Вариант 1: использование неизменяемой конфигурации DTO:
private sealed class LoginManagerConfiguration
{
public Uri Login { get; private set; }
public Uri Target { get; private set; }
public string MainRegionName { get; private set; }
public LoginManagerConfiguration(Uri login, Uri target, string mainRegionName)
{
this.Login = login;
this.Target = target;
this.MainRegionName = mainRegionName;
}
}
Теперь вы можете позволить вашему LoginManager
получить зависимость от LoginManagerConfiguration
:
public LoginManager(IRegionManager regionManager,
IEventAggregator eventAggregator,
LoginManagerConfiguration configuration)
{
...
}
LoginManagerConfiguration
можно просто зарегистрировать так:
container.RegisterInstance<LoginManagerConfiguration>(
new LoginManagerConfiguration(
login: new Uri("Login"),
target: new Uri("Target"),
mainRegionName: ConfigurationManager.AppSettings["MainRegion"]));
Может быть заманчиво указать объект конфигурации для всего приложения вместо этого конкретного DTO, но это ловушка. Такой объект конфигурации в масштабах приложения является эквивалентом конфигурации шаблона поиска сервисов. Становится неясным, какие значения конфигурации требуется типу, и затрудняет тестирование классов.
Вариант 2: Производный от этого класса
Другой вариант - наследовать от этого класса только для настройки DI. Это особенно полезно, когда вы не можете изменить сигнатуру класса (т. Е. Когда это сторонний компонент):
private sealed class DILoginManager : LoginManager
{
DILoginManager(IRegionManager regionManager,
IEventAggregator eventAggregator)
: base(regionManager, eventAggregator,
ConfigurationManager.AppSettings["MainRegion"],
new Uri("Login"),
new Uri("Target"))
{
...
}
}
Определите этот класс близко к корню композиции вашего приложения. Этот класс становится деталью реализации вашей конфигурации DI. Регистрация вашего типа теперь будет очень простой:
container.RegisterType<ILoginManager, DILoginManager>();
Будьте очень осторожны с вызовами, которые лениво загружают значения конфигурации, такие как ConfigurationManager.AppSettings["MainRegion"]
. Это может легко привести к ситуациям, когда ошибки конфигурации не обнаруживаются при запуске приложения, что действительно предпочтительно.
Вариант 3: Использовать делегата фабрики
Последний вариант, который я хотел бы представить, это завод. Это будет очень похоже на ответ Traveses, но более безопасно для типов:
var mainRegion = ConfigurationManager.AppSettings["MainRegion"];
container.Register<ILoginManager>(new InjectionFactory(c =>
{
return new LoginManager(
c.Resolve<IRegionManager>(),
c.Resolve<IEventAggregator>(),
ConfigurationManager.AppSettings["MainRegion"],
new Uri("Login"),
new Uri("Target"));
}));