每次ModelView在MVVM中使用Model时,是否需要使用DI和IoC?

时间:2017-08-15 06:14:31

标签: wpf dependency-injection inversion-of-control mvvm-light

我是.NET MVVM应用程序结构的新手,并且只是基本知识它的原理,如解耦,绑定,命令等。我使用MVVM Light框架来简化常见MVVM问题如消息和服务位置。

我不明白的一件事:每次使用ViewModel的Model类时,是否需要调用SimpleIoC? 例: 我有一个简单的View,一个与之对应的ViewModel和一个具有一个类Settings的模型。

MainWindow.xaml

<Window ...>
    <Window.Resources>
        <viewModels:MainWindowModel x:Key="ViewModel" />
    </Window.Resources>
    <DockPanel DataContext="{StaticResource ViewModel}">
        <Button Command="{Binding DoSomeCommand}" />
    </DockPanel>
</Window>

MainWindowModel.cs

public class MainWindowModel: INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    public ICommand DoSomeCommand { get; private set; }

    protected void RaisePropertyChangedEvent(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    public MainWindowModel()
    {
        DoSomeCommand = new RelayCommand(DoSome);
    }

    public void DoSome()
    {
        var data = Settings.Instance;  //singleton

        //... do some with data ...

        Debug.Log($"{data.Prop1}, {data.Prop2}, {data.Prop3}");
    }
}

Settings.cs

public static class Settings 
{
    //... singleton implementations ...

    public int Prop1 { get; set; } // implementation of getters and setters        
    public int Prop2 { get; set; } 
    public int Prop3 { get; set; }  
}

此代码有一个巨大的缺点:DoSome()方法不是单元可测试的。好的,让我们解决一下:

public class MainWindowModel: INotifyPropertyChanged
{
     //...
     private Settings _data;

     public MainWindowModel() 
     {
          _data = Settings.Instance;
          DoSomeCommand = new RelayCommand(() => DoSome(_data));
     }

     public void DoSome(Settings data)
     {  
         //... do some with data ...

         Debug.Log($"{data.Prop1}, {data.Prop2}, {data.Prop3}");
     }
}

现在我可以模拟或存根Settings类并测试DoSome()

但我知道&#39;设置&#39; class可以在不同的实现中,例如&#39; SettingsXML&#39; (XML的数据),&#39; SettingsRegistry&#39; (Window Registry的数据),&#39; SettingsINI&#39; (来自INI文件的数据,很奇怪,但确实如此)。为了避免潜在的问题,我在接口中重写了它:

public interface ISettings
{
    public int Prop1;
    public int Prop2;
    public int Prop3;
}


public static class Settings: ISettings 
{
    //... singleton implementations ...

    public int Prop1 { get; set; } // implementation of getters and setters        
    public int Prop2 { get; set; } 
    public int Prop3 { get; set; }  
}

public class MainWindowModel: INotifyPropertyChanged
{
     //...
     private ISettings _data;

     public void DoSome(ISettings data)
     {  
         ... do some with data ...

         Debug.Log($"_data.Prop1}, {data.Prop2}, {data.Prop3}");
     } 
}

一切看起来都不错。 DoSome()是可测试的,Settings实现可能不同。有一件事困扰我:MainWindowModel知道实际的设置类别(_data = Settings.Instance)。

MVVM结构可以吗?

是否真的有必要使用一些IoC,写一些&#39; SettingsWrapper&#39;依赖注入ISettings类的类,然后使用_data = SimpleIoc.Default.GetInstance<SettingsWrapper>

如果Settings课程不是单身人士,我该怎么做?

很抱歉,如果我对DI和IoC的基本概念完全错误的话。如果你能纠正我,我将不胜感激。

1 个答案:

答案 0 :(得分:1)

  

有一件事困扰我:MainWindowModel知道实际的设置类别(_data = Settings.Instance)。

不应该。它应该只知道它正在注入的接口。

您可以在创建ISettings类本身时使用DoSome注入MainWindowModel类,而不是将ISettings对象传递给public MainWindowModel(ISettings settings) { ... } 方法:

ViewModelLocator

然后您可以让public class ViewModelLocator { public ViewModelLocator() { ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default); SimpleIoc.Default.Register<ISettings>(() => Settings.Instance); SimpleIoc.Default.Register<MainViewModel>(); } public MainViewModel Main { get { return ServiceLocator.Current.GetInstance<MainViewModel>(); } } } 负责创建视图模型类:

<DockPanel DataContext="{Binding Main, Source={StaticResource Locator}}">
    <Button Command="{Binding DoSomeCommand}" />
</DockPanel>
@wrapper