使用 CSLA DynamicRootList 创建主/明细 DGV - 第一部分





5.00/5 (4投票s)
本项目展示了如何使用 CSLA EditableRootListBase(或 DynamicRootList)作为主列表对象来创建主/明细 DataGridView。它展示了如何在主从 DataGridView 上实现列表排序和自动保存。

1. 引言
CSLA 是一个适用于 .NET Framework 2.0、3.0 和 3.5 的免费三层框架。CslaGen 是 CSLA 的代码生成器。关于 CSLA 和 CslaGen,请参阅如何使用 CslaGen 生成 CSLA 数据访问层代码。
这个项目诞生于我试图证明这是不可能完成的时候。嗯……事情总是这样发生的。:)
它展示了如何使用 CSLA DynamicRootList
或 EditableRootListBase
(简称 ERLB
)作为主列表对象来创建主/明细 DataGridView
(简称 DGV
)。如果您使用 ERLB
作为主列表,自动保存是标准功能。本项目还展示了如何在明细列表中实现自动保存。作为奖励,您还可以获得两个列表的排序功能。
本项目详细展示了一个 CslaGen 项目的示例,并讨论了所有主要问题。截至(2009 年 3 月),CslaGen 尚不支持 DynamicRootList
的生成。另外,作为一个附加功能,您可以查看将 CslaGen 对象类型 EditableRootCollection + EditableSwitchable 转换为正确的 CSLA DynamicRootList
+ DynamicRoot
所需的更改。
1.1. 技术细节
- CslaGen 没有版本号。我使用了一个稍作定制的版本(包括更改过的模板文件),您可以在这里下载。
- 解决方案在 *References* 文件夹下包含了 CSLA 3.0.5 对象代码。
- 该解决方案基于 .NET Framework 2.0 和 Visual Studio 2008 构建。
- 本项目使用了 Microsoft SQL Server 2005,但您也可以使用 Express 版本。
2. 背景
CSLA 框架有不同类型的对象,而 EditableRootListBase
(或 DynamicRootList
)是最后加入该框架的。它专门设计用于与 Windows Forms DGV
进行数据绑定。在 CSLA 中,对象遵循一些约定。我只指出与本项目相关的部分。
- 根对象是唯一可以独立加载和保存的对象类型。
- 子对象由其父对象(无论是根对象还是另一个子对象)加载和保存。
- 通常,子对象不引用其父对象(也不应该需要)。
ERLB
是一个例外,因为它是一个由根对象组成的根列表对象。- 您可以在常用术语表中找到更多详细信息。
EditableRootList
是一个对象列表。这些对象必须是子对象。假设您有一个员工列表,它是一个EditableRootList
。每个员工都是一个EditableChild
。一旦您编辑了员工数据(员工是一个子对象),要保存它,您必须保存整个员工列表(根)。这并不可行。通常的做法是有一个根只读列表,其中包含所有员工,您可以选择要编辑的员工作为根对象(一个独立的对象,不是根只读列表的子对象)。但这与DGV
的工作方式不同。因此,一种新型对象应运而生:EditableRootListBase
,一个根对象集合
ERLB
是一个根集合,它加载对象,但允许您单独保存每个对象,即集合中的每个对象都是一个根对象。这符合 DGV
的行为,即在您移动到另一行时会立即保存每一行。
2.1. 名称的含义?
我花了一些功夫才理解“不那么常用的术语”,但这个问题不像您想象的那么简单,因为它揭示了关于 ERLB
的重要信息。CSLA 模板现在包括 DynamicRootList
和 DynamicRoot
。如前所述,我们知道 DynamicRootList
就是 ERLB
的另一个名称。但 DynamicRoot
是什么?为什么我们不能使用 EditableRoot
模板?事实上,ERLB
使用根对象,但不是普通的 editable root 对象,因为它们的加载方式类似于 editable child 的加载方式。事实证明,CslaGen 对象类型 EditableSwitchable 可以同时作为 editable root 和 editable child 加载。正如我们稍后将看到的,使用 CslaGen 对象类型 EditableRootCollection + EditableSwitchable 是最接近 DynamicRootList
+ DynamicRoot
生成的方式,因为它只需要进行一些小的更改即可完成工作。
为了简化起见,从现在开始,我们将只使用 DynamicRootList
,并省略 EditableRootListBase
和 ERLB
的名称。同样,我们将只使用 DynamicRoot
,并省略 EditableRoot
。
3. 其他值得关注的点
大多数人会参考 ProjectTracker 来了解 CSLA 的使用示例。除了 DynamicRootList
的全面示例外,本文还提供了另一个使用示例来源。您可能会发现这些示例很有趣:
- 使用
SortedBindingList
对DataGridView
进行排序 - 使用新语法使用
AddBusinessRules
- 使用
System.Threading.Interlocked.Decrement
为新对象获取唯一的临时 ID - 乐观并发和
DataPortal
事件(CslaGen 中的标准功能) - 完整的 CslaGen 项目文档,讨论了所有不太明显的选项(请参阅附录以获取完整的屏幕截图集合)
4. 使用场景
对于这个示例项目,我选择了“品牌与型号”这个主题。每个品牌是几个型号的父级,并且没有型号是两个品牌的子级。不允许有两个同名的品牌。对于给定的品牌,不允许有两个同名的型号,尽管同一型号名称可以存在于不同的品牌中。请注意,名称匹配时不区分大小写。
由于我打算使其可重用,因此在 UI(用户界面)层面,名称都围绕着“主”和“明细”。在 BO(业务对象)层面,我使用实际的对象名称。为了重用它,您只需要将 BrandColl
、Brand
、ModelColl
和 Model
替换为您自己的对象名称。为了使其更具可重用性,您应该将其制作成组件。
5. 数据库
Warehouse 数据库有一个用于品牌的表,另一个用于型号的表。BrandID
是型号表的外键。

