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}" /> 
相关问题