更新:2007 年 11 月
可以使用 Visual Studio 中的扩展性支持来以编程方式修改数据库项目。Visual Studio Team System Database Edition 中的数据库项目按照与 Visual C# 和 Visual Basic 项目一致的方式支持 Visual Studio 自动化模型(又称作设计时扩展性或 DTE)。有关此模型的更多信息,请参见扩展 Visual Studio 环境。在本演练中,将创建 Visual Studio 宏,这些宏使用自动化模型来完成以下两项任务:
切换数据库项目中所有触发器的生成操作。如果触发器设置为“生成”,则该宏会将触发器更改为“不在生成中”。如果触发器设置为“不在生成中”,则该宏会将触发器更改为“生成”。
将某个目录中的所有脚本文件添加到数据库项目中的一个文件夹中。如果该文件夹不存在,将创建它。将只添加具有特定扩展名的脚本文件。
这些任务也可以在使用 Visual C# 或 Visual Basic 编写的 Visual Studio 外接程序中执行。为简单起见,本演练使用宏。
在下列过程中,您将执行如下操作:
创建一个按对象类型组织的数据库项目,并导入 AdventureWorks 数据库架构。
启动“宏资源管理器”并创建一些包含宏和支持代码的模块。
创建一个宏,用来切换已打开解决方案内数据库项目中所有触发器的生成操作。
创建一个用来向数据库项目中添加脚本的宏和支持代码。
从“命令窗口”运行 ToggleTriggers 宏。
从“宏资源管理器”运行 AddScriptsInDirectory 宏。
先决条件
为了完成本演练,计算机上必须装有 Database Edition。本演练假设您已经在运行 Microsoft SQL Server 2005 的数据库服务器上安装了 AdventureWorks 示例数据库的副本。您可以替换按对象类型组织的任何其他数据库项目。在您具有访问权限的目录中,必须有一个或多个具有 .sql 扩展名的文件。
创建数据库项目
启动 Visual Studio(如果尚未启动的话)。
在“文件”菜单上,指向“新建”,然后单击“项目”。
将出现“新建项目”对话框。
在“项目类型”列表中,展开“数据库项目”节点,然后单击“Microsoft SQL Server”。
在“模板”列表中,单击“SQL Server 2005”。
在“名称”中键入“MyAdvWorks”,并接受“位置”和“解决方案名称”的默认值。
选中“创建解决方案的目录”复选框(如果默认情况下未选中),然后单击“确定”。
随即会创建一个包含 MyAdvWorks 空数据库项目的解决方案。
接下来启动“导入数据库架构”进程,在该进程中,指定到源数据库的连接字符串。
从现有 AdventureWorks 数据库中导入数据库架构
在“视图”菜单上单击“架构视图”。
如果“架构视图”尚未显示,此时将显示该视图。
在“架构视图”中单击“MyAdvWorks”。
在“项目”菜单上,单击“导入数据库架构”。
说明:
还可以右击“MyAdvWorks”并单击“导入数据库架构”。
将出现“导入数据库向导”。
在“源数据库连接”列表中,单击与现有 AdventureWorks 数据库相对应的连接。如果尚未连接到该数据库,则必须先创建与该数据库的连接。有关更多信息,请参见如何:创建数据库连接。
单击“完成”。
导入架构之后,对应于数据库中的对象的项目项显示在“解决方案资源管理器”中的数据库项目之下。“架构视图”显示在数据库项目中定义的对象。
启动宏资源管理器并创建模块
在“视图”菜单上指向“其他窗口”,再单击“宏资源管理器”。
将出现“宏资源管理器”。
在“宏资源管理器”中右击“MyMacros”节点,再单击“新建模块”。
将出现“添加模块”对话框。
在“名称”中,键入“BuildActionExample”。
单击“添加”。
在“宏资源管理器”中右击“MyMacros”节点,再单击“新建模块”。
将出现“添加模块”对话框。
在“名称”中,键入“ImportScriptsExample”。
单击“添加”。
接着,将创建一个宏,用来切换指定数据库中所有数据操作语言 (DML) 触发器的生成操作。
创建 ToggleTriggers 宏
ToggleTriggers 宏采用一个可选参数,该可选参数是包含要更新的触发器的数据库项目的名称。如果您未指定项目名称,该宏将提示您指定一个。您可以修改该宏,使其改为标识解决方案中每个项目的类型并更新所有的数据库项目。不过,该方法超出了本演练的范围。
创建 ToggleTriggers 宏
在“宏资源管理器”中右击 BuildActionExample 模块,再单击“编辑”。
将出现“Microsoft Visual Studio Macros”窗口。此窗口显示 BuildActionExample 模块的内容。
用下面的 VBScript 代码替换该模块的内容:
Imports System Imports System.ComponentModel Imports EnvDTE Imports EnvDTE80 Imports System.Diagnostics Public Module BuildActionExample ' Macro to toggle the BuildAction for all DML triggers ' in a database project. ' Before running this macro, you must: ' 1) Ensure that a solution file is open and saved to disk. ' 2) Pass in the name of a database project contained in the ' open solution in the dbProjName parameter. Sub ToggleTriggers(Optional ByVal dbProjName As String = "") Dim project As Project ' if the database project name was not passed in, prompt the user for it. If (String.IsNullOrEmpty(dbProjName)) Then dbProjName = InputBox("Type the database project name.") If (String.IsNullOrEmpty(dbProjName)) Then Return End If End If ' Loop through each project until we find the one we want For Each project In DTE.Solution Dim projectItem As EnvDTE.ProjectItem 'Look for a project whose name matches the parameter If (dbProjName.Equals(project.Name)) Then 'Then loop through the project items, looking for 'the Schema Objects folder. For Each projectItem In project.ProjectItems() If (projectItem.Name = "Schema Objects") Then ' loop through the subfolders and list the files, looking for the Tables sub-folder Dim subItem As EnvDTE.ProjectItem For Each subItem In projectItem.ProjectItems() If (subItem.Name = "Tables") Then ' loop through looking for the Triggers subfolder Dim subsubItem As EnvDTE.ProjectItem For Each subsubItem In subItem.ProjectItems() If (subsubItem.Name = "Triggers") Then ' okay, we're in the right folder, now set the build actions Dim triggerItem As EnvDTE.ProjectItem For Each triggerItem In subsubItem.ProjectItems() 'MsgBox(" trigger: " + triggerItem.Name) Dim buildAction As EnvDTE.Property buildAction = triggerItem.Properties.Item("DBProjectBuildAction") ' here we toggle the build action. If it was NotInBuild(0), ' we set it to Build(1). If it was Build(1), then we set it ' to NotInBuild(0). If (buildAction.Value = 0) Then buildAction.Value = 1 ElseIf (buildAction.Value = 1) Then buildAction.Value = 0 End If Next End If Next End If Next End If Next End If Next End Sub End Module
该宏会循环访问解决方案的内容,直到它找到名称与指定名称相匹配的数据库项目。在该宏标识此项目之后,它会循环访问项目项以查找“解决方案项”文件夹。在“解决方案项”文件夹中,该宏会查找“表”文件夹并在“表”文件夹中查找“触发器”文件夹。该宏随后会检索每个触发器的“DBProjectBuildAction”属性值。如果该值为 1(“生成”),则它会切换到 0(“不在生成中”)。同样,如果该值为 0,则它会切换到 1。尽管“属性”窗口中的属性名称为“生成操作”,但是基础属性名为“DBProjectBuildAction”。
在宏窗口中打开“文件”菜单,再单击“保存 <我的宏>”。
接着,将创建一个用来将某个目录中的脚本文件添加到指定数据库项目的宏。
创建 AddScriptsInDirectory 宏
AddScriptsInDirectory 宏采用三个参数:要向其中添加脚本文件的数据库项目的名称;该数据库项目中要向其中添加脚本的文件夹的名称;包含该宏将导入的脚本文件的路径。如果您在运行该宏时未指定这些参数,该宏将提示您指定它们。如果您没有根据该提示指定项目文件夹的名称,这些文件将添加到“脚本”文件夹中。
该宏比前一个宏复杂得多。为简单起见,将通过创建下面的两个函数和两个子例程来生成 AddScriptsInDirectory 宏:
Function IsFileIncluded - 此函数验证指定文件名的扩展名是否位于应当被视为脚本且应当添加到数据库项目中的文件的扩展名列表中。
Function GetOutputWindowPane - 此函数返回输出窗口,以便能够报告进度消息。
Sub AddScriptsInDirectory2 - 这个子例程采用一个项目文件夹和一个路径,并添加扩展名与脚本扩展名列表相匹配的所有文件。
Sub AddScriptsInDirectory - 这个子例程是该宏的入口例程,用来处理传递给它的参数、执行验证、创建目标文件夹或者在数据库项目中查找目标文件夹(如果目标文件夹已经存在的话)。该子例程随后会调用 AddScriptsInDirectory2 以便向该文件夹中添加文件。
创建 AddScriptsInDirectory 宏
在“宏资源管理器”中右击 ImportScriptsExample 模块,再单击“编辑”。
将出现“Microsoft Visual Studio Macros”窗口。此窗口显示 ImportScriptsExample 模块的内容。
用下面的 VBScript 代码替换该模块的内容:
Imports System Imports EnvDTE Imports EnvDTE80 Imports System.Diagnostics Public Module ImportScriptsExample ' A list of folder names, file names, and extensions that we want to add ' to the solution. Dim outputWindowPaneTitle As String = "Add scripts to a project folder report" Dim includedExtensions As New System.Collections.Specialized.StringCollection ' Function to filter out folder names, file names, and extensions that we do not ' want to add to the solution. Function IsFileIncluded(ByVal filePath As String) As Boolean Dim extension As String Dim fileName As String extension = System.IO.Path.GetExtension(filePath) extension = extension.ToLower() fileName = System.IO.Path.GetFileName(filePath) fileName = fileName.ToLower() If (includedExtensions.Contains(extension)) Then Return True Else If (includedExtensions.Contains(fileName)) Then Return True Else Return False End If End If End Function ' This function retrieves the output window pane Function GetOutputWindowPane(ByVal Name As String, Optional ByVal show As Boolean = True) As OutputWindowPane Dim window As Window Dim outputWindow As OutputWindow Dim outputWindowPane As OutputWindowPane window = DTE.Windows.Item(EnvDTE.Constants.vsWindowKindOutput) If show Then window.Visible = True outputWindow = window.Object Try outputWindowPane = outputWindow.OutputWindowPanes.Item(Name) Catch e As System.Exception outputWindowPane = outputWindow.OutputWindowPanes.Add(Name) End Try outputWindowPane.Activate() Return outputWindowPane End Function ' Given a folder within the solution and a folder on disk, add all files whose extensions ' are on a list of "good" extensions to the folder in the solution. Sub AddScriptsInDirectory2(ByVal newScriptFolder As ProjectItem, ByVal startFolder As String) Dim files As String() Dim file As String Dim folder As String ' get a list of files in the specified folder files = System.IO.Directory.GetFiles(startFolder) ' get the output window pane so we can report status Dim outputWindowPane As EnvDTE.OutputWindowPane outputWindowPane = GetOutputWindowPane(outputWindowPaneTitle, True) ' Examine all the files within the folder. For Each file In files ' if this file's extension is one we want to include... If (IsFileIncluded(file)) Then ' try to add it to the folder Dim projItem As ProjectItem Try projItem = newScriptFolder.ProjectItems().AddFromFile(file) outputWindowPane.OutputString("The item """ + file + """ was added" + vbLf) If (Not (projItem Is Nothing)) Then If (Not (projItem.Document Is Nothing)) Then projItem.Document.Close(vsSaveChanges.vsSaveChangesNo) End If End If Catch ' if an error occurs, report the failure outputWindowPane.OutputString("The item """ + file + """may have not been added to the solution." + vbLf) End Try End If Next End Sub ' creates a new subfolder within the Scripts folder in the specified database project ' then adds all files in the specified path to the newly created scripts sub-folder. Sub AddScriptsInDirectory(Optional ByVal dbProjName As String = "", Optional ByVal scriptFolderName As String = "", Optional ByVal startFolder As String = "") If (String.IsNullOrEmpty(dbProjName)) Then dbProjName = InputBox("Type the name of the database project to which you want the scripts to be imported.") If (String.IsNullOrEmpty(dbProjName)) Then Return End If End If If (String.IsNullOrEmpty(scriptFolderName)) Then scriptFolderName = InputBox("Type the script folder name.") If (String.IsNullOrEmpty(scriptFolderName)) Then scriptFolderName = "Scripts" End If End If If (String.IsNullOrEmpty(startFolder)) Then startFolder = InputBox("Type the folder path to import.") If (String.IsNullOrEmpty(startFolder)) Then Return End If End If If (System.IO.Directory.Exists(startFolder) = False) Then MsgBox("The specified folder could not be found.") Return End If GetOutputWindowPane(outputWindowPaneTitle, True).Clear() If System.IO.Directory.Exists(startFolder) = False Then Dim outputWindowPane As EnvDTE.OutputWindowPane outputWindowPane = GetOutputWindowPane(outputWindowPaneTitle, True) outputWindowPane.OutputString("The path entered could not be found" + vbLf) Exit Sub End If includedExtensions = New System.Collections.Specialized.StringCollection ' If you do not want a file with a particular extension or name ' to be added, then add that extension or name to this list: includedExtensions.Add(".sql") includedExtensions.Add(".tsql") Dim newScriptFolder As ProjectItem Dim project As Project ' now check to see if the desired folder in the project already exists For Each project In DTE.Solution Dim projectItem As EnvDTE.ProjectItem If (dbProjName.Equals(project.Name)) Then Dim found As Boolean found = False For Each projectItem In project.ProjectItems() If (scriptFolderName.Equals(projectItem.Name)) Then ' the desired folder already exists, save the projectItem that corresponds ' to the folder. found = True newScriptFolder = projectItem End If Next ' if the folder does not exist within the project, create it. If (Not found) Then ' the folder does not already exist, so create it newScriptFolder = project.ProjectItems().AddFolder(scriptFolderName, EnvDTE.Constants.vsProjectItemKindPhysicalFolder) End If End If Next ' now add the scripts in the folder to the project folder AddScriptsInDirectory2(newScriptFolder, startFolder) End Sub End Module
在宏窗口中打开“文件”菜单,再单击“保存 <我的宏>”。
在“文件”菜单上单击“关闭并返回”。
接着,将运行您的宏以演示结果。
运行 ToggleTriggers 宏
如果您运行宏时所针对的解决方案不是在本演练中创建的任何解决方案,则必须指定您的解决方案中所包含的数据库项目的名称,而不是指定 MyAdvWorks。
从“命令”窗口运行 ToggleTriggers 宏
在“解决方案资源管理器”中展开 MyAdvWorks 数据库项目。
展开“架构对象”文件夹。
展开“表”文件夹。
展开“触发器”文件夹。
在“解决方案资源管理器”中右击任何触发器,再单击“属性”。
记下所选触发器的“生成操作”属性值。
在“视图”菜单上指向“其他窗口”,再单击“命令窗口”。
将出现“命令窗口”。
在“命令”窗口中,键入以下内容:
Macros.MyMacros.BuildActionExample.ToggleTriggers MyAdvWorks
MyAdvWorks 是指要切换其触发器“生成操作”属性的数据库项目的名称。
等待此宏完成。
当该宏完成运行后,查看步骤 5 中的触发器属性。
此时,“生成操作”属性值与运行该宏之前的属性值相反。
您可以再次运行该宏,以便将“生成操作”属性值还原到其初始状态。
接着,将从“宏资源管理器”运行 AddScriptsInDirectory 宏。
运行 AddScriptsInDirectory 宏
如果您运行宏时所针对的解决方案不是在本演练中创建的任何解决方案,则必须指定您的解决方案中所包含的数据库项目的名称,而不是指定 MyAdvWorks。
从“宏资源管理器”运行 AddScriptsInDirectory 宏
如果“宏资源管理器”尚未打开,请打开“视图”菜单,指向“其他窗口”,再单击“宏资源管理器”。
将出现“宏资源管理器”。
在“宏资源管理器”中,右击 AddScriptsInDirectory(您可能需要展开 ImportScriptsExample 模块来显示该宏),再单击“运行”。
将出现“Visual Studio Macros”对话框,提示您“键入要在其中导入脚本的数据库项目的名称”[Type the name of the database project to which you want the scripts to be imported.]。
键入“MyAdvWorks”,再单击“确定”。
将再次出现“Visual Studio Macros”对话框,提示您“键入脚本文件夹的名称”[Type the script folder name.]。
单击“确定”接受默认行为(即,向“脚本”文件夹中添加脚本)。
将再次出现“Visual Studio Macros”对话框,提示您“键入用于导入的文件夹路径”[Type the folder path to import.]。
键入您在本主题前面“先决条件”部分中记下的包含脚本文件的路径。例如,如果您的脚本位于 C:\Temp 中,请键入“C:\Temp”,再单击“确定”。
该宏将一直运行,直到具有 .sql 或 .tsql 扩展名的所有文件添加到 MyAdvWorks 数据库项目的“脚本”文件夹中。
后续步骤
本演练说明了在针对数据库项目使用 Visual Studio 自动化模型时可以执行的任务的简单示例。如果您需要更大的灵活性,则可以使用 Visual Studio 外接程序中的自动化模型。