【问题标题】:N-layered database application without using an ORM, how does the UI specify what it needs of data to display?不使用 ORM 的 N 层数据库应用程序,UI 如何指定需要显示的数据?
【发布时间】:2010-12-04 04:58:00
【问题描述】:

我在这里寻找指针和信息,我会制作这个 CW,因为我怀疑它没有一个正确的答案。这是针对 C# 的,因此我将在下面对 Linq 进行一些引用。我也为这篇长文道歉。让我在这里总结一下问题,然后是完整的问题。

总结:在 UI/BLL/DAL/DB 4 层应用程序中,如何更改用户界面,以显示更多列(例如在网格中),避免通过业务逻辑层泄漏到数据访问中层,以获取要显示的数据(假设它已经在数据库中)。


让我们假设一个具有 3(4) 层的分层应用程序:

  • 用户界面 (UI)
  • 业务逻辑层 (BLL)
  • 数据访问层 (DAL)
  • 数据库(DB;第 4 层)

在这种情况下,DAL 负责构造 SQL 语句并针对数据库执行它们,返回数据。

“正确”构建这样一个层以始终执行“选择*”的唯一方法是什么?对我来说,这是一个很大的禁忌,但让我解释一下为什么我想知道。

假设我希望在我的 UI 中显示所有具有有效就业记录的员工。我所说的“活跃”是指雇佣记录从开始到日期包含今天(或者甚至可能是我可以在用户界面中设置的日期)。

在这种情况下,假设我想向所有这些人发送电子邮件,所以我在 BLL 中有一些代码可以确保我还没有向同一个人发送电子邮件,等等。

对于 BLL,它需要最少的数据量。也许它会调用数据访问层来获取活跃员工的列表,然后调用来获取它已发送的电子邮件列表。然后它加入这些并构造一个新列表。也许这可以在数据访问层的帮助下完成,这并不重要。

重要的是,对于业务层,它需要的数据确实不多。也许它只需要两个列表的每个员工的唯一标识符进行匹配,然后说“这些是那些活跃的人的唯一标识符,你还没有发送过电子邮件”。然后我是否构建 DAL 代码来构建仅检索业务层需要的 SQL 语句? IE。只是“SELECT id FROM employees WHERE ...”?

那么我应该为用户界面做什么?对于用户而言,最好包含更多信息,具体取决于为什么我要发送电子邮件。例如,我可能想包括一些基本的联系信息,或者他们工作的部门,或者他们的经理姓名等,而不是说我至少要显示姓名和电子邮件地址信息。

用户界面如何获取这些数据?我是否更改 DAL 以确保将足够的数据返回给 UI?我是否更改 BLL 以确保它为 UI 返回足够的数据?如果从 DAL 返回到 BLL 的对象或数据结构也可以发送到 UI,那么 BLL 可能不需要太多更改,但是 UI 的要求会影响层超出它应该与之通信的层.如果这两个世界在不同的数据结构上运行,则可能必须对两者进行更改。

然后,当 UI 发生变化时,为了进一步帮助用户,通过添加更多列,我必须/应该多深才能更改 UI? (假设数据已经存在于数据库中,因此不需要更改。)

提出的一个建议是使用 Linq-To-SQL 和 IQueryable,这样如果 DAL 处理什么(如在什么类型的数据中)和为什么(如在 WHERE 子句中)返回 IQueryables, BLL 可能会将这些返回到 UI,然后 UI 可以构造一个 Linq 查询来检索它需要的数据。然后,用户界面代码可以拉入它需要的列。这会起作用,因为使用 IQuerables,UI 最终会实际执行查询,然后它可以使用“select new { X, Y, Z }”来指定它需要什么,甚至在必要时加入其他表。

这对我来说看起来很乱。 UI 自己执行 SQL 代码,即使它隐藏在 Linq 前端之后。

但是,为了发生这种情况,不应该允许 BLL 或 DAL 关闭数据库连接,并且在 IoC 类型的世界中,DAL 服务可能会比 UI 代码想要的更快地被处理掉,因此 Linq 查询可能会以“无法访问已处置的对象”异常结束。

所以我正在寻找指针。我们离我们有多远?你是如何处理这个问题的?我认为 UI 的更改会通过 BLL 泄漏到 DAL 中这一事实是一个非常糟糕的解决方案,但现在看来我们无法做得更好。

请告诉我我们有多愚蠢并证明我错了?

请注意,这是一个遗留系统。多年来,更改数据库架构并不在范围内,因此使用 ORM 对象的解决方案基本上相当于“select *”,这并不是一个真正的选择。我们有一些大表,我们希望避免在整个层列表中拉起。

【问题讨论】:

    标签: c# data-access-layer business-logic isolation leaky-abstraction


    【解决方案1】:

    这根本不是一个容易解决的问题。我见过很多尝试(包括您描述的 IQueryable 方法),但没有一个是完美的。不幸的是,我们仍在等待完美的解决方案。在那之前,我们将不得不弥补缺陷。

    我完全同意 DAL 问题不应被允许泄漏到上层,因此绝缘 BLL 是必要的。

    即使您无法在当前项目中重新定义数据访问技术,从 Persistence Ignorance 的角度考虑域模型仍然会有所帮助。 Persistence Ignorance 的一个推论是每个域对象都是一个独立的单元,没有像数据库列这样的东西的概念。最好将数据完整性强制为此类对象中的不变量,但这也意味着实例化的域对象将加载其所有组成数据。这是一个非此即彼的命题,因此关键在于找到一个好的领域模型,以确保每个领域对象都保存(并且必须加载)“适当”数量的数据。

    太细粒度的对象可能会导致混乱的 DAL 接口,但太粗粒度的对象可能会导致加载太多不相关的数据。

    一个非常重要的练习是分析和正确建模领域模型的聚合,以便它们达到适当的平衡。这本书Domain-Driven Design 包含了一些关于建模聚合的非常有启发性的分析。

    在这方面可能有帮助的另一个策略是尽可能多地应用好莱坞原则。您描述的主要问题与查询有关,但如果您可以将重点转移到更面向命令的方向,您也许可以定义一些更粗粒度的接口,这些接口要求您始终加载数据太多。

    我不知道有任何简单的解决方案可以应对这一挑战。我上面描述的技术可以帮助您解决一些问题,但归根结底,它仍然是一门需要经验、技能和纪律的艺术。

    【讨论】:

      【解决方案2】:

      使用作为 UI 消费案例的视图模型(或数据传输对象)的概念。获取这些对象将是 BLL 的工作,如果数据不完整,则请求额外的数据(我们称之为模型)。然后 BLL 可以正确决定返回什么视图模型。不要让您的模型(数据)细节渗透到 UI 中。

      UI <-- (viewmodel) ---> BLL <-- (model) --> Peristence/Data layers
      

      这种解耦可以更好地扩展您的应用程序。我认为持久性独立性自然不属于这种方法,因为视图模型的构建和规范可以通过使用 linq2ql 或其他 orm 技术在 BLL 中灵活地完成。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2015-06-09
        • 2015-08-27
        • 1970-01-01
        • 2020-04-13
        • 1970-01-01
        • 2012-04-04
        • 1970-01-01
        相关资源
        最近更新 更多