随着软件开发的复杂性增加,版本控制已经成为项目管理中不可或缺的一部分。团队目前使用的是SubVersion进行源代码管理,它在分支和合并方面的表现令人满意,极大地帮助了部署流程。然而,面临的挑战是,应用程序以数据库为中心,大量的业务逻辑存储在表、视图和存储过程中,这些并没有纳入源代码控制,更重要的是,没有变更历史记录。
为了解决这个问题,提出了两种解决方案。第一种是购买Microsoft Visual Studio 2008 Database Edition,它提供了一个强大的环境来处理数据库变更,类似于C#项目,并具有数据库逆向工程、架构和数据比较等许多功能。但这个解决方案的问题在于,它与Team Foundation Server(TFS)紧密绑定,而TFS并不是首选。此外,开发人员需要改变他们与数据库的交互方式。使用VSDE,他们需要创建脚本来修改数据库,然后对一个本地临时数据库执行这些脚本。虽然这在看来是最佳实践,但对于一些开发人员来说可能是个问题。
第二种解决方案是定期将数据库对象脚本化,并提交这些脚本到SubVersion。这种方法可以在不改变处理数据库方式的情况下,为提供数据库变更的历史记录。这个过程可以是自动化的,并且可以设置在服务器机器上,使用计划任务,每30分钟运行一次。如果想要更细粒度的历史记录,应该设置计划任务每15分钟或更短时间运行一次。需要注意的是,如果数据库没有变化,这个过程不会提交任何内容。
        本教程将解释如何创建这个自动化过程。在开始研究这个问题时,寻找了一个命令行工具,它能够脚本化所有数据库对象。发现了很多不错的开源项目,它们以不同的方式做到了这一点:
        
        和
        
        。不幸的是,对于大型项目来说,每次都脚本化整个数据库是非常慢的,因此尝试创建一个自定义的控制台应用程序,使用
        Microsoft.SqlServer.Management.Smo
        只脚本当前运行和上一次运行之间的差异。
    
        控制台应用程序分为两个类:
        Program.cs
        :应用程序的入口点,处理输入参数并调用辅助类来脚本化数据库对象。
        ScripterHelper.cs
        :辅助类使用
        Microsoft.SqlServer.Management.Smo
        来脚本化数据库对象。
        辅助类将上次修改的对象日期存储在一个名为
        
        通过一个简单的批处理文件,可以轻松地自动提交SubVersion中的更改。
        最有趣的代码部分是如何使用
        Microsoft.SqlServer.Management.Smo
        来脚本化数据库对象。
        执行此操作的类是
        Scripter
        :
    
        Server server = new Server("(local)");
        Database db = server.Databases["AdventureWorks"];
        Scripter scripter = new Scripter();
        scripter.Server = server;
        scripter.Options.IncludeHeaders = true;
        scripter.Options.SchemaQualify = true;
        scripter.Options.SchemaQualifyForeignKeysReferences = true;
        scripter.Options.NoCollation = true;
        scripter.Options.DriAllConstraints = true;
        scripter.Options.DriAll = true;
        scripter.Options.DriAllKeys = true;
        scripter.Options.DriIndexes = true;
        scripter.Options.ClusteredIndexes = true;
        scripter.Options.NonClusteredIndexes = true;
        scripter.Options.ToFileOnly = true;
        foreach(Table table in db.Tables)
        {
            if(!table.IsSystemObject)
            {
                scripter.Options.FileName = Path.Combine("C:\\Scripts", table.Name + ".sql");
                scripter.Script(new Urn[] { table.Urn });
            }
        }
与SubVersion的集成是通过一个小型批处理文件完成的:
        rem Script the database objects AdventureWorks under the local SQL Server into the folder C:\Scripts
        SQLScripter.exe (local) AdventureWorks "C:\Scripts"
        rem Change current Folder
        C:\Scripts
        rem Use for command to detect the file status and eventually add or delete the files from svn
        for /f "tokens=2*" %%i in ('svn status ^| find "?"') do svn add %%i
        for /f "tokens=2*" %%i in ('svn status ^| find "!"') do svn delete %%i
        rem Commit the changes in svn
        svn commit -m "Automatic commit" "C:\Scripts"
控制台应用程序接受以下参数: 服务器(要脚本化的数据库的主机) 数据库名称(要脚本化的数据库的名称) 脚本路径(应用程序将存储脚本的路径) 对象类型(过滤参数,用于限制将被脚本化的对象类型:t=表,v=视图,s=存储过程,a=用户定义的聚合,f=用户定义的函数,空=全部)