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

lui*_*ent 7 .net dependency-injection ioc-container inversion-of-control n-tier-architecture

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

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

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

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

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

任何观点都将不胜感激.

Ste*_*rne 5

在您的情况下使用 DI / IoC 仍然有意义,因为这一切都是为了放弃对依赖项来自何处、由谁管理其生命周期等的控制。

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

因此,虽然在 ASP.NET 中,控制反转通常是通过构造函数注入到控制器等来实现的Dependency Injection,但这并不意味着您必须在 Windows 窗体中遵循相同的模式 - 例如,您可以只创建一个全局的IoC 容器的实例可用于所有表单,并让它们通过类似的方式获取依赖关系

var SomeBusinessObject = container.Get<SomeBOType>(); //Ninject style.
Run Code Online (Sandbox Code Playgroud)

在这种情况下,它仍然是 IoC(表单不会直接创建依赖项,它不知道容器是否为它提供了一个全新的实例,或者是全局共享的单个静态实例,它不知道什么时候会销毁等),但严格来说它不是依赖注入- 但是您可以获得由 IoC 框架为您管理的复杂依赖关系图的所有好处。

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


Ste*_*ven 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应用程序的每个请求生活方式的生活方式),并将创建ProcessOrderCommandHandler可以执行实际逻辑的真实类.通过这样做,我们确保DbContext在该"请求"中的所有类中注入单个工作单元(在我们的情况下为实体框架).当然所有依赖注入一直沿着调用图.

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

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

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

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

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

然而,我在这个过程中发现了一件事:

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

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

  1. 同时......在我的架构的命令方面
  2. 同时......在我的架构的查询方面
  3. 编写高度可维护的WCF服务