如何处理依赖注入在WPF / MVVM应用 [英] How to handle dependency injection in a WPF/MVVM application

查看:155
本文介绍了如何处理依赖注入在WPF / MVVM应用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我开始一个新的桌面应用程序,我希望它使用MVVM和WPF来构建。

I am starting a new desktop application and I want to build it using MVVM and WPF.

我也打算使用TDD。

问题是,我不知道我应该怎么使用IoC容器注入我的生产code我的依赖关系。

The problem is that I don´t know how I should use an IoC container to inject my dependencies on my production code.

假设我有如下因素类和接口:

Suppose I have the folowing class and interface:

public interface IStorage
{
    bool SaveFile(string content);
}

public class Storage : IStorage
{
    public bool SaveFile(string content){
        // Saves the file using StreamWriter
    }
}

然后我有了另一个类的IStorage 作为依赖,还假设这个类是一个视图模型或商业类...

And then I have another class that has IStorage as a dependency, suppose also that this class is a ViewModel or a business class...

public class SomeViewModel
{
    private IStorage _storage;

    public SomeViewModel(IStorage storage){
        _storage = storage;
    }
}

有了这个,我可以很容易地编写单元测试,以确保它们正常工作,使用模拟考试等。

With this I can easily write unit tests to ensure that they are working properly, using mocks and etc.

的问题是,当涉及在实际应用中使用它。我知道我必须有一个IoC容器链接的的IStorage 接口的默认实现,但请问有什么可以做呢?

The problem is when it comes to use it in the real application. I know that I must have an IoC container that links a default implementation for the IStorage interface, but how may I to do it?

例如,会是怎样,如果我有下面的XAML代码:

For example, how would it be if I had the following xaml:

<Window 
    ... xmlns definitions ...
>
   <Window.DataContext>
        <local:SomeViewModel />
   </Window.DataContext>
</Window>

我怎样才能正确地告诉WPF在这种情况下注入的依赖?

How can I correctly 'tell' WPF to inject dependencies in that case?

此外,假设我需要从我的 CS code SomeViewModel 的一个实例,我应该怎么办?呢?

Also, suppose I need an instance of SomeViewModel from my cs code, how should I do it?

我觉得I'm在这个完全失去了,我就AP preciate的是如何处理它的最佳方法的任何例子或指导。

I feel I´m completely lost in this, I would appreciate any example or guidance of how is the best way to handle it.

我熟悉StructureMap,但我可不是专家。此外,如果有一个更好/更容易/外的现成的框架,请让我知道。

I am familiar with StructureMap, but I´m not an expert. Also, if there is a better/easier/out-of-the-box framework, please let me know.

先谢谢了。

推荐答案

我一直使用Ninject,发现它与一起工作非常愉快。一切都建立在code,语法是非常简单,它有一个好的文档(和大量的答案在SO)。

I have been using Ninject, and found that it's a pleasure to work with. Everything is set up in code, the syntax is fairly straightforward and it has a good documentation (and plenty of answers on SO).

所以基本上是这样的:

创建视图模型,并采取的IStorage接口构造函数的参数:

Create the view model, and take the IStorage interface as constructor parameter:

class UserControlViewModel
{
    public UserControlViewModel(IStorage storage)
    {

    }
}

用GET属性视图模型,从Ninject加载视图模型创建ViewModelLocator:

Create a ViewModelLocator with a get property for the view model, which loads the view model from Ninject:

class ViewModelLocator
{
    public UserControlViewModel UserControlViewModel
    {
        get { return IocKernel.Get<UserControlViewModel>();} // Loading UserControlViewModel will automatically load the binding for IStorage
    }
}

请在ViewModelLocator在App.xaml中的应用程序范围内的资源:

Make the ViewModelLocator an application wide resource in App.xaml:

<Application ...>
    <Application.Resources>
        <local:ViewModelLocator x:Key="ViewModelLocator"/>
    </Application.Resources>
</Application>

绑定用户控件的DataContext的在ViewModelLocator相应的属性。

Bind the DataContext of the UserControl to the corresponding property in the ViewModelLocator.

<UserControl ...
             DataContext="{Binding UserControlViewModel, Source={StaticResource ViewModelLocator}}">
    <Grid>
    </Grid>
</UserControl>

创建一个类继承NinjectModule,这将设置必要的绑定(的IStorage和视图模型):

Create a class inheriting NinjectModule, which will set up the necessary bindings (IStorage and the viewmodel):

class IocConfiguration : NinjectModule
{
    public override void Load()
    {
        Bind<IStorage>().To<Storage>().InSingletonScope(); // Reuse same storage every time

        Bind<UserControlViewModel>().ToSelf().InTransientScope(); // Create new instance every time
    }
}

初​​始化与必要的Ninject模块,应用程序启动的内核的IoC(上面的那个现在):

Initialize the IoC kernel on application startup with the necessary Ninject modules (the one above for now):

public partial class App : Application
{       
    protected override void OnStartup(StartupEventArgs e)
    {
        IocKernel.Initialize(new IocConfiguration());

        base.OnStartup(e);
    }
}

我用一个静态的IocKernel类来保存的IoC内核的应用广泛的实例,因此在需要的时候我可以很容易地访问它:

I have used a static IocKernel class to hold the application wide instance of the IoC kernel, so I can easily access it when needed:

public static class IocKernel
{
    private static StandardKernel _kernel;

    public static T Get<T>()
    {
        return _kernel.Get<T>();
    }

    public static void Initialize(params INinjectModule[] modules)
    {
        if (_kernel == null)
        {
            _kernel = new StandardKernel(modules);
        }
    }
}

此溶液确实使使用静态的ServiceLocator(该IocKernel),它通常被认为是反模式,因为它隐藏了类的依赖关系。然而,它是很难避免一些对UI类人工服务查询,因为他们必须有一个参数的构造函数,你不能反正控制实例化,所以你不能注入VM。至少这样可以让你测试虚拟机隔离,这就是所有的业务逻辑。

This solution does make use of a static ServiceLocator (the IocKernel), which is generally regarded as an anti-pattern, because it hides the class' dependencies. However it is very difficult to avoid some sort of manual service lookup for UI classes, since they must have a parameterless constructor, and you cannot control the instantiation anyway, so you cannot inject the VM. At least this way allows you to test the VM in isolation, which is where all the business logic is.

如果任何人有一个更好的办法,请你分享。

If anyone has a better way, please do share.

这篇关于如何处理依赖注入在WPF / MVVM应用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