具有 NHibernate、Ninject 和 Windows 表单的 3 层架构
3 Tier Architecture with NHibernate, Ninject and Windows Forms
所以我正在重构一个中小型 Windows Forms 应用程序,该应用程序由通过 NHibernate 访问的 SQLite 数据库支持。当前的解决方案仅包含一个 App 项目和一个 Lib 项目,因此它的结构不是很好,并且在很多地方都紧密耦合。
我从 this answer 中的结构开始,但 运行 遇到了一些问题。
- 数据库初始化:
由于构建 NHibernate SessionFactory 的代码在 DAL 中,我需要将 ISession 注入我的存储库,因此我需要直接在我的 Forms 项目中引用 DAL 和 NHibernate,以便能够使用 Ninject (哪个应该在 App Project / Presentation Layer 中完成,对吗?)
这不是我试图用这种架构避免的事情之一吗?
理想情况下哪些项目应该相互引用?
- 一般DI:
我很难弄清楚如何正确地进行 DI。我读到过关于使用组合根只有一个地方可以直接使用 Ninject 容器,但这与当前使用 NHibernate 会话的方式并不能很好地发挥作用。
我们有一个
MainForm
,它显然是应用程序的入口点,并在其整个生命周期内保留一个 Session 。此外,用户可以打开多个 SubForms
(主要但不限于)用于编辑单个实体),目前每个实体都有一个生命周期较短的单独会话。这是通过公开 SessionFactory 并根据需要打开新会话的静态 Helper 来完成的。
除了组合根模式之外,还有其他使用DI和Windows形式的方法吗?
我如何利用 Ninject 的功能进行作用域注入以在每个表单的基础上管理我的 NHibernate 会话(如果可能的话)?
- 术语:
我对什么是
Repository
和 Service
有点困惑。对已发布答案的评论指出“存储库包含业务逻辑是可以的,在这种情况下您可以将其称为服务”。当我们经常想将过滤等推送到数据库中时,我们的存储库只包含基本的 CRUD 操作感觉有点无用。因此,我们继续使用 GetByName
或更复杂的 GetAssignmentCandidates
等方法扩展存储库。感觉很合适,因为实现在业务层中,但它们仍称为存储库。我们还使用 Controllers
来 类 直接与 UI 元素交互,但我认为这个名称在 Web 世界中更常见。
我们的Repositories
真的应该叫Services
吗?
对不起,文字墙。任何答案将不胜感激!
关于 1:
是和不是。是的,您希望 UI 层不依赖于向下 x 层的某些细节。但事实并非如此。组合根只是驻留在同一个程序集中,逻辑上它不是同一层。
关于 2:
限制容器的使用。工厂(对于会话,..)有时是必要的。应避免使用 static
。但是,某些框架会阻止您使用理想的设计。在那种情况下,尝试尽可能地近似。
如果您目前可以做到 new FooForm()
,那么您可以用 DI 或 DI 工厂 (p.Ex。ninject.extensions.Factory) 替换它。如果您完全无法控制类型的实例化方式,那么您需要使用 static
像服务定位器一样访问内核,然后 "locate" 直接依赖(而间接依赖被注入到直接依赖中通过 DI 容器)。
关于 3:我认为这是有争议的,而且可能经常被误解。我不认为 你所谓的 类 真的很重要(当然是,但是代码库的一致性比决定是否全部命名更重要存储库或服务),重要的是如何设计它们的职责和关系。
因此,我自己更喜欢在名为 类 的 -Query
中提取过滤器和内容,每个都只提供一种方法。但其他人有其他偏好......我认为关于这个主题的博客文章等已经足够多了,在这里重新散列是没有用的。
针对像您这样的情况实施的最佳实践是使用 MVP 设计模式。这是我可以提供给您的架构。
- MyApp.Infrastructure // 基础层 - 无参考
- MyApp.Models // 域层 - 对基础设施的引用
- MyApp.Presenter // 就像 MVC 中的控制器一样 - 对服务、模型的引用,
- MyApp.Repository.NH // DAL 层 - 参考模型、基础设施
- MyApp.Services // BLL 层 - 对存储库、模型的引用
- MyApp.Services.Cache // 缓存 BLL 层(强烈推荐)- 参考服务、模型
MyApp.UI.Web.WebForms // UI 图层 - 引用所有图层
我会尽量用'Category'模型的基本实现的例子来解释。
-基础设施-
- EntityBase.cs
- BussinesRule.cs
- IEntity.cs
- IRepository.cs
-型号-
类别(文件夹)
- Category.cs // 实现 IEntity 并派生自 EntityBase
- ICategoryRepository.cs // 实现 IRepository
-主持人-
接口
IHomeView.cs // 把每一个 属性 和你需要的方法。
ICategoryPresenter.cs
实施
CategoryPresenter.cs // 实现 ICategoryPresenter
CategoryPresenter(IHomeView 视图,ICategorySevice 类别服务){
}
-存储库-
存储库(文件夹)
- GenricRepository.cs // 实现 IRepository
- CategoryRepository : 实现 ICategoryRepository 并派生自 GenricRepository
-服务-
接口
- ICategorySevice.cs
- AddCategory(类别模型);
实施
- CategorySevice.cs // 实现 ICategorySevice
CategorySevice(ICategoryRepository categoryRepository){}
AddCategory(类别模型){
// 通过 ICategoryRepository 实现做员工。
}
-Services.Cache-
// 这一切都取决于您的选择..Radis 或 Web 缓存..
-UI.Web.WebForms-
Views - Home(Folder) // 实现类似于 MVC 视图中的结构。
Index.aspx // 实现 IHomeView
Page_Init(){
// Get instance of Presenter
var categoryPresenter = CategoryPresenter(this, new CategorySevice);
}
我不确定我是否答对了你的问题,但也许可以给你一个想法:)
所以我正在重构一个中小型 Windows Forms 应用程序,该应用程序由通过 NHibernate 访问的 SQLite 数据库支持。当前的解决方案仅包含一个 App 项目和一个 Lib 项目,因此它的结构不是很好,并且在很多地方都紧密耦合。
我从 this answer 中的结构开始,但 运行 遇到了一些问题。
- 数据库初始化: 由于构建 NHibernate SessionFactory 的代码在 DAL 中,我需要将 ISession 注入我的存储库,因此我需要直接在我的 Forms 项目中引用 DAL 和 NHibernate,以便能够使用 Ninject (哪个应该在 App Project / Presentation Layer 中完成,对吗?)
这不是我试图用这种架构避免的事情之一吗?
理想情况下哪些项目应该相互引用?
- 一般DI:
我很难弄清楚如何正确地进行 DI。我读到过关于使用组合根只有一个地方可以直接使用 Ninject 容器,但这与当前使用 NHibernate 会话的方式并不能很好地发挥作用。
我们有一个
MainForm
,它显然是应用程序的入口点,并在其整个生命周期内保留一个 Session 。此外,用户可以打开多个SubForms
(主要但不限于)用于编辑单个实体),目前每个实体都有一个生命周期较短的单独会话。这是通过公开 SessionFactory 并根据需要打开新会话的静态 Helper 来完成的。
除了组合根模式之外,还有其他使用DI和Windows形式的方法吗?
我如何利用 Ninject 的功能进行作用域注入以在每个表单的基础上管理我的 NHibernate 会话(如果可能的话)?
- 术语:
我对什么是
Repository
和Service
有点困惑。对已发布答案的评论指出“存储库包含业务逻辑是可以的,在这种情况下您可以将其称为服务”。当我们经常想将过滤等推送到数据库中时,我们的存储库只包含基本的 CRUD 操作感觉有点无用。因此,我们继续使用GetByName
或更复杂的GetAssignmentCandidates
等方法扩展存储库。感觉很合适,因为实现在业务层中,但它们仍称为存储库。我们还使用Controllers
来 类 直接与 UI 元素交互,但我认为这个名称在 Web 世界中更常见。
我们的Repositories
真的应该叫Services
吗?
对不起,文字墙。任何答案将不胜感激!
关于 1: 是和不是。是的,您希望 UI 层不依赖于向下 x 层的某些细节。但事实并非如此。组合根只是驻留在同一个程序集中,逻辑上它不是同一层。
关于 2:
限制容器的使用。工厂(对于会话,..)有时是必要的。应避免使用 static
。但是,某些框架会阻止您使用理想的设计。在那种情况下,尝试尽可能地近似。
如果您目前可以做到 new FooForm()
,那么您可以用 DI 或 DI 工厂 (p.Ex。ninject.extensions.Factory) 替换它。如果您完全无法控制类型的实例化方式,那么您需要使用 static
像服务定位器一样访问内核,然后 "locate" 直接依赖(而间接依赖被注入到直接依赖中通过 DI 容器)。
关于 3:我认为这是有争议的,而且可能经常被误解。我不认为 你所谓的 类 真的很重要(当然是,但是代码库的一致性比决定是否全部命名更重要存储库或服务),重要的是如何设计它们的职责和关系。
因此,我自己更喜欢在名为 类 的 -Query
中提取过滤器和内容,每个都只提供一种方法。但其他人有其他偏好......我认为关于这个主题的博客文章等已经足够多了,在这里重新散列是没有用的。
针对像您这样的情况实施的最佳实践是使用 MVP 设计模式。这是我可以提供给您的架构。
- MyApp.Infrastructure // 基础层 - 无参考
- MyApp.Models // 域层 - 对基础设施的引用
- MyApp.Presenter // 就像 MVC 中的控制器一样 - 对服务、模型的引用,
- MyApp.Repository.NH // DAL 层 - 参考模型、基础设施
- MyApp.Services // BLL 层 - 对存储库、模型的引用
- MyApp.Services.Cache // 缓存 BLL 层(强烈推荐)- 参考服务、模型
MyApp.UI.Web.WebForms // UI 图层 - 引用所有图层
我会尽量用'Category'模型的基本实现的例子来解释。
-基础设施-
- EntityBase.cs
- BussinesRule.cs
- IEntity.cs
- IRepository.cs
-型号-
类别(文件夹)
- Category.cs // 实现 IEntity 并派生自 EntityBase
- ICategoryRepository.cs // 实现 IRepository
-主持人-
接口
IHomeView.cs // 把每一个 属性 和你需要的方法。
ICategoryPresenter.cs
实施
CategoryPresenter.cs // 实现 ICategoryPresenter
CategoryPresenter(IHomeView 视图,ICategorySevice 类别服务){
}
-存储库-
存储库(文件夹)
- GenricRepository.cs // 实现 IRepository
- CategoryRepository : 实现 ICategoryRepository 并派生自 GenricRepository
-服务-
接口
- ICategorySevice.cs
- AddCategory(类别模型);
实施
- CategorySevice.cs // 实现 ICategorySevice
CategorySevice(ICategoryRepository categoryRepository){}
AddCategory(类别模型){ // 通过 ICategoryRepository 实现做员工。 }
-Services.Cache-
// 这一切都取决于您的选择..Radis 或 Web 缓存..
-UI.Web.WebForms-
Views - Home(Folder) // 实现类似于 MVC 视图中的结构。 Index.aspx // 实现 IHomeView
Page_Init(){
// Get instance of Presenter
var categoryPresenter = CategoryPresenter(this, new CategorySevice);
}
我不确定我是否答对了你的问题,但也许可以给你一个想法:)