使用ORM(DevExpress XPO,NHibernate或MS Entity Framework)升级数据库的最佳实践是什么?
我正在开始一个新项目,必须选择一个ORM。开发过程需要经常发布中间测试版本,并且每个版本可能会在数据库结构中进行更改。每个新版本都必须轻轻升级数据库以保留当前数据。
对于旧的解决方案,我将提供一组SQL脚本,用于将数据库从v1升级到v2,从v2升级到v3,依此类推,并依次执行它们。
但是,对于ORM来说它将如何工作?我是否仍应编写SQL脚本来升级数据库?
我知道简单地添加新字段不会引起问题(例如,请参见UpdateSchema() method for XPO),但是如果我必须拆分一个表并将当前记录重新分配到2个新表中怎么办?
最佳答案
我无法评论其他ORM,但自2007年以来,我已将DevExpress XPO用于公司的财务应用程序。架构在每个发行版中都会有所变化,但多年来也发生了一些重大的架构变化。默认XPO升级机制的某种扩展版本可以轻松应对所有更改。
关于升级XPO应用程序,有很好的基本信息here。
UpdateDatabaseBeforeSchemaUpdate()
和UpdateDatabaseAfterSchemaUpdate()
方法。您可以在其中严格控制升级过程。 如您所述,某些升级将由XPO自动处理(例如,添加新列),但有些事情需要其他控制,例如使用现有记录的默认值初始化新列。
例如,假设您的应用程序版本2.0中已将
MyNewField
添加到MyEntity
XPO类中。假设现有记录的默认值应为3。 XPO将处理新列的创建,但是现有记录将为NULL。 (如果在XPO类中指定默认值,则该值仅与新记录有关)。为了更正现有记录的值,您可以将以下内容添加到实体模块的UpdateDatabaseAfterSchemaUpdate()
中:public override void UpdateDatabaseAfterUpdateSchema()
{
base.UpdateDatabaseAfterUpdateSchema();
if (CurrentDBVersion < new Version(2, 0, 0, 0))
ObjectSpace.GetSession().ExecuteNonQuery(
"UPDATE [MyEntity] SET [MyNewField] = 3 WHERE [MyNewField] IS NULL");
}
(如果您希望避免使用直接SQL,也可以使用
ObjectSpace.GetObjects<MyEntity>()
和foreach
。)在将表一分为二的更极端的示例中,可以使用相同的方法,但是可以改写
UpdateDatabaseBeforeUpdateSchema()
,运行SQL来拆分表,让XPO执行其他任何模式更新,并在必要时填充任何默认值在UpdateDatabaseAfterUpdateSchema()
中。您会发现遇到约束问题,例如违反外键,因此您可能会发现需要编写一些常规例程,例如
DropAllForeignKeyConstraints()
作为UpdateDatabaseBeforeUpdateSchema()
的一部分。有时您会发现XPO已经提供了某些功能,有时则没有。缺少的约束和索引将在架构更新中重新生成。 (根据我的经验,切换主数据表的主键是最难完成的更新例程。)默认情况下,所有调用都在SQL事务中进行,因此,如果发生任何故障,则应全部回滚。
开发人员需要知道何时对域模型进行更改很可能导致基础架构出现问题。
为了进行测试,我们保留了一些旧的客户数据库,并在构建过程中进行了一系列前后测试,以确保现有客户能够正确升级他们要升级的版本。在生产中,只要遇到问题升级,就会将问题数据添加到此测试库中,以防止将来出现类似问题。
我们正在与大型国际公司和银行打交道。客户对结果非常满意。在企业的DBA需要签署更改的情况下,他们似乎并不介意拥有命令行工具来执行升级而不是脚本。