7

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

本质上,我想创建一个 n 层应用程序,但我也想要层之间的松散耦合。但是,我不太确定这是否是 Windows 窗体的好方法

现在我只是想知道使用任何 IoC(StructureMap、Ninject、Spring.Net)是否真的是一个明智的选择,我以前曾将它们用于 Asp.Net Web 应用程序,但现在让我怀疑的是使用当我浏览选项卡时,Windows 表单我的业务实体将持续存在,这与 Web 表单或 mvc 应用程序不同,在这些应用程序中,需要为执行的每个新请求注入我的业务实体,我的意思是因为Asp.Net 页面生命周期在哪里执行初始化和控制实例化。

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

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

任何观点都将不胜感激。

4

3 回答 3

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 服务会返回DTO 。IQueryHandler<TQuery, TResult>
  • 我们使用 Entity Framework 5(带有 T4 和设计器的 POCO 类)。
  • 这些 DTO(主要)用于阅读;它们不会被发送回服务器进行更新。
  • 任何更改状态的请求(执行用例)都是通过向服务器发送命令消息(通过ICommandHandler<TCommand>抽象)来完成的。
  • 一个用例被封装在实现ICommandHandler<TCommand>接口的单个​​类中。

正如我所说,它一直是依赖注入,这个和所描述的设计给了我们很大的灵活性。例如:

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

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

  • winforms 中的绑定经过优化,可以与 DataSet 一起使用。如果您尝试其他任何东西(Poco's、Entity Framework 实体等),您会遇到一些非常令人沮丧的时刻,您会发现除了 DataSets 之外对其他任何东西的支持都很少。很明显,微软没有在这个领域投资,也不会再投资这个领域。为了解决这些限制,我们编写了自己的BindingList<T>实现,但发现很难创建一个可以正确处理排序和过滤的实现(尤其是因为我们的 DTO 没有实现INotifyPropertyChanged)。我们还编写了自己的基础架构来为 Winforms 添加 DataAnnotations 验证支持。

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

  1. 同时......在我的架构的指挥方面
  2. 同时......在我的架构的查询方面
  3. 编写高度可维护的 WCF 服务
于 2013-11-14T22:00:49.640 回答
4

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

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

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

var SomeBusinessObject = container.Get<SomeBOType>(); //Ninject style.

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

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

于 2013-11-13T13:37:48.947 回答
2

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

容器提供了一个抽象层来容纳组件。尤其是 DI 容器,通过提供实例化类实例的通用工厂类来减少我刚才描述的那种依赖耦合。然后这些实例由容器配置,允许在更广泛的层面上重用构造逻辑。

你可以在这里阅读更多

于 2013-11-12T19:47:15.837 回答