具有 NHibernate、Ninject 和 Windows 表单的 3 层架构

3 Tier Architecture with NHibernate, Ninject and Windows Forms

所以我正在重构一个中小型 Windows Forms 应用程序,该应用程序由通过 NHibernate 访问的 SQLite 数据库支持。当前的解决方案仅包含一个 App 项目和一个 Lib 项目,因此它的结构不是很好,并且在很多地方都紧密耦合。

我从 this answer 中的结构开始,但 运行 遇到了一些问题。

  1. 数据库初始化: 由于构建 NHibernate SessionFactory 的代码在 DAL 中,我需要将 ISession 注入我的存储库,因此我需要直接在我的 Forms 项目中引用 DAL 和 NHibernate,以便能够使用 Ninject (哪个应该在 App Project / Presentation Layer 中完成,对吗?)

这不是我试图用这种架构避免的事情之一吗?

理想情况下哪些项目应该相互引用?

  1. 一般DI: 我很难弄清楚如何正确地进行 DI。我读到过关于使用组合根只有一个地方可以直接使用 Ninject 容器,但这与当前使用 NHibernate 会话的方式并不能很好地发挥作用。 我们有一个 MainForm ,它显然是应用程序的入口点,并在其整个生命周期内保留一个 Session 。此外,用户可以打开多个 SubForms(主要但不限于)用于编辑单个实体),目前每个实体都有一个生命周期较短的单独会话。这是通过公开 SessionFactory 并根据需要打开新会话的静态 Helper 来完成的。

除了组合根模式之外,还有其他使用DI和Windows形式的方法吗?

我如何利用 Ninject 的功能进行作用域注入以在每个表单的基础上管理我的 NHibernate 会话(如果可能的话)?

  1. 术语: 我对什么是 RepositoryService 有点困惑。对已发布答案的评论指出“存储库包含业务逻辑是可以的,在这种情况下您可以将其称为服务”。当我们经常想将过滤等推送到数据库中时,我们的存储库只包含基本的 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 设计模式。这是我可以提供给您的架构。

  1. MyApp.Infrastructure // 基础层 - 无参考
  2. MyApp.Models // 域层 - 对基础设施的引用
  3. MyApp.Presenter // 就像 MVC 中的控制器一样 - 对服务、模型的引用,
  4. MyApp.Repository.NH // DAL 层 - 参考模型、基础设施
  5. MyApp.Services // BLL 层 - 对存储库、模型的引用
  6. MyApp.Services.Cache // 缓存 BLL 层(强烈推荐)- 参考服务、模型
  7. 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);
   }

我不确定我是否答对了你的问题,但也许可以给你一个想法:)