3

我有一個Silverlight 5項目中的IValueConverter實例,它將自定義數據轉換爲不同的顏色。我需要從數據庫中讀取實際的顏色值(因爲這些可以由用戶編輯)。使用Unity將對象注入到IValueConverter實例中

由於Silverlight使用異步調用通過實體框架從數據庫加載數據,因此我創建了一個簡單的存儲庫,該存儲庫包含來自數據庫的值。

接口:

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]; 
     } 
    } 
} 

現在我想用統一注入我的倉庫到的IValueConverter實例的這種情況下...

應用.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(); 
    } 
} 

的問題是,我沒有在轉換器實例(即獲得相同的統一容器。存儲庫未註冊)。

+0

如何您的轉換器的情況下產生的?你在XAML中設置了它嗎? – Jehof 2012-04-23 10:34:00

+0

是的。我將值轉換器設置爲XAML對象(TextBox的前景)的綁定。 – froeschli 2012-04-23 12:00:43

回答

0

根據您的評論,您需要使用ServiceLocator來獲取ConfigurationRepository的實例,使得Converter的實例不是由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

這不是「依賴注入」,而是「服務定位器」反模式用法。這行代碼不容易測試。 – 2014-09-15 05:46:28

+0

@GenaVerdel是的你是對的,爲了使它可測試,你可以添加另一個構造函數來傳入一個IConfigurationRepository的實例。他的問題不是關於依賴注入器或ServiceLocator,而是如何將值注入到IValueConverter中。而且沒有其他方法可以使用ServiceLocator和DefaultCtor來注入依賴關係,因此實例是由WPF使用默認ctor創建的。 – Jehof 2014-09-15 06:12:53

+0

你是對的,在XAML中實例化的IValueConverter是IoC-unaware。這就是爲什麼我建議在出現外部依賴問題時不要使用它們。 我會建議在這種特殊情況下使用依賴屬性。這種方法是可測試的,同時仍然提供預期的功能。 如果此解決方案對您無效,請切換到環境上下文。 – 2014-09-16 07:24:46

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}" /> 
相關問題