DI在桌面应用中有意义吗?

时间:2013-11-12 18:33:36

标签: .net dependency-injection inversion-of-control ioc-container n-tier-architecture

我即将创建一个桌面应用程序(使用.NET窗体)

基本上,我想创建一个n层应用程序,但我也希望层之间松散耦合。但是,我不太确定这是否适用于Windows窗体

现在我只是想知道使用任何IoC(StructureMap,Ninject,Spring.Net)是否真的是一个明智的选择,我之前使用过它们用于Asp.Net网络应用程序,但现在让我怀疑的是事实使用Windows窗体时我的业务实体将在我浏览选项卡时保持不变,而不像网络表单或mvc应用程序那样需要为每个执行的新请求注入我的业务实体,我的意思是因为{{3在哪里执行初始化并控制实例化。

这是一个长期开发项目,它结合了维护跟踪,库存,工作单和管理报告。我目前正在制定其架构提案。

也许我误解了使用IoC的重点,所以请告诉我你认为什么是更好的选择?

任何观点都将不胜感激。

3 个答案:

答案 0 :(得分:5)

你的问题很奇怪。您的问题意味着您将以不同于Web应用程序的方式为Winforms应用程序编写业务层,但如果正确应用分层,业务层应完全独立于所使用的技术。因此,从这个意义上讲,如果要在Web应用程序的业务层中应用依赖注入模式,则还应将其应用于桌面应用程序的业务层。

我目前正在自己​​开发Winforms项目并广泛使用依赖注入模式(和IoC容器)。对我来说,不应该使用DI;它自然而然地应用了SOLID原则。然而,你是否应该使用IoC容器是一个完全不同的问题,尽管对于我所编写的应用程序类型和我使用的架构类型,我无法想象没有它的生活。

虽然桌面应用程序在性质上与Web应用程序非常不同,但我在这两种类型的应用程序上使用相同的模式。例如,我在Windows窗体类中使用构造函数注入,这些窗体主要依赖于几个通用接口,即:

  • IRepository<TEntity>(存储库模式),用于加载实体。
  • IQueryHandler<TQuery, TResult>用于执行各种复杂或自定义查询。
  • ICommandHandler<TCommand>用于执行用例(处理用户操作)。

我在我构建的Web应用程序中使用相同的抽象。

这些接口帮助我几个月前将这个桌面应用程序从2层应用程序(在桌面应用程序中运行的所有业务逻辑)更改为3层应用程序(现在所有业务逻辑都转移到WCF服务) )。我们无需更改表单中的任何代码即可完成此操作。

在2层模型中,我们没有直接注入ICommandHandler<TCommand>实现,而是注入了一个(单例)代理类,每次调用它时都会创建一个新的实现。例如,当表单调用注入的ICommandHandler<ProcessOrder>时,实际的CommandHandlerProxy<ProcessOrder>将启动一个生命周期范围(一种模仿Web应用程序的每个请求生活方式的生活方式)并创建真实的{{1}将执行实际逻辑的类。通过这样做,我们确保在该“请求”中的所有类中注入单个工作单元(在我们的示例中为实体框架的ProcessOrderCommandHandler)。当然,所有依赖注入一直都是调用图。

在新的3层模型中,表单注入DbContext,它将给定命令序列化为JSON并将其发送到WCF服务,WCF服务将对其进行拾取,反序列化命令,创建WcfProxyCommandHandler<TCommand>并执行命令。

但请记住,这个模型可能与您可能习惯的非常不同。例如:

  • 真实实体隐藏在WCF服务之后。桌面应用程序对它们一无所知。
  • 当通过ProcessOrderCommandHandler抽象请求数据时,从WCF服务返回DTO
  • 我们使用Entity Framework 5(POCO类与T4和设计师)。
  • 这些DTO(大部分)用于阅读;它们不会被送回服务器进行更新。
  • 任何状态更改请求(用例的执行)都是通​​过向服务器发送命令消息(通过IQueryHandler<TQuery, TResult>抽象)来完成的。
  • 一个用例封装在实现ICommandHandler<TCommand>接口的单个​​类中。

正如我所说的那样,它依赖于依赖注入,这和所描述的设计为我们提供了很大的灵活性。例如:

  • 我们发现添加新功能非常容易。
  • 我们发现很容易添加新的跨领域问题。
  • 降低心理障碍;它使应用程序更易于维护,并且不太可能以意想不到的方式中断。

但是我在这个过程中发现了一件事:

  • winforms中的绑定经过优化,可与DataSet配合使用。如果你尝试其他任何东西(Poco's,实体框架实体等),你会得到一些非常令人沮丧的时刻,你会发现除了DataSet之外的其他任何东西的支持都是最小的。很明显,微软没有在这个领域投资,也不会再投资这个领域了。为了解决这些限制,我们编写了自己的ICommandHandler<TCommand>实现,并发现很难创建一个能够正确处理排序和过滤的实现(特别是因为我们的DTO没有实现BindingList<T>)。我们还wrote our own infrastructure向Winforms添加DataAnnotations验证支持。

如果您想了解我使用的设计的更多信息,请阅读以下文章:

  1. Meanwhile... on the command side of my architecture
  2. Meanwhile... on the query side of my architecture
  3. Writing Highly Maintainable WCF Services

答案 1 :(得分:4)

在你的情况下使用DI / IoC仍然有意义,因为它完全是放弃控制依赖关系的来源,管理他们的生命周期等等。

从这个意义上讲,原则Inversion of Control几乎与平台无关,它在ASP.NETWinForms之间的连接方面可能略有不同,但主体是相同的

因此,在ASP.NET中,通常控制反转通常是通过Dependency Injection 实现,并且主要是通过构造函数注入控制器等,这并不意味着您必须遵循Windows窗体中的相同模式 - 例如,您可以让IoC容器的全局实例可用于所有窗体,并让它们通过类似

的方式获得它们的depdendencies
var SomeBusinessObject = container.Get<SomeBOType>(); //Ninject style.

在这种情况下,它仍然是IoC(表单不直接创建依赖项,它不知道容器是否给它一个全新的实例,或者是一个全局共享的单个静态实例,它不知道何时它将被销毁,等等)但严格来说并不是依赖 Injection - 但是你可以获得IoC框架为你管理复杂依赖图的所有好处。

另外请记住,您始终可以处理用户切换标签的事件,并将其视为全新的“请求”,然后丢弃并重新获取您的依赖关系,如果由于某种原因,对应用程序应该如何工作很重要。

答案 2 :(得分:2)

是的,今天比以往任何时候都更加关注重用现有组件并将不同组件连接在一起以形成一个有凝聚力的架构。但是这种布线很快就会成为一项艰巨的任务,因为随着应用程序的大小和复杂性的增加,依赖性也会增加。缓解依赖关系扩散的一种方法是使用依赖注入(DI),它允许您将对象注入到类中,而不是依赖于类来创建对象本身。

容器提供了一个抽象层,用于容纳组件。特别是DI容器通过提供实例化类实例的通用工厂类来减少我刚刚描述的依赖关系的类型。然后,这些实例由容器配置,允许在更广泛的层次上重用构造逻辑。

您可以阅读更多here