你的问题很奇怪。您的问题意味着您将以与 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 验证支持。
如果您想了解更多关于我使用的设计的信息,请阅读以下文章:
- 同时......在我的架构的指挥方面
- 同时......在我的架构的查询方面
- 编写高度可维护的 WCF 服务