在 SQL Server 中,Price
列定义为 money
类型。对于 .NET Framework,使用的数据类型是 Decimal
。
CslaGen 会自动支持乐观并发,如果您包含一个 timestamp
数据类型的 SQL 列。尽管有这个名称,这种数据类型与时间无关,实际上是一个版本计数器,从零开始,并且在每次行更新时递增。这就是为什么我更喜欢称之为 RowVersion
。乐观并发会检查当前行版本是否与对象获取时行的版本匹配,如果不匹配,则拒绝保存,并警告您该对象已被更改。这意味着其他用户已更改或删除了它。
在 SQL Server 中运行脚本来创建数据库和表。同时,也请运行脚本来创建存储过程。项目文件 *CslaERLB1.zip* 包含所有脚本。您将使用编译后的源代码自行填充数据库。
6. 对象
尽管 CslaGen 尚不支持 DynamicRootList
,但 CslaGen 文件(*Warehouse.xml*)已包含在 *CslaERLB1.zip* 中。我使用了 CslaGen 对象类型 EditableRootCollection 和 EditableSwitchable。然后我修改了生成的文件,并将它们转换成了一组正确的 DynamicRootList
+ DynamicRoot
对象(CSLA 模板命名)。

BrandColl
是 Brand
对象的根集合。它是一个 DynamicRootList
,一个由 DynamicRoot
对象组成的集合。因此,Brand
也是一个根对象。Brand
对象是 ModelColl
集合的父级,而 ModelColl
是 Model
对象的集合。
注释
- 嵌套类型是对象的条件。
static
字段_lastID
用作临时 ID 的种子,用于在新对象提交到数据库并获取实际 ID(由数据库提供)之前。AddBusinessRules
方法负责将验证规则添加到对象中。CslaGen 对验证规则有一定的支持。但我更喜欢自己处理。在这种情况下,它使用了新语法(自 CSLA 2.1 起)。NoDuplicates
方法是一个验证规则,根据 4. Use cases 中所述的规则,检查名称是否已存在于集合中。- 为了使用
DGV
,每个集合都必须有一个AddNewCore
方法。
本文的其他部分
历史
- 文档版本 1:2009 年 3 月 12 日
- 代码版本 1.1: 2009 年 3 月 14 日 - 错误修正