【问题标题】:Building a WinForms Application using MVC and Ninject as IoC Container使用 MVC 和 Ninject 作为 IoC 容器构建 WinForms 应用程序
【发布时间】:2016-03-04 17:20:02
【问题描述】:

我不得不重新编写一个大型 WinForms 应用程序,我想使用 MVC 来增加测试能力等。我还想采用 Ninject 作为我的 IoC 容器,因为它轻量级、快速并且会增加我的可扩展性应用程序向前。

我已经阅读了大量内容,并设法开始了解这个新应用程序的架构。但是,我不确定在使用 Ninject 时我的想法是否正确。代码...

从 Program.cs 和相关类开始...

static class Program
{
    [STAThread]
    static void Main()
    {
        FileLogHandler fileLogHandler = new FileLogHandler(Utils.GetLogFilePath());
        Log.LogHandler = fileLogHandler;
        Log.Trace("Program.Main(): Logging initialized");

        CompositionRoot.Initialize(new ApplicationModule());

        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(CompositionRoot.Resolve<ApplicationShellView>());
    }
}

public class CompositionRoot
{
    private static IKernel _ninjectKernel;

    public static void Initialize(INinjectModule module)
    {
        _ninjectKernel = new StandardKernel(module);
    }

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

public class ApplicationModule : NinjectModule
{
    public override void Load()
    {
        Bind(typeof(IApplicationShellView)).To(typeof(ApplicationShellView));
    }
}

我的ApplicationShellView

public partial class ApplicationShellView : Form, IApplicationShellView
{
    public ApplicationShellView()
    {
        InitializeComponent();
    }

    public void InitializeView()
    {
        dockPanel.Theme = vS2012LightTheme;
    }
}

带接口

public interface IApplicationShellView
{
    void InitializeView();
}

这个视图的控制器是

public class ApplicationShellController
{
    private IApplicationShellView view;

    public ApplicationShellController(IApplicationShellView view)
    {
        view.InitializeView();
    }
}

目前控制器是多余的,虽然这段代码有效并且我的视图显示,但我有一些重要的问题......

  1. 我应该使用ApplicationShellController 来初始化我的表单吗,目前这使用MVC“模式”?
  2. 感觉就像我写了一个服务定位器,从我读到的内容来看,这很糟糕。我还应该如何使用 Ninject for IoC 来初始化我的应用程序?
  3. 关于我做的对[如果有的话!]/错的任何其他建议?

非常感谢您的宝贵时间。

【问题讨论】:

  • 我认为这个/类似的问题之前被问过,特别是关于“服务定位器”。一般来说,这个问题是/应该更多关于如何使用 WinForms 结合任何 DI 容器进行 MVC...
  • 这不是一个通用问题。这是一个关于具体情况的问题。你怎么能说它以前被问过?我的问题不是专门针对服务定位器,而是如何将 MVC 与 IOC 容器耦合。
  • 您能否举例说明如何在没有 DI 的情况下在 ApplicationShellView 中使用其他表单或服务? (我对winforms没有经验,我应该看看生活管理)。
  • 所以我的意思是我认为这个问题最困难的部分是如何将 MVC + DI-container 与 winforms 结合起来(无论使用哪个 DI 容器)。我认为已经存在涉及这方面的问题。 ninject 特定的部分非常小,我很乐意为您提供帮助。但与@MaDeRkAn 一样,我对 WinForms 的了解还不够。
  • this 怎么样?

标签: c# winforms model-view-controller ninject inversion-of-control


【解决方案1】:
  1. 不,您不应该初始化控制器,这正是 IoC 和 Ninject 的用途。当您初始化视图/表单时,Ninject 应该让视图获取它所依赖的控制器,这将自动获取它所依赖的控制器等等。
    当然,这不会像您现在设置的那样工作。首先,您的视图需要知道它所依赖的控制器。

    public partial class ApplicationShellView : Form, IApplicationShellView
    {
        private IApplicationShellController _controller;
    
        public ApplicationShellView()
        {
            InitializeComponent();
            init();
    
            //InitializeView()
        }
    
        private void init() {
            _controller = NinjectProgram.Kernel.Get<IApplicationShellController>();
            //Because your view knows the controller you can always pass himself as parameter or even use setter to inject
            //For example:  _controller.SetView1(this);
        }
    
        public void InitializeView()
        {
            dockPanel.Theme = vS2012LightTheme;
        }
    }
    
    public class ApplicationShellController : IApplicationShellController
    {
    
        //Implementes functionality for the MainForm.
    
        public ApplicationShellController()
        {
            //Also possible to add other controllers with DI
        }
    }
    
  2. 这确实看起来像一个服务定位器,只需初始化您的视图就足够了。

    public class NinjectProgram
    {
        //Gets the inject kernal for the program.
        public static IKernel Kernel { get; protected set; }
    }
    
    public class Program : NinjectProgram
    {
        [STAThread]
        private static void Main()
        {
            Kernel = new StandardKernel();
            Kernel.Load(new ApplicationModule());
    
            Application.Run(new ApplicationShellView());
        }
    }
    
    public class ApplicationModule : NinjectModule
    {
        public override void Load()
        {
            //Here is where we define what implementations map to what interfaces.
            Bind<IApplicationShellController>().To<ApplicationShellController>();
    
            //We can also load other modules this project depends on.
            Kernel.Load(new NinjectModule());
        }
    }
    
  3. 不要试图让它变得太复杂,一个好的开始很重要,但您始终可以在开发过程中随时随地应用更改。

我相信以下 GitHub 项目可能是一个很好的起点:Example of how you might use Ninject within a WinForms application.

如果您还有其他问题,请发表评论,我会尽快回复您的问题

【讨论】:

  • 我现在已经掌握了 Seemann 的书“.NET 的依赖注入”,它似乎建议使用 DI Container/Composition Root,就像我上面提到的那样。这本书建议这个 CompositionRoot 应该是唯一应该使用 IoC 容器的 ResolveGet 用于 Ninject)方法的地方。然后它建议使用这个“CompositionRoot”使用“三个调用模式”,我没有得到的是当这个模式变成“服务定位器反模式”时?从我在第一章读到的内容看来。本书的第 3 条,我们不应该在 CompositionRoot 之外使用内核调用。非常感谢
  • @Killercam 我同意 Mark 的说法,当内核在“组合根”之外使用时,它变成了一种反模式。我(大部分)也同意他的观点。我个人有时会违反它,但对于某些工厂来说,以防对容器的依赖关系的缺点超过将其放在组合根目录中的好处。
  • 我对 WinForms 不是很熟悉,但因为我相信 @Killercam 的原始代码可以正常工作,所以我认为调用 _controller = NinjectProgram.Kernel.Get&lt;IApplicationShellController&gt;(); 是服务位置,在这种情况下是反模式。为什么?因为调用可以更靠近 main 方法——就像在 Killercam 的原始解决方案中一样。
  • @Oceans 我遇到的另一个问题是控制器必须能够直接作用于视图,因此必须按照您的建议传入视图 - 这会创建一个循环引用,该引用违背“真实" 依赖注入。然而,因为在 WinForms 中我们没有绑定系统,如果不引入丑陋的中介者,这种循环引用很难被打破。我是否应该担心设置控制器对视图的访问(必须为所有控制器/视图完成),因为它仍然是可测试的并且可以工作,还是我应该做其他事情?
猜你喜欢
  • 2013-08-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-04
  • 1970-01-01
  • 1970-01-01
  • 2013-02-09
  • 2015-03-03
相关资源
最近更新 更多