Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/xcode/7.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Dependency injection 在Windows客户端(WPF)应用程序中执行依赖项注入的正确方法_Dependency Injection_Ninject_Service Locator_Testability_Thick Client - Fatal编程技术网

Dependency injection 在Windows客户端(WPF)应用程序中执行依赖项注入的正确方法

Dependency injection 在Windows客户端(WPF)应用程序中执行依赖项注入的正确方法,dependency-injection,ninject,service-locator,testability,thick-client,Dependency Injection,Ninject,Service Locator,Testability,Thick Client,我习惯于在web应用程序中使用IoC/DI,主要是使用MVC3的Ninject。我的控制器是为我创建的,用所有适当的依赖项、子依赖项等填充 但是,在厚客户端应用程序中情况就不同了。我必须创建我自己的对象,或者我必须恢复到服务定位器风格的方法,在这种方法中,我要求内核(可能通过一些接口,以允许可测试性)给我一个具有依赖性的完整对象 然而,我在几个地方看到服务定位器被描述为反模式 所以我的问题是——如果我想在我的厚客户端应用程序中受益于Ninject,有没有更好/更合适的方法来实现这一切 可测试性

我习惯于在web应用程序中使用IoC/DI,主要是使用MVC3的Ninject。我的控制器是为我创建的,用所有适当的依赖项、子依赖项等填充

但是,在厚客户端应用程序中情况就不同了。我必须创建我自己的对象,或者我必须恢复到服务定位器风格的方法,在这种方法中,我要求内核(可能通过一些接口,以允许可测试性)给我一个具有依赖性的完整对象

然而,我在几个地方看到服务定位器被描述为反模式

所以我的问题是——如果我想在我的厚客户端应用程序中受益于Ninject,有没有更好/更合适的方法来实现这一切

  • 可测试性
  • 适当的DI/IoC
  • 尽可能少的耦合
请注意,我在这里谈论的不仅仅是MVVM和将视图模型转换为视图。这是由于需要从内核提供一个repository类型的对象,然后将从该存储库获取的实体注入功能(数据当然来自数据库,但它们也需要一些对象作为参数,这取决于世界的状态,Ninject知道如何提供)。我是否可以在不让存储库和实体成为不稳定的混乱的情况下以某种方式做到这一点

如果有什么不清楚的地方,请告诉我。谢谢

编辑7月14日

我相信所提供的两个答案可能是正确的。然而,我身体的每一根纤维都在对抗这种变化;其中一些可能是由于缺乏知识造成的,但也有一个具体的原因使我很难看到这种做事方式的优雅

在最初的问题中,我没有充分解释这一点,但问题是我正在编写一个库,它将被几个(起初是4-5个,以后可能会更多)WPF客户端应用程序使用。这些应用程序都在同一个域模型上运行,所以将它们保存在一个库中是保持干燥的唯一方法。然而,这个系统的客户也有可能编写自己的客户机——我希望他们有一个简单、干净的库可以与之交谈。我不想强迫他们在他们的组合根中使用DI(在他的书中使用Mark Seeman这样的术语),因为与他们只是更新MyCrazySystemAdapter()并使用它相比,这会使事情变得非常复杂

现在,MyCrazySystemAdapter(之所以选择这个名称,是因为我知道人们在这里会不同意我的观点)需要由子组件组成,并使用DI组合在一起。MyCrazySystemAdapter本身不需要注射。它是客户端与系统对话所需的唯一接口。因此,客户应该很高兴地得到其中一个,DI就像魔术一样发生在幕后,对象由许多不同的对象组成,使用最佳实践和原则

我确实意识到这将是一种有争议的做事方式。然而,我也知道哪些人将成为这个API的客户。如果他们发现他们需要学习和连接DI系统,并在应用程序入口点(组合根)提前创建整个对象结构,而不是更新单个对象,他们会给我中指,直接搞乱数据库,以你难以想象的方式把事情搞砸


TL;DR:对于客户端来说,交付一个结构合理的API太麻烦了。我的API需要交付一个单独的对象——使用DI和适当的实践在幕后构建——供他们使用。有时,现实世界压倒了向后构建一切以保持模式和实践真实性的愿望。

我对WPF或MVVM一无所知,但您的问题基本上是如何在不使用服务定位器(或直接使用容器)的情况下从容器中取出内容,对吗?
如果是,我可以给你举个例子

关键是您使用的是一个工厂,它在内部使用容器。这样,您实际上只在一个地方使用容器

注意:我将使用WinForms的一个示例,并且没有绑定到特定的容器(因为,正如我所说,我不知道WPF…并且我使用Castle Windsor而不是NInject),但是由于您的基本问题没有具体绑定到WPF/NInject,所以您应该很容易将我的答案“移植”到WFP/NInject

工厂是这样的:

public class Factory : IFactory
{
    private readonly IContainer container;

    public Factory(IContainer container)
    {
        this.container = container;
    }

    public T GetStuff<T>()
    {
        return (T)container.Resolve<T>();
    }
}
当应用程序启动时,容器被初始化,主窗体被解析(因此它通过构造函数注入获得工厂)


我建议看看Caliburn之类的MVVM框架。它们提供了与IoC容器的集成


基本上,您应该在app.xaml中构建完整的应用程序。如果某些部件需要稍后创建,因为您还不知道在启动时创建它们的所有内容,那么可以将工厂作为接口(请参见下文)或Func(请参见)注入到需要创建此实例的类中。在下一个Ninject版本中,这两种技术都将得到本机支持

e、 g

公共接口IFooFactory{IFoo CreateFoo();}
公共类FooFactory:IFooFactory
{
私有IKernel内核;
食品工厂(IKernel内核)
{
this.kernel=内核;
}
公共ifoocreatefoo()
{
this.kernel.Get();
}
}

请注意,工厂实现在逻辑上属于容器配置,而不是业务类的实现。

-1您的
GetStuff()
是一个穿着连衣裙的服务定位器。不要这样做。(你剩下的答案没有错,但狡猾让我觉得很有趣)甚至在看到雷莫的答案(我认为这是正确的方法)之前,我就打算建议工厂注资(在适当的情况下作为Funcs a la)
public partial class MainForm : Form
{
    private readonly IFactory factory;

    public MainForm(IFactory factory)
    {
        this.factory = factory;
        InitializeComponent();  // or whatever needs to be done in a WPF form
    }
}
static class Program
{
    static void Main()
    {
        var container = new Container();
        container.Register<MainForm>();
        container.Register<IFactory, Factory>();
        container.Register<IYourRepository, YourRepository>();

        Application.Run(container.Resolve<MainForm>());
    }
}
var repo = this.factory.GetStuff<IYourRepository>();
repo.DoStuff();
var form = this.factory.GetStuff<IAnotherForm>();
form.Show();
public partial class MainForm : Form
{
    private readonly IAnotherForm form;

    // pass AnotherForm via constructor injection
    public MainForm(IAnotherForm form)
    {
        this.form = form;
        InitializeComponent();  // or whatever needs to be done in a WPF form
    }

    // open AnotherForm
    private void Button1_Click(object sender, EventArgs e)
    {
        this.form.Show();
    }
}

public partial class AnotherForm : Form
{
    private readonly IRepository repo;

    // pass the repository via constructor injection
    public AnotherForm(IRepository repo)
    {
        this.repo= repo;
        InitializeComponent();  // or whatever needs to be done in a WPF form

        // use the repository
        this.repo.DoStuff();
    }
}
public interface IFooFactory { IFoo CreateFoo(); }
public class FooFactory : IFooFactory
{
    private IKernel kernel;
    FooFactory(IKernel kernel)
    {
        this.kernel = kernel;
    }

    public IFoo CreateFoo()
    {
        this.kernel.Get<IFoo>();
    }
}