3

У меня есть экземпляр IValueConverter в проекте Silverlight 5, который преобразует пользовательские данные в разные цвета. Мне нужно прочитать фактические значения цвета из базы данных (так как они могут быть отредактированы пользователем).Использование Unity для ввода объектов в экземпляр IValueConverter

Поскольку Silverlight использует асинхронные вызовы для загрузки данных через Entity Framework из базы данных, я создал простой репозиторий, в котором хранятся значения из db.

Интерфейс:

public interface IConfigurationsRepository 
{ 
    string this[string key] { get; } 
} 

Реализация:

public class ConfigurationRepository : IConfigurationsRepository 
{ 
    private readonly TdTerminalService _service = new TdTerminalService(); 

    public ConfigurationRepository() 
    { 
     ConfigurationParameters = new Dictionary<string, string>(); 
     _service.LoadConfigurations().Completed += (s, e) => 
      { 
       var loadOperation = (LoadOperation<Configuration>) s; 
       foreach (Configuration configuration in loadOperation.Entities) 
       { 
        ConfigurationParameters[configuration.ParameterKey] = configuration.ParameterValue; 
       } 
      }; 
    } 

    private IDictionary<string, string> ConfigurationParameters { get; set; } 

    public string this[string key] 
    { 
     get 
     { 
      return ConfigurationParameters[key]; 
     } 
    } 
} 

Теперь я хотел бы использовать Unity, чтобы ввести этот экземпляр моего репозитория в экземпляр IValueConverter ...

App .xaml.cs:

private void RegisterTypes() 
{ 
    _container = new UnityContainer(); 
    IConfigurationsRepository configurationsRepository = new ConfigurationRepository(); 
    _container.RegisterInstance<IConfigurationsRepository>(configurationsRepository); 
} 

IValueConverter:

public class SomeValueToBrushConverter : IValueConverter 
{ 
    [Dependency] 
    private ConfigurationRepository ConfigurationRepository { get; set; } 

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 
    { 
     switch ((SomeValue)value) 
     { 
      case SomeValue.Occupied: 
       return new SolidColorBrush(ConfigurationRepository[OccupiedColor]); 
      default: 
       throw new ArgumentException(); 
     } 
    } 

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 
    { 
     throw new NotImplementedException(); 
    } 
} 

Проблема заключается в том, что я не получаю тот же Unity-контейнера в случае преобразователя (то есть. репозиторий не зарегистрирован).

+0

Как экземпляр вашего преобразователя создан? Вы устанавливаете его в XAML? – Jehof

+0

Да. Я установил преобразователь значений в привязку XAML-объекта (Foreground of TextBox). – froeschli

ответ

0

Согласно вашему комментарию, вам необходимо использовать ServiceLocator, чтобы получить экземпляр вашего ConfigurationRepository, потому что экземпляр конвертера не создан Unity, а движком Silverlight/XAML.

Таким образом, ваша собственность, украшенная атрибутом DependencyAttribute, не будет введена.

C#

public class SomeValueToBrushConverter : IValueConverter 
{ 
    public SomeValueToBrushConverter(){ 
     ConfigurationRepository = ServiceLocator.Current.GetInstance<ConfigurationRepository>(); 
    } 

    private ConfigurationRepository ConfigurationRepository { get; set; } 
} 

В вашем методе RegisterTypes вам нужно настроить поиска сервиса:

_container = new UnityContainer(); 
UnityServiceLocator locator = new UnityServiceLocator(_container); 
ServiceLocator.SetLocatorProvider(() => locator); 
+1

Это не «инъекция зависимостей», а использование антипаттера «Локатор сервисов». Эта строка кода трудно проверить. –

+0

@GenaVerdel да, вы правы, чтобы сделать его проверяемым, вы можете добавить другой конструктор для передачи в экземпляр IConfigurationRepository. Его вопрос не о Dependency Injectior или ServiceLocator, а о том, как вводить значения в IValueConverter. И нет другого способа использовать ServiceLocator и DefaultCtor для ввода зависимостей, потому что экземпляр создается WPF с использованием по умолчанию ctor. – Jehof

+0

Вы правы, IValueConverter, созданный в XAML, IoC-unaware.Вот почему я рекомендую воздерживаться от их использования вообще всякий раз, когда возникает проблема внешней зависимости. В этом конкретном случае я бы рекомендовал использовать свойство Dependency. Этот подход будет проверяться при сохранении ожидаемой функциональности. Если это решение не работает для вас, переключитесь в контекст окружения. –

4

Можно использовать MarkupExtension для разрешения зависимостей от DI контейнера:

public class IocResolver : MarkupExtension 
{ 
    public IocResolver() 
    { } 

    public IocResolver(string namedInstance) 
    { 
     NamedInstance = namedInstance; 
    } 

    [ConstructorArgument("namedInstance")] 
    public string NamedInstance { get; set; } 

    public override object ProvideValue(IServiceProvider serviceProvider) 
    { 
     var provideValueTarget = (IProvideValueTarget)serviceProvider 
      .GetService(typeof(IProvideValueTarget)); 

     // Find the type of the property we are resolving 
     var targetProperty = provideValueTarget.TargetProperty as PropertyInfo; 

     if (targetProperty == null) 
      throw new InvalidProgramException(); 

     Debug.Assert(Resolve != null, "Resolve must not be null. Please initialize resolving method during application startup."); 
     Debug.Assert(ResolveNamed != null, "Resolve must not be null. Please initialize resolving method during application startup."); 

     // Find the implementation of the type in the container 
     return NamedInstance == null 
      ? (Resolve != null ? Resolve(targetProperty.PropertyType) : DependencyProperty.UnsetValue) 
      : (ResolveNamed != null ? ResolveNamed(targetProperty.PropertyType, NamedInstance) : DependencyProperty.UnsetValue); 
    } 

    public static Func<Type, object> Resolve { get; set; } 
    public static Func<Type, string, object> ResolveNamed { get; set; } 
} 

IocResolver должен быть инициализирован во время применения запуск Тион как:

IocResolver.Resolve = kernel.Get; 
IocResolver.ResolveNamed = kernel.GetNamed; 
// or what ever your DI container looks like 

После этого, вы можете использовать его в XAML, чтобы ввести зависимость в XAML:

<!-- Resolve an instance based on the type of property 'SomeValueToBrushConverter' --> 
<MyConverter SomeValueToBrushConverter="{services:IocResolver}" /> 

<!-- Resolve a named instance based on the type of property 'SomeValueToBrushConverter' and the name 'MyName' --> 
<MyConverter SomeValueToBrushConverter="{services:IocResolver NamedInstance=MyName}" />