向导和 CRUD 应用程序,用于构建其他 CRUD 应用程序






4.65/5 (41投票s)
探讨使用 CRUD 应用程序在 Web 浏览器中管理其他 CRUD 应用程序的可能性,而无需任何手动编码。此外,还介绍了一种从 UI 而非相反的方向推导数据库结构的步骤向导。
引言
在研究了 CRUD 应用程序的极简元模型 和一些 即时代码生成 的示例后,现在让我们看看如何更轻松地构建和管理 CRUD(创建、读取、更新、删除)应用程序。
在本文中,我们将探讨当我们决定放弃 XML 并直接将元数据保存在数据库中时,所带来的新可能性。这样,我们就可以将 Evolutility(“即时代码生成器”)用作 CRUD 应用程序来管理其他 CRUD 应用程序的元数据(就像我们在“待办事项列表”或“地址簿”的先前示例中用于管理数据一样)。基本上,应用程序本身就成为了应用程序设计者。
我们还将介绍一个步骤向导,通过从 UI 推导数据库结构来构建应用程序,而不是采用更常用的相反方式。
将元数据从 XML 移至数据库
当然,XML 是“人类可读的”,但即使有了 XSD,它也可能并非总是“人类可写”,尤其是在处理元数据时。要摆脱 XML,我们可以构建一个读取和持久化 XML 的设计器,或者我们可以将元数据移至数据库并使用新的 CRUD 应用程序进行管理。后一种选择更有趣,需要的代码也更少。
使用我们简单的元模型,从 XML 到数据库并不难
- 我们可以从为每个 XML 元素使用一个数据库表,为每个 XML 属性使用一个数据库列开始。在元模型中有 4 个元素:
form
、data
、panel
和field
。这些可以轻松地转换为 4 个数据库表。 - 由于
form
和data
之间的关系是 1 对 1,因此我们可以通过将它们合并到同一个表中来规范化模式。 - 我们还添加一个表来存储必要的枚举值(此处为字段类型)。
- 按照惯例,我们以“EvoDico_”作为表名前缀,以指示表的用途。
- 最后(模式中未显示),当删除表单时,我们添加一个触发器来自动删除该表单的字段和面板。
现在,我们剩下以下数据库模式来存储我们的应用程序元数据
为简单起见,这组表可以与应用程序数据位于同一个数据库中。在实际应用中,由于元数据更改的频率不如数据,因此将它们保存在单独的数据库中可能更有利,这样可以更轻松地独立备份和恢复它们。
用于构建它的 SQL 脚本包含在本文随附的演示项目中。
用于管理其他 CRUD 应用程序的 CRUD 应用程序
使用上述模式,我们现在可以构建 CRUD 应用程序来管理我们其他应用程序的元数据。
我们可以摆脱实现模型(XML)并更贴近用户的心理模型,将 UI 结构与数据库结构解耦。通过仔细组织字段到面板和选项卡,我们可以例如将 UI 元数据与数据库元数据分开。此外,一些用户友好的字段标签将比我们之前的 XML 属性更有意义。
我们可以显示如下的应用程序摘要
我们将有不同的 CRUD 应用程序来钻取字段
这些 CRUD 页面现在是其他 CRUD 应用程序的“设计器”。
保持设计器触手可及
我们还可以通过允许用户在运行时编辑特定 UI 元素的元数据来模糊应用程序和设计器之间的界限。这是通过在应用程序的每个 UI 元素附近添加一个小图标来实现的。这些图标(按元素类型着色)会弹出前面段落中显示的相应设计器页面(或一些等效的自定义页面,其中元数据以略有不同的方式呈现)。
创建 CRUD 应用程序的步骤向导
现在让我们来看一种更简单的方法:一个步骤向导。为了更贴近用户的心理模型,我们可以在向导 UI 中完全省略所有对数据库的引用。在内部,我们可以从 UI 元数据推导数据库结构(而不是相反)。
为了收集构建 CRUD 应用程序所需的最低 UI 元数据(这里使用与前 2 篇文章相同的“待办事项列表”示例),我们将有以下步骤
步骤 1 - 应用程序定义:收集应用程序名称,以及用户称呼的实体的单数和复数形式。
步骤 2 - 数据定义:收集字段列表(标签和类型)。
步骤 3 - 字段详细定义:指定每个字段所需的其他信息。字段属性取决于在上一步中选择的字段类型。
步骤 4 - 搜索选项:决定哪些字段包含在搜索、高级搜索和列表结果中。
步骤 5 - 面板布局:收集用于在“视图”和“编辑”模式下将字段分组的面板列表。还根据“流定位”方案指定每个面板的相对宽度。
步骤 6 - 字段布局:决定哪些字段属于哪个面板(在上一步中指定),以及每个字段在其面板内的相对宽度。
步骤 7 - CRUD 应用程序已准备就绪:此步骤显示了应用程序所需的 XML 和 SQL。实际上,这些可以隐藏起来,因为大多数用户可能不知道如何处理它们,但开发人员会觉得它们很有用。此页面还包含可立即自定义应用程序的链接。
尽管没有明确引用数据库,但该向导可以自动构建所有必要的表并使用种子数据填充它们(您可能需要稍后进行调整以进行优化)。诀窍在于,由于 CRUD 的范围足够有限,因此只有一个驱动表,我们可以使用字段标签作为列名,在去除逗号和特殊字符,并将空格替换为下划线后。我们使用的是“UI 字段类型”,而不是我们在元模型文章中讨论的实际数据类型。对于“枚举值”,我们已经知道了模式:驱动表中的整数值用于存储辅助表的主键……
由于向导在 Web 上运行,并且应用程序不需要编译,因此在最后一个步骤之后,新应用程序即可立即使用。
使用代码
要运行演示项目,请按照以下步骤操作
- 将包含网站的 EvoDico 目录复制到您的 Web 服务器。
- 创建一个新的 SQL Server 数据库。
- 在 Web.config 文件(或任何 ASPX 页面)的
appSettings
部分更改数据库连接字符串。 - 按照以下顺序在您的数据库上运行 SQL 目录中的 SQL 脚本
- evodico.2.2.sql
- evodico-seed.2.2.sql
- evodico-sample.2.2.sql
- 运行 SQL 脚本以创建示例应用程序的表(位于 SQL/Samples/ 目录中)。
以最小的努力从头开始构建 CRUD 应用程序
- 从运行向导开始
- 尝试您的新应用程序
- 自定义您的新应用程序
- 也许将其迁移到 XML 以获得更大的灵活性(因为数据库存储库尚未支持 XML 的所有功能)
- 如有必要,手动调整您的数据库
玩火
最后,我们可以设想使用触发器在修改元数据时自动修改数据库结构。这相当危险(尤其当我们可能有几个应用程序运行在同一组表上时),但它可能是进一步自动化流程的方式。
为此目的的一个可能的触发器可以是这样的
CREATE TRIGGER [EvoDico_Field_TR_Add] ON [EvoDico_Field]
FOR INSERT
AS
DECLARE @dbtable nVARCHAR(100), @dbcolumn nVARCHAR(100),
@maxlength INT,@typeID INT , @TypeSQL nvarchar(20)
SELECT @dbcolumn=dbcolumn, @maxlength=INSERTED.maxlength, @dbtable=dbtable,
@TypeSQL=EvoDico_FieldType.sqlname
FROM INSERTED, EvoDico_Form, EvoDico_FieldType (nolock)
WHERE INSERTED.formID=EvoDico_Form.id AND EvoDico_FieldType.ID=INSERTED.TypeID
select @typeID=id from sysobjects where name=@dbtable and xtype='u'
if @typeID>0
begin
if not exists (select id from syscolumns where name = @dbcolumn and id=@typeID)
if @TypeSQL='nvarchar'
exec('ALTER TABLE ['+@dbtable+'] ADD ['+@dbcolumn+'] nvarchar(
'+@maxlength+') NULL')
else
exec('ALTER TABLE ['+@dbtable+'] ADD ['+@dbcolumn+'] '+@TypeSQL+' NULL')
end
呼吁为初创的新开源项目做出贡献
这是一个正在进行中的项目。代码生成器是稳定的,但向导和设计器(用于管理其他应用程序的应用程序)仍需要一些工作。
本文附带的代码也可在 SourceForge 上获得并更新,遵循 Affero GPL v3 并提供双重许可,项目网站是 www.evolutility.org。在您的空闲时间,请随时改进此代码并做出贡献。谢谢。