欢迎投稿

今日深度:

论数据库项目的代码组织,EntityFram

论数据库项目的代码组织,EntityFram


数据库项目的代码组织大致有两种形式:增量式与快照式。

Entity Framework (Core)的数据库迁移工程就是经典的增量式组织形式:有专门的迁移历史表标识增量版本,不同版本之间的升级、降级由相应的脚本专职负责。在升/降级脚本的编撰过程中,有经验的数据库开发工程师会采取各种措施,来实现数据无损发布。

快照式组织形式的典型是微软的SQL Server工程(即.sqlproj工程):工程中为每个对象列出最终脚本,按Object Type方式分门别类放置于不同的目录下;工程编译后得到一个DACPAC文件表达数据库的期望架构,利用SqlPackage.exe工具,与目标数据库比较后自动生成迁移脚本。

两种组织形式各有其优缺点:

  • 快照式的代码组织结构简洁明了,对象定位迅速、方便;但SqlPackage.exe工具比较傻,即使选择幂等方式其自动生成的迁移脚本仍有数据损失的风险,必须由有经验的数据库开发人员把关,通常得进行适当的调整才敷使用。
  • 增量式的发布没有数据损失风险,可以很方便地通过连续集成平台实现自动发布;但对象代码组织凌乱不易定位,对象的更改历史不清晰明确,对开发极不友好。

必须指出,对象代码组织凌乱不易定位是很严重的问题。每当对某个已经存在的对象进行调整时,开发人员必须向上追溯到最近的一个版本,在此基础上进行更改——这是一个很容易产生错误的环节,比如追溯到的不是最近的版本而是上上一个版本,比如多人协同时有人有不良习惯,屡屡更改其已提交的增量脚本而未通知其他开发人员,等等。

而对象的更改历史不清晰明确也是很严重的缺点。首先如前所叙,将涉及到同一个对象的增量脚本找出来本身即是费力的工作;其次,通常地一个增量脚本可能包含多个对象的更改,使用代码比较工具时会引入大量噪声不利分析。

笔者在数个项目中需要开发达数千行代码的存储过程,对此深有体会。

那么这两种组织形式在实践中该如何选择呢?个人的体会是,纯粹地追求快照式或增量式都不是好的选择,应该混合起来用:

  1. 每个对象在工程中有一个最终脚本,留存其在当前版本中的快照;
  2. 工程中专门设立一个增量脚本目录,放置对表结构、索引、系统数据的更改脚本;
  3. 由专门的工具实施数据库迁移,在迁移过程中首先(根据历史版本号)决定必须执行的增量脚本,执行之并更新历史版本号,然后遍历快照脚本区,将其中的编程对象,按函数、视图、存储过程、触发器的次序依次执行其脚本。

其中第二点、第三点容易产生疑问:为什么不对编程对象产生增量脚本?首先这些编程对象的更改天然就不表现为增量形式,不象表结构、系统数据的更改那样只提及发生变化的部分,而是在ALTER命令中以完全体的形式出现,硬加入增量脚本会大幅增加其尺寸并分散开发人员注意力;其次,如视图这种编程对象,当底层表结构发生变化后通常必须在数据库里重新声明一次;再次,(由于开发人员的失误)编程对象中可能含有不适应增量脚本变更的代码段,因为SQL Server的延迟解析特性,不重新声明一次不会报告此类错误。

索引的更改其实也表现为完全体形式:同名的索引要进行调整只能先DROP后CREATE。放在增量脚本中加以体现,是出于以下考虑:

  1. 因为创建索引是非常消耗资源的行为,尤其当目标表含有大量数据且不是采用着昂贵的企业版SQL Server(有在线索引创建支持)时,应尽量避免重建索引的操作;
  2. 索引有大量元数据,如何确认索引是否发生了变化需要很繁琐的代码。

书写增量脚本时,目标索引已经很明确需要更改了,上述两点便不再成为问题。

在这样一种混合代码组织中,首先建议编程对象的书写遵从一定规范,比如这样

IF (OBJECT_ID(N'dbo.MyProc', N'P') IS NULL)
BEGIN;
    EXEC sys.sp_executesql N'CREATE PROC dbo.MyProc AS RETURN;';
END;
GO

ALTER PROC dbo.MyProc
AS
BEGIN;
    SET NOCOUNT ON;

    /* The logic code here */
END;
GO

可以避免编程对象的元数据,如create_date发生变化;其次,要废弃的编程对象其脚本文件必须保留,其内容是检查该对象是否存在,若存在则需删除,即

IF (OBJECT_ID(N'dbo.MyProc', N'P') IS NOT NULL)
BEGIN;
    DROP PROC dbo.MyProc;
END;

采用这种代码组织形式的缺点是,需要自己开发相应的迁移工具进行部署。但这个开发工作量并不大,而且其他数据库项目也可以使用这个工具,只要其目录结构满足事先约定。我司2006年借鉴Jesse Hersch (Elsasoft LLC)的思路利用SMO(SQL Server Object Managerment)库开发的迁移工具延用至今,满足了大量项目的需求。

当然时至今日此工具表现出了一些缺点,最大的缺点是依赖的SMO库有32位和64位两种不兼容版本,而且似乎微软也不再进行升级。其实迁移工具中只使用了SMO中的一样功能,执行包含GO命令的T-SQL脚本,所以很容易摆脱对其依赖:一种方法是取GO加回车为分隔符,将T-SQL脚本拆分成几段依次调用SqlCommand执行;另一种是调用System.Diagnositics.Process,使用-i -I开关【1】执行SqlCmd命令运行T-SQL脚本【2】。这两种方式用PowerShell、C#脚本都可以很方便地实现。

 

【1】SqlCmd命令默认关闭引用标识符(Quoted Identifier),所以需要用开关-I显式打开。

【2】这一方式要求分发服务器上装有SqlCmd工具。

 

www.htsjk.Com true http://www.htsjk.com/Sql_Server/45112.html NewsArticle 论数据库项目的代码组织,EntityFram 数据库项目的代码组织大致有两种形式:增量式与快照式。 Entity Framework (Core)的数据库迁移工程就是经典的增量式组织形式:有专门的迁移历史表标...
评论暂时关闭