연습: 데이터베이스 프로젝트 배포를 확장하여 배포 계획 수정
데이터베이스 프로젝트를 배포할 때 사용자 지정 작업을 수행하는 배포 참가자를 만들 수 있습니다. DeploymentPlanModifier 또는 DeploymentPlanExecutor 중 하나를 만들 수 있습니다. 계획을 실행하기 전에 변경하려면 DeploymentPlanModifier를 사용하고, 계획을 실행하는 동안 작업을 수행하려면 DeploymentPlanExecutor를 사용합니다. 이 연습에서는 SqlRestartableScriptContributor라는 DeploymentPlanModifier를 만듭니다. 이 참가자는 배포 스크립트의 일괄 처리에 IF 문을 추가하여 실행 중 오류가 발생하더라도 일괄 처리가 완료되기 전까지는 스크립트를 다시 실행할 수 있도록 합니다.
이 연습에서는 다음 주요 작업을 수행합니다.
DeploymentPlanModifier 형식의 배포 참가자 만들기
배포 참가자 설치
배포 참가자 테스트
사전 요구 사항
이 연습을 완료하려면 다음 구성 요소가 필요합니다.
컴퓨터에 Visual Studio 2010 Premium 또는 Visual Studio 2010 Ultimate가 설치되어 있어야 합니다.
데이터베이스 개체가 포함된 데이터베이스 프로젝트가 있어야 합니다.
데이터베이스 프로젝트를 배포할 수 있는 SQL Server 인스턴스가 있어야 합니다.
참고
이 연습은 Visual Studio의 데이터베이스 기능에 이미 친숙한 사용자를 위한 것입니다. 사용자는 클래스 라이브러리를 만드는 방법, 코드 편집기를 사용하여 클래스에 코드를 추가하는 방법 등과 같은 기본 Visual Studio 개념도 알고 있어야 합니다.
배포 참가자 만들기
배포 참가자를 만들려면 다음 작업을 수행해야 합니다.
클래스 라이브러리 프로젝트를 만들고 필요한 참조를 추가합니다.
DeploymentPlanModifier에서 상속하는 SqlRestartableScriptContributor라는 클래스를 정의합니다.
OnExecute 메서드를 재정의합니다.
private 도우미 메서드를 추가합니다.
결과 어셈블리를 빌드합니다.
클래스 라이브러리 프로젝트를 만들려면
MyOtherDeploymentContributor라는 Visual C# 또는 Visual Basic 클래스 라이브러리 프로젝트를 만듭니다.
솔루션 탐색기에서 프로젝트를 마우스 오른쪽 단추로 클릭한 다음 참조 추가를 클릭합니다.
.NET 탭을 클릭합니다.
Microsoft.Data.Schema, Microsoft.Data.Schema.Sql, Microsoft.Data.Schema.ScriptDom 및 Microsoft.Data.Schema.ScriptDom.Sql 항목을 강조 표시하고 확인을 클릭합니다.
다음에는 클래스에 코드를 추가하기 시작합니다.
SqlRestartableScriptContributor 클래스를 정의하려면
코드 편집기에서 class1.cs 파일을 다음 using 문과 같이 업데이트합니다.
using System; using System.Collections.Generic; using System.Text; using Microsoft.Data.Schema.Build; using Microsoft.Data.Schema.ScriptDom.Sql; using Microsoft.Data.Schema.SchemaModel; using System.Globalization; using Microsoft.Data.Schema.ScriptDom; using Microsoft.Data.Schema.Extensibility; using Microsoft.Data.Schema.Sql; using Microsoft.Data.Schema.Sql.Build; using Microsoft.Data.Schema.Sql.SchemaModel;
Imports System Imports System.Collections.Generic Imports System.Text Imports Microsoft.Data.Schema.Build Imports Microsoft.Data.Schema.ScriptDom.Sql Imports Microsoft.Data.Schema.SchemaModel Imports System.Globalization Imports Microsoft.Data.Schema.ScriptDom Imports Microsoft.Data.Schema.Extensibility Imports Microsoft.Data.Schema.Sql Imports Microsoft.Data.Schema.Sql.Build Imports Microsoft.Data.Schema.Sql.SchemaModel
클래스 정의를 다음 예제와 같이 업데이트합니다.
/// <summary> /// This deployment contributor modifies a deployment plan by adding if statements /// to the existing batches in order to make a deployment script able to be rerun to completion /// if an error is encountered during execution /// </summary> [DatabaseSchemaProviderCompatibility(typeof(SqlDatabaseSchemaProvider))] class SqlRestartableScriptContributor : DeploymentPlanModifier { }
''' <summary> ''' This deployment contributor modifies a deployment plan by adding if statements ''' to the existing batches in order to make a deployment script able to be rerun to completion ''' if an error is encountered during execution ''' </summary> <DatabaseSchemaProviderCompatibility(GetType(SqlDatabaseSchemaProvider))> _ Class SqlRestartableScriptContributor Inherits DeploymentPlanModifier End Class
이제 DeploymentPlanModifier에서 상속하는 빌드 참가자가 정의되었습니다. 여기에서는 DatabaseSchemaProviderCompatibilityAttribute 특성을 사용하여 이 참가자가 SqlDatabaseSchemaProvider에서 상속하는 모든 데이터베이스 스키마 공급자와 호환됨을 나타냈습니다.
다음 멤버 선언을 추가합니다.
private const string BatchIdColumnName = "BatchId"; private const string DescriptionColumnName = "Description"; private const string CompletedBatchesVariableName = "CompletedBatches"; private const string CompletedBatchesVariable = "$(CompletedBatches)"; private const string CompletedBatchesSqlCmd = @":setvar " + CompletedBatchesVariableName + " __completedBatches_{0}_{1}"; private const string TotalBatchCountSqlCmd = @":setvar TotalBatchCount {0}"; private const string CreateCompletedBatchesTable = @" if OBJECT_ID(N'tempdb.dbo." + CompletedBatchesVariable + @"', N'U') is null begin use tempdb create table [dbo].[$(CompletedBatches)] ( BatchId int primary key, Description nvarchar(300) ) use [$(DatabaseName)] end "; private const string DropCompletedBatchesTable = @"drop table [tempdb].[dbo].[" + CompletedBatchesVariable + "]";
Private Const BatchIdColumnName As String = "BatchId" Private Const DescriptionColumnName As String = "Description" Private Const CompletedBatchesVariableName As String = "CompletedBatches" Private Const CompletedBatchesVariable As String = "$(CompletedBatches)" Private Const CompletedBatchesSqlCmd As String = ":setvar " & CompletedBatchesVariableName & " __completedBatches_{0}_{1}" Private Const TotalBatchCountSqlCmd As String = ":setvar TotalBatchCount {0}" Private Const CreateCompletedBatchesTable As String = vbCr & vbLf & "if OBJECT_ID(N'tempdb.dbo." & CompletedBatchesVariable & "', N'U') is null" & vbCr & vbLf & "begin" & vbCr & vbLf & vbTab & "use tempdb" & vbTab & vbCr & vbLf & vbTab & "create table [dbo].[$(CompletedBatches)]" & vbCr & vbLf & vbTab & "(" & vbCr & vbLf & vbTab & vbTab & "BatchId int primary key," & vbCr & vbLf & vbTab & vbTab & "Description nvarchar(300)" & vbCr & vbLf & vbTab & ")" & vbCr & vbLf & vbTab & "use [$(DatabaseName)]" & vbTab & vbCr & vbLf & "end" & vbCr & vbLf Private Const DropCompletedBatchesTable As String = "drop table [tempdb].[dbo].[" & CompletedBatchesVariable & "]"
다음에는 OnExecute 메서드를 재정의하여 데이터베이스 프로젝트가 배포될 때 실행할 코드를 추가합니다.
OnExecute를 재정의하려면
SqlRestartableScriptContributor 클래스에 다음 메서드를 추가합니다.
/// <summary> /// You override the OnExecute method to do the real work of the contributor. /// </summary> /// <param name="context"></param> protected override void OnExecute(DeploymentPlanContributorContext context) { // Replace this with the method body }
''' <summary> ''' You override the OnExecute method to do the real work of the contributor. ''' </summary> ''' <param name="context"></param> Protected Overloads Overrides Sub OnExecute(ByVal context As DeploymentPlanContributorContext) ' Replace this with the method body End Sub
여기에서는 기본 클래스 DeploymentPlanContributor의 OnExecute 메서드를 재정의합니다. 이 클래스는 DeploymentPlanModifier 및 DeploymentPlanExecutor 모두의 기본 클래스입니다. OnExecute 메서드에는 지정된 인수에 대한 액세스, 소스 및 대상 데이터베이스 모델, 빌드 속성, 확장 파일을 제공하는 DeploymentPlanContributorContext 개체가 전달됩니다. 이 예제에서는 배포 계획과 대상 데이터베이스 이름을 가져옵니다.
이제 OnExecute 메서드에 본문 시작 부분을 추가합니다.
// Obtain the first step in the Plan from the provided context DeploymentStep nextStep = context.PlanHandle.Head; int batchId = 0; BeginPreDeploymentScriptStep beforePreDeploy = null; // Loop through all steps in the deployment plan while (nextStep != null) { // Increment the step pointer, saving both the current and next steps DeploymentStep currentStep = nextStep; nextStep = currentStep.Next; // Add additional step processing here } // if we found steps that required processing, set up a temporary table to track the work that you are doing if (beforePreDeploy != null) { // Add additional post-processing here } // Cleanup and drop the table DeploymentScriptStep dropStep = new DeploymentScriptStep(DropCompletedBatchesTable); base.AddAfter(context.PlanHandle, context.PlanHandle.Tail, dropStep);
' Obtain the first step in the Plan from the provided context Dim nextStep As DeploymentStep = context.PlanHandle.Head Dim batchId As Integer = 0 Dim beforePreDeploy As BeginPreDeploymentScriptStep = Nothing ' Loop through all steps in the deployment plan While nextStep IsNot Nothing ' Increment the step pointer, saving both the current and next steps Dim currentStep As DeploymentStep = nextStep nextStep = currentStep.[Next] ' Add additional step processing here End While ' if we found steps that required processing, set up a temporary table to track the work that you are doing If beforePreDeploy IsNot Nothing Then ' Add additional post-processing here End If ' Cleanup and drop the table Dim dropStep As New DeploymentScriptStep(DropCompletedBatchesTable) MyBase.AddAfter(context.PlanHandle, context.PlanHandle.Tail, dropStep)
이 코드에서는 몇 개의 로컬 변수를 정의하고, 배포 계획의 모든 단계를 처리할 루프를 설정합니다. 루프가 완료된 후에는 후처리 작업을 수행해야 하며, 그런 다음 계획이 실행될 때 진행 상태를 추적하기 위해 배포 중에 만든 임시 테이블을 삭제합니다. 주요 형식은 DeploymentStep과 DeploymentScriptStep이며, 주요 메서드는 AddAfter입니다.
이제 추가 단계 처리를 추가하여 "Add additional step processing here"라는 주석을 바꿉니다.
// Look for steps that mark the pre/post deployment scripts // These steps will always be in the deployment plan even if the // user's project does not have a pre/post deployment script if (currentStep is BeginPreDeploymentScriptStep) { // This step marks the begining of the predeployment script. // Save the step and move on. beforePreDeploy = (BeginPreDeploymentScriptStep)currentStep; continue; } if (currentStep is BeginPostDeploymentScriptStep) { // This is the step that marks the beginning of the post deployment script. // We do not continue processing after this point. break; } if (currentStep is SqlPrintStep) { // We do not need to put if statements around these continue; } // if we have not yet found the beginning of the pre-deployment script steps, // skip to the next step. if (beforePreDeploy == null) { // We only surround the "main" statement block with conditional // statements continue; } // Determine if this is a step that we need to surround with a conditional statement DeploymentScriptDomStep domStep = currentStep as DeploymentScriptDomStep ; if (domStep == null) { // This step is not a step that we know how to modify, // so skip to the next step. continue; } TSqlScript script = domStep.Script as TSqlScript; if (script == null) { // The script dom step does not have a script with batches - skip continue; } // Loop through all the batches in the script for this step. All the statements // in the batch will be enclosed in an if statement that will check the // table to ensure that the batch has not already been executed IModelElement element; string stepDescription; GetStepInfo(context, domStep, out stepDescription, out element); int batchCount = script.Batches.Count; for (int batchIndex = 0; batchIndex < batchCount; batchIndex++) { // Add batch processing here }
' Look for steps that mark the pre/post deployment scripts ' These steps will always be in the deployment plan even if the ' user's project does not have a pre/post deployment script If TypeOf currentStep Is BeginPreDeploymentScriptStep Then ' This step marks the begining of the predeployment script. ' Save the step and move on. beforePreDeploy = DirectCast(currentStep, BeginPreDeploymentScriptStep) Continue While End If If TypeOf currentStep Is BeginPostDeploymentScriptStep Then ' This is the step that marks the beginning of the post deployment script. ' We do not continue processing after this point. Exit While End If If TypeOf currentStep Is SqlPrintStep Then ' We do not need to put if statements around these Continue While End If ' if we have not yet found the beginning of the pre-deployment script steps, ' skip to the next step. If beforePreDeploy Is Nothing Then ' We only surround the "main" statement block with conditional ' statements Continue While End If ' Determine if this is a step that we need to surround with a conditional statement Dim domStep As DeploymentScriptDomStep = TryCast(currentStep, DeploymentScriptDomStep) If domStep Is Nothing Then ' This step is not a step that we know how to modify, ' so skip to the next step. Continue While End If Dim script As TSqlScript = TryCast(domStep.Script, TSqlScript) If script Is Nothing Then ' The script dom step does not have a script with batches - skip Continue While End If ' Loop through all the batches in the script for this step. All the statements ' in the batch will be enclosed in an if statement that will check the ' table to ensure that the batch has not already been executed Dim element As IModelElement = Nothing Dim stepDescription As String = "" GetStepInfo(context, domStep, stepDescription, element) Dim batchCount As Integer = script.Batches.Count For batchIndex As Integer = 0 To batchCount - 1 ' Add batch processing here Next
이 코드 주석에서는 처리에 대해 설명합니다. 상위 수준에서 이 코드는 사용자가 원하는 단계를 찾고 다른 단계는 무시하며 배포 후 단계의 처음 부분에 도달하면 중지합니다. 단계에 조건으로 둘러싸야 하는 문이 포함되어 있으면 추가 처리를 수행합니다. 주요 형식, 메서드 및 속성으로는 BeginPreDeploymentScriptStep, BeginPostDeploymentScriptStep, IModelElement, TSqlScript, Script, DeploymentScriptDomStep 및 SqlPrintStep이 있습니다.
이제 "Add batch processing here"라는 주석 대신 일괄 처리 코드를 추가합니다.
// Create the if statement that will contain the batch's contents IfStatement ifBatchNotExecutedStatement = CreateIfNotExecutedStatement(batchId); BeginEndBlockStatement statementBlock = new BeginEndBlockStatement(); ifBatchNotExecutedStatement.ThenStatement = statementBlock; statementBlock.StatementList = new StatementList(); TSqlBatch batch = script.Batches[batchIndex]; int statementCount = batch.Statements.Count; // Loop through all statements in the batch, embedding those in an sp_execsql // statement that must be handled this way (schemas, stored procedures, // views, functions, and triggers). for (int statementIndex = 0; statementIndex < statementCount; statementIndex++) { // Add additional statement processing here } // Add an insert statement to track that all the statements in this // batch were executed. Turn on nocount to improve performance by // avoiding row inserted messages from the server string batchDescription = string.Format(CultureInfo.InvariantCulture, "{0} batch {1}", stepDescription, batchIndex); PredicateSetStatement noCountOff = new PredicateSetStatement(); noCountOff.IsOn = false; noCountOff.Options = SetOptions.NoCount; PredicateSetStatement noCountOn = new PredicateSetStatement(); noCountOn.IsOn = true; noCountOn.Options = SetOptions.NoCount; InsertStatement batchCompleteInsert = CreateBatchCompleteInsert(batchId, batchDescription); statementBlock.StatementList.Statements.Add(noCountOn); statementBlock.StatementList.Statements.Add(batchCompleteInsert); statementBlock.StatementList.Statements.Add(noCountOff); // Remove all the statements from the batch (they are now in the if block) and add the if statement // as the sole statement in the batch batch.Statements.Clear(); batch.Statements.Add(ifBatchNotExecutedStatement); // Next batch batchId++;
' Create the if statement that will contain the batch's contents Dim ifBatchNotExecutedStatement As IfStatement = CreateIfNotExecutedStatement(batchId) Dim statementBlock As New BeginEndBlockStatement() ifBatchNotExecutedStatement.ThenStatement = statementBlock statementBlock.StatementList = New StatementList() Dim batch As TSqlBatch = script.Batches(batchIndex) Dim statementCount As Integer = batch.Statements.Count ' Loop through all statements in the batch, embedding those in an sp_execsql ' statement that must be handled this way (schemas, stored procedures, ' views, functions, and triggers). For statementIndex As Integer = 0 To statementCount - 1 ' Add additional statement processing here Next ' Add an insert statement to track that all the statements in this ' batch were executed. Turn on nocount to improve performance by ' avoiding row inserted messages from the server Dim batchDescription As String = String.Format(CultureInfo.InvariantCulture, "{0} batch {1}", stepDescription, batchIndex) Dim noCountOff As New PredicateSetStatement() noCountOff.IsOn = False noCountOff.Options = SetOptions.NoCount Dim noCountOn As New PredicateSetStatement() noCountOn.IsOn = True noCountOn.Options = SetOptions.NoCount Dim batchCompleteInsert As InsertStatement = CreateBatchCompleteInsert(batchId, batchDescription) statementBlock.StatementList.Statements.Add(noCountOn) statementBlock.StatementList.Statements.Add(batchCompleteInsert) statementBlock.StatementList.Statements.Add(noCountOff) ' Remove all the statements from the batch (they are now in the if block) and add the if statement ' as the sole statement in the batch batch.Statements.Clear() batch.Statements.Add(ifBatchNotExecutedStatement) ' Next batch batchId += 1
이 코드에서는 BEGIN/END 블록과 함께 IF 문을 만듭니다. 그런 다음 일괄 처리의 문에서 추가 처리를 수행합니다. 처리가 완료된 후에는 INSERT 문을 추가하여 스크립트 실행의 진행 상태를 추적하는 임시 테이블에 정보를 추가합니다. 마지막으로, 일괄 처리에 있던 문을 해당 문이 포함된 새 IF 문으로 바꿔 일괄 처리를 업데이트합니다.
주요 형식, 메서드 및 속성으로는 IfStatement, BeginEndBlockStatement, StatementList, TSqlBatch, PredicateSetStatement, SetOptions 및 InsertStatement가 있습니다.
이제 문 처리 루프의 본문을 추가합니다. "Add additional statement processing here"라는 주석을 다음과 같이 바꿉니다.
TSqlStatement smnt = batch.Statements[statementIndex]; if (IsStatementEscaped(element)) { // "escape" this statement by embedding it in a sp_executesql statement string statementScript = null; domStep.ScriptGenerator.GenerateScript(smnt, out statementScript); ExecuteStatement spExecuteSql = CreateExecuteSql(statementScript); smnt = spExecuteSql; } statementBlock.StatementList.Statements.Add(smnt);
Dim smnt As TSqlStatement = batch.Statements(statementIndex) If IsStatementEscaped(element) Then ' "escape" this statement by embedding it in a sp_executesql statement Dim statementScript As String = Nothing domStep.ScriptGenerator.GenerateScript(smnt, statementScript) Dim spExecuteSql As ExecuteStatement = CreateExecuteSql(statementScript) smnt = spExecuteSql End If statementBlock.StatementList.Statements.Add(smnt)
일괄 처리의 각 문에 대해, 해당 문이 sp_executesql 문으로 래핑되어야 하는 형식이면 그에 따라 문을 수정합니다. 그런 다음 코드에서는 이전에 만든 BEGIN/END 블록의 문 목록에 해당 문을 추가합니다. 주요 형식, 메서드 및 속성으로는 TSqlStatement 및 ExecuteStatement가 있습니다.
마지막으로, "Add additional post-processing here"라는 주석 대신 후처리 섹션을 추가합니다.
// Declare a SqlCmd variables. // // CompletedBatches variable - defines the name of the table in tempdb that will track // all the completed batches. The temporary table's name has the target database name and // a guid embedded in it so that: // * Multiple deployment scripts targeting different DBs on the same server // * Failed deployments with old tables do not conflict with more recent deployments // // TotalBatchCount variable - the total number of batches surrounded by if statements. Using this // variable pre/post deployment scripts can also use the CompletedBatches table to make their // script rerunnable if there is an error during execution StringBuilder sqlcmdVars = new StringBuilder(); sqlcmdVars.AppendFormat(CultureInfo.InvariantCulture, CompletedBatchesSqlCmd, context.Options.TargetDatabaseName, Guid.NewGuid().ToString("D")); sqlcmdVars.AppendLine(); sqlcmdVars.AppendFormat(CultureInfo.InvariantCulture, TotalBatchCountSqlCmd, batchId); DeploymentScriptStep completedBatchesSetVarStep = new DeploymentScriptStep(sqlcmdVars.ToString()); base.AddBefore(context.PlanHandle, beforePreDeploy, completedBatchesSetVarStep); // Create the temporary table we will use to track the work that we are doing DeploymentScriptStep createStatusTableStep = new DeploymentScriptStep(CreateCompletedBatchesTable); base.AddBefore(context.PlanHandle, beforePreDeploy, createStatusTableStep);
' Declare a SqlCmd variables. ' ' CompletedBatches variable - defines the name of the table in tempdb that will track ' all the completed batches. The temporary table's name has the target database name and ' a guid embedded in it so that: ' * Multiple deployment scripts targeting different DBs on the same server ' * Failed deployments with old tables do not conflict with more recent deployments ' ' TotalBatchCount variable - the total number of batches surrounded by if statements. Using this ' variable pre/post deployment scripts can also use the CompletedBatches table to make their ' script rerunnable if there is an error during execution Dim sqlcmdVars As New StringBuilder() sqlcmdVars.AppendFormat(CultureInfo.InvariantCulture, CompletedBatchesSqlCmd, context.Options.TargetDatabaseName, Guid.NewGuid().ToString("D")) sqlcmdVars.AppendLine() sqlcmdVars.AppendFormat(CultureInfo.InvariantCulture, TotalBatchCountSqlCmd, batchId) Dim completedBatchesSetVarStep As New DeploymentScriptStep(sqlcmdVars.ToString()) MyBase.AddBefore(context.PlanHandle, beforePreDeploy, completedBatchesSetVarStep) ' Create the temporary table we will use to track the work that we are doing Dim createStatusTableStep As New DeploymentScriptStep(CreateCompletedBatchesTable) MyBase.AddBefore(context.PlanHandle, beforePreDeploy, createStatusTableStep)
처리를 통해 조건문으로 둘러싸야 하는 단계를 하나 이상 발견한 경우 배포 스크립트에 문을 추가하여 SQLCMD 변수를 정의해야 합니다. 첫 번째 변수인 CompletedBatches에는 배포 스크립트에서 스크립트 실행 시 성공적으로 완료된 일괄 처리를 추적하는 데 사용되는 임시 테이블의 고유 이름이 포함됩니다. 두 번째 변수인 TotalBatchCount에는 배포 스크립트의 총 일괄 처리 수가 포함됩니다.
주목해야 할 추가 형식, 속성 및 메서드는 다음과 같습니다.
StringBuilder, DeploymentScriptStep 및 AddBefore
다음에는 이 메서드에서 호출하는 도우미 메서드를 정의합니다.
CreateExecuteSql 메서드를 추가하려면
다음 코드를 추가하여 제공된 문을 EXEC sp_executesql 문으로 둘러싸는 CreateExecuteSQL 메서드를 정의합니다.
/// <summary> /// The CreateExecuteSql method "wraps" the provided statement script in an "sp_executesql" statement /// Examples of statements that must be so wrapped include: stored procedures, views, and functions /// </summary> /// <param name="string"></param> /// <returns></returns> private static ExecuteStatement CreateExecuteSql(string statementScript) { // define a new Exec statement ExecuteStatement executeSp = new ExecuteStatement(); ExecutableProcedureReference spExecute = new ExecutableProcedureReference(); executeSp.ExecutableEntity = spExecute; // define the name of the procedure that you want to execute, in this case sp_executesql SchemaObjectName procName = new SchemaObjectName(); procName.Identifiers.Add(CreateIdentifier("sp_executesql", QuoteType.NotQuoted)); ProcedureReference procRef = new ProcedureReference(); procRef.Name = procName; spExecute.ProcedureReference = procRef; // add the script parameter, constructed from the provided statement script ExecuteParameter scriptParam = new ExecuteParameter(); spExecute.Parameters.Add(scriptParam); scriptParam.ParameterValue = CreateLiteral(statementScript, LiteralType.UnicodeStringLiteral); scriptParam.Variable = CreateLiteral("@stmt", LiteralType.Variable); return executeSp; }
''' <summary> ''' The CreateExecuteSql method "wraps" the provided statement script in an "sp_executesql" statement ''' Examples of statements that must be so wrapped include: stored procedures, views, and functions ''' </summary> ''' <param name="statementScript"></param> ''' <returns></returns> Private Shared Function CreateExecuteSql(ByVal statementScript As String) As ExecuteStatement ' define a new Exec statement Dim executeSp As New ExecuteStatement() Dim spExecute As New ExecutableProcedureReference() executeSp.ExecutableEntity = spExecute ' define the name of the procedure that you want to execute, in this case sp_executesql Dim procName As New SchemaObjectName() procName.Identifiers.Add(CreateIdentifier("sp_executesql", QuoteType.NotQuoted)) Dim procRef As New ProcedureReference() procRef.Name = procName spExecute.ProcedureReference = procRef ' add the script parameter, constructed from the provided statement script Dim scriptParam As New ExecuteParameter() spExecute.Parameters.Add(scriptParam) scriptParam.ParameterValue = CreateLiteral(statementScript, LiteralType.UnicodeStringLiteral) scriptParam.Variable = CreateLiteral("@stmt", LiteralType.Variable) Return executeSp End Function
주요 형식, 메서드 및 속성으로는 ExecuteStatement, ExecutableProcedureReference, SchemaObjectName, ProcedureReference 및 ExecuteParameter가 있습니다.
CreateLiteral 메서드를 추가하려면
다음 코드를 추가하여 CreateLiteral 메서드를 정의합니다. 이 메서드는 제공된 문자열에서 지정된 형식의 리터럴 개체를 만듭니다.
/// <summary> /// The CreateLiteral method creates a Literal object of the specified type from the provided string /// </summary> /// <param name="value"></param> /// <param name="type"></param> /// <returns></returns> private static Literal CreateLiteral(string value, LiteralType type) { Literal l = new Literal(); l.Value = value; l.LiteralType = type; return l; }
''' <summary> ''' The CreateLiteral method creates a Literal object of the specified type from the provided string ''' </summary> ''' <param name="value"></param> ''' <param name="type"></param> ''' <returns></returns> Private Shared Function CreateLiteral(ByVal value As String, ByVal type As LiteralType) As Literal Dim l As New Literal() l.Value = value l.LiteralType = type Return l End Function
주요 형식, 메서드 및 속성으로는 Literal 및 LiteralType가 있습니다.
CreateIdentifier 메서드를 추가하려면
다음 코드를 추가하여 CreateIdentifier 메서드를 정의합니다. 이 메서드는 제공된 문자열에서 지정된 따옴표 형식을 사용하는 식별자 개체를 만듭니다.
/// <summary> /// The CreateIdentifier method returns a Identifier with the specified value and quoting type /// </summary> /// <param name="value"></param> /// <param name="quoteType"></param> /// <returns></returns> private static Identifier CreateIdentifier(string value, QuoteType quoteType) { Identifier id = new Identifier(); id.Value = value; id.QuoteType = quoteType; return id; }
''' <summary> ''' The CreateIdentifier method returns a Identifier with the specified value and quoting type ''' </summary> ''' <param name="value"></param> ''' <param name="quoteType"></param> ''' <returns></returns> Private Shared Function CreateIdentifier(ByVal value As String, ByVal quoteType As QuoteType) As Identifier Dim id As New Identifier() id.Value = value id.QuoteType = quoteType Return id End Function
주요 형식, 메서드 및 속성으로는 Identifier 및 QuoteType이 있습니다.
CreateCompletedBatchesName 메서드를 추가하려면
다음 코드를 추가하여 CreateCompletedBatchesName 메서드를 정의합니다. 이 메서드는 일괄 처리용 임시 테이블에 삽입할 이름을 만듭니다.
/// <summary> /// The CreateCompletedBatchesName method creates the name that will be inserted /// into the temporary table for a batch. /// </summary> /// <returns></returns> private static SchemaObjectName CreateCompletedBatchesName() { SchemaObjectName name = new SchemaObjectName(); name.Identifiers.Add(CreateIdentifier("tempdb", QuoteType.SquareBracket)); name.Identifiers.Add(CreateIdentifier("dbo", QuoteType.SquareBracket)); name.Identifiers.Add(CreateIdentifier(CompletedBatchesVariable, QuoteType.SquareBracket)); return name; }
''' <summary> ''' The CreateCompletedBatchesName method creates the name that will be inserted ''' into the temporary table for a batch. ''' </summary> ''' <returns></returns> Private Shared Function CreateCompletedBatchesName() As SchemaObjectName Dim name As New SchemaObjectName() name.Identifiers.Add(CreateIdentifier("tempdb", QuoteType.SquareBracket)) name.Identifiers.Add(CreateIdentifier("dbo", QuoteType.SquareBracket)) name.Identifiers.Add(CreateIdentifier(CompletedBatchesVariable, QuoteType.SquareBracket)) Return name End Function
주요 형식, 메서드 및 속성으로는 SchemaObjectName이 있습니다.
IsStatementEscaped 메서드를 추가하려면
다음 코드를 추가하여 IsStatementEscaped 메서드를 정의합니다. 이 메서드는 모델 요소의 형식이 해당 문을 EXEC sp_executesql 문에 래핑해야만 IF 문 내에 포함할 수 있는 형식인지 확인합니다.
/// <summary> /// Helper method that determins whether the specified statement needs to /// be escaped /// </summary> /// <param name="smnt"></param> /// <returns></returns> private static bool IsStatementEscaped(IModelElement element) { return element is ISql90Schema || element is ISqlProcedure || element is ISqlView || element is ISqlFunction || element is ISqlTrigger; }
''' <summary> ''' Helper method that determins whether the specified statement needs to ''' be escaped ''' </summary> ''' <param name="element"></param> ''' <returns></returns> Private Shared Function IsStatementEscaped(ByVal element As IModelElement) As Boolean Return TypeOf element Is ISql90Schema OrElse TypeOf element Is ISqlProcedure OrElse TypeOf element Is ISqlView OrElse TypeOf element Is ISqlFunction OrElse TypeOf element Is ISqlTrigger End Function
주요 형식, 메서드 및 속성으로는 IModelElement, ISql90Schema, ISqlProcedure, ISqlView, ISqlFunction 및 ISqlTrigger가 있습니다.
CreateBatchCompleteInsert 메서드를 추가하려면
다음 코드를 추가하여 CreateBatchCompleteInsert 메서드를 정의합니다. 이 메서드는 스크립트 실행의 진행 상태를 추적하기 위해 배포 스크립트에 추가할 INSERT 문을 만듭니다.
/// <summary> /// Helper method that creates an INSERT statement to track a batch being completed /// </summary> /// <param name="batchId"></param> /// <param name="batchDescription"></param> /// <returns></returns> private static InsertStatement CreateBatchCompleteInsert(int batchId, string batchDescription) { InsertStatement insert = new InsertStatement(); SchemaObjectDataModificationTarget batchesCompleted = new SchemaObjectDataModificationTarget(); insert.Target = batchesCompleted; batchesCompleted.SchemaObject = CreateCompletedBatchesName(); // Build the columns inserted into Column batchIdColumn = new Column(); batchIdColumn.Identifiers.Add(CreateIdentifier(BatchIdColumnName, QuoteType.NotQuoted)); Column descriptionColumn = new Column(); descriptionColumn.Identifiers.Add(CreateIdentifier(DescriptionColumnName, QuoteType.NotQuoted)); insert.Columns.Add(batchIdColumn); insert.Columns.Add(descriptionColumn); // Build the values inserted ValuesInsertSource valueSource = new ValuesInsertSource(); insert.InsertSource = valueSource; RowValue values = new RowValue(); values.ColumnValues.Add(CreateLiteral(batchId.ToString(), LiteralType.Integer)); values.ColumnValues.Add(CreateLiteral(batchDescription, LiteralType.UnicodeStringLiteral)); valueSource.RowValues.Add(values); return insert; }
''' <summary> ''' Helper method that creates an INSERT statement to track a batch being completed ''' </summary> ''' <param name="batchId"></param> ''' <param name="batchDescription"></param> ''' <returns></returns> Private Shared Function CreateBatchCompleteInsert(ByVal batchId As Integer, ByVal batchDescription As String) As InsertStatement Dim insert As New InsertStatement() Dim batchesCompleted As New SchemaObjectDataModificationTarget() insert.Target = batchesCompleted batchesCompleted.SchemaObject = CreateCompletedBatchesName() ' Build the columns inserted into Dim batchIdColumn As New Column() batchIdColumn.Identifiers.Add(CreateIdentifier(BatchIdColumnName, QuoteType.NotQuoted)) Dim descriptionColumn As New Column() descriptionColumn.Identifiers.Add(CreateIdentifier(DescriptionColumnName, QuoteType.NotQuoted)) insert.Columns.Add(batchIdColumn) insert.Columns.Add(descriptionColumn) ' Build the values inserted Dim valueSource As New ValuesInsertSource() insert.InsertSource = valueSource Dim values As New RowValue() values.ColumnValues.Add(CreateLiteral(batchId.ToString(), LiteralType.[Integer])) values.ColumnValues.Add(CreateLiteral(batchDescription, LiteralType.UnicodeStringLiteral)) valueSource.RowValues.Add(values) Return insert End Function
주요 형식, 메서드 및 속성으로는 InsertStatement, SchemaObjectDataModificationTarget, Column, ValuesInsertSource 및 RowValue가 있습니다.
CreateIfNotExecutedStatement 메서드를 추가하려면
다음 코드를 추가하여 CreateIfNotExecutedStatement 메서드를 정의합니다. 이 메서드는 임시 일괄 처리에서 이 일괄 처리가 이미 실행되었음을 나타내는 테이블을 실행하는지 여부를 확인하는 IF 문을 생성합니다.
/// <summary> /// This is a helper method that generates an if statement that checks the batches executed /// table to see if the current batch has been executed. The if statement will look like this /// /// if not exists(select 1 from [tempdb].[dbo].[$(CompletedBatches)] /// where BatchId = batchId) /// begin /// end /// </summary> /// <param name="batchId"></param> /// <returns></returns> private static IfStatement CreateIfNotExecutedStatement(int batchId) { // Create the exists/select statement ExistsPredicate existsExp = new ExistsPredicate(); Subquery subQuery = new Subquery(); existsExp.Subquery = subQuery; QuerySpecification select = new QuerySpecification(); subQuery.QueryExpression = select; select.SelectElements.Add(CreateLiteral("1", LiteralType.Integer)); SchemaObjectTableSource completedBatchesTable = new SchemaObjectTableSource(); select.FromClauses.Add(completedBatchesTable); completedBatchesTable.SchemaObject = CreateCompletedBatchesName(); WhereClause where = new WhereClause(); select.WhereClause = where; Column batchIdColumn = new Column(); batchIdColumn.Identifiers.Add(CreateIdentifier(BatchIdColumnName, QuoteType.SquareBracket)); Literal batchIdValue = CreateLiteral(batchId.ToString(), LiteralType.Integer); BinaryExpression stepsEqual = new BinaryExpression(); where.SearchCondition = stepsEqual; stepsEqual.BinaryExpressionType = BinaryExpressionType.Equals; stepsEqual.FirstExpression = batchIdColumn; stepsEqual.SecondExpression = batchIdValue; // Put together the rest of the statement IfStatement ifNotExists = new IfStatement(); UnaryExpression notExp = new UnaryExpression(); ifNotExists.Predicate = notExp; notExp.UnaryExpressionType = UnaryExpressionType.Not; notExp.Expression = existsExp; return ifNotExists; }
''' <summary> ''' This is a helper method that generates an if statement that checks the batches executed ''' table to see if the current batch has been executed. The if statement will look like this ''' ''' if not exists(select 1 from [tempdb].[dbo].[$(CompletedBatches)] ''' where BatchId = batchId) ''' begin ''' end ''' </summary> ''' <param name="batchId"></param> ''' <returns></returns> Private Shared Function CreateIfNotExecutedStatement(ByVal batchId As Integer) As IfStatement ' Create the exists/select statement Dim existsExp As New ExistsPredicate() Dim subQuery As New Subquery() existsExp.Subquery = subQuery Dim [select] As New QuerySpecification() subQuery.QueryExpression = [select] [select].SelectElements.Add(CreateLiteral("1", LiteralType.[Integer])) Dim completedBatchesTable As New SchemaObjectTableSource() [select].FromClauses.Add(completedBatchesTable) completedBatchesTable.SchemaObject = CreateCompletedBatchesName() Dim where As New WhereClause() [select].WhereClause = where Dim batchIdColumn As New Column() batchIdColumn.Identifiers.Add(CreateIdentifier(BatchIdColumnName, QuoteType.SquareBracket)) Dim batchIdValue As Literal = CreateLiteral(batchId.ToString(), LiteralType.[Integer]) Dim stepsEqual As New BinaryExpression() where.SearchCondition = stepsEqual stepsEqual.BinaryExpressionType = BinaryExpressionType.Equals stepsEqual.FirstExpression = batchIdColumn stepsEqual.SecondExpression = batchIdValue ' Put together the rest of the statement Dim ifNotExists As New IfStatement() Dim notExp As New UnaryExpression() ifNotExists.Predicate = notExp notExp.UnaryExpressionType = UnaryExpressionType.[Not] notExp.Expression = existsExp Return ifNotExists End Function
주요 형식, 메서드 및 속성으로는 IfStatement, ExistsPredicate, Subquery, SchemaObjectTableSource, WhereClause, Column, Literal, BinaryExpression 및 UnaryExpression이 있습니다.
GetStepInfo 메서드를 추가하려면
다음 코드를 추가하여 GetStepInfo 메서드를 정의합니다. 이 메서드의 기능은 다음과 같습니다.
/// <summary> /// Helper method that generates a useful description of the step. /// </summary> /// <param name="context"></param> /// <param name="domStep"></param> /// <param name="stepDescription"></param> /// <param name="element"></param> private static void GetStepInfo( DeploymentPlanContributorContext context, DeploymentScriptDomStep domStep, out string stepDescription, out IModelElement element) { element = null; CreateElementStep createStep = null; AlterElementStep alterStep = null; DropElementStep dropStep = null; // figure out what type of step we've got, and retrieve // either the source or target element. if ((createStep = domStep as CreateElementStep) != null) { element = createStep.SourceElement; } else if ((alterStep = domStep as AlterElementStep) != null) { element = alterStep.SourceElement; } else if ((dropStep = domStep as DropElementStep) != null) { element = dropStep.TargetElement; } // construct the step description by concatenating the type and the fully qualified // name of the associated element. string stepTypeName = domStep.GetType().Name; if (element != null) { string elementName = context.Source.DatabaseSchemaProvider.UserInteractionServices.GetElementName( element, Microsoft.Data.Schema.ElementNameStyle.FullyQualifiedName); stepDescription = string.Format(CultureInfo.InvariantCulture, "{0} {1}", stepTypeName, elementName); } else { // if the step has no associated element, just use the step type as the description stepDescription = stepTypeName; } }
''' <summary> ''' Helper method that generates a useful description of the step. ''' </summary> ''' <param name="context"></param> ''' <param name="domStep"></param> ''' <param name="stepDescription"></param> ''' <param name="element"></param> Private Shared Sub GetStepInfo(ByVal context As DeploymentPlanContributorContext, ByVal domStep As DeploymentScriptDomStep, ByRef stepDescription As String, ByRef element As IModelElement) element = Nothing Dim createStep As CreateElementStep = Nothing Dim alterStep As AlterElementStep = Nothing Dim dropStep As DropElementStep = Nothing ' figure out what type of step we've got, and retrieve ' either the source or target element. If (InlineAssignHelper(createStep, TryCast(domStep, CreateElementStep))) IsNot Nothing Then element = createStep.SourceElement ElseIf (InlineAssignHelper(alterStep, TryCast(domStep, AlterElementStep))) IsNot Nothing Then element = alterStep.SourceElement ElseIf (InlineAssignHelper(dropStep, TryCast(domStep, DropElementStep))) IsNot Nothing Then element = dropStep.TargetElement End If ' construct the step description by concatenating the type and the fully qualified ' name of the associated element. Dim stepTypeName As String = domStep.[GetType]().Name If element IsNot Nothing Then Dim elementName As String = context.Source.DatabaseSchemaProvider.UserInteractionServices.GetElementName(element, Microsoft.Data.Schema.ElementNameStyle.FullyQualifiedName) stepDescription = String.Format(CultureInfo.InvariantCulture, "{0} {1}", stepTypeName, elementName) Else ' if the step has no associated element, just use the step type as the description stepDescription = stepTypeName End If End Sub Private Shared Function InlineAssignHelper(Of T)(ByRef target As T, ByVal value As T) As T target = value Return value End Function
주목할 형식 및 메서드로는 DeploymentPlanContributorContext, DeploymentScriptDomStep, IModelElement, CreateElementStep, AlterElementStep, and DropElementStep이 있습니다.
변경 내용을 Class1.cs에 저장합니다.
다음에는 클래스 라이브러리를 빌드합니다.
어셈블리에 서명하고 빌드하려면
프로젝트 메뉴에서 MyOtherDeploymentContributor 속성을 클릭합니다.
서명 탭을 클릭합니다.
어셈블리 서명을 클릭합니다.
강력한 이름 키 파일 선택에서 **<새로 만들기>**를 클릭합니다.
강력한 이름 키 만들기 대화 상자의 키 파일 이름에 MyRefKey를 입력합니다.
(옵션) 강력한 이름 키 파일의 암호를 지정할 수 있습니다.
확인을 클릭합니다.
파일 메뉴에서 모두 저장을 클릭합니다.
빌드 메뉴에서 솔루션 빌드를 클릭합니다.
다음에는 데이터베이스 프로젝트를 배포할 때 어셈블리가 로드되도록 어셈블리를 설치하고 등록해야 합니다.
배포 참가자 설치
배포 참가자를 설치하려면 다음 작업을 수행해야 합니다.
어셈블리 및 연결된 .pdb 파일을 Extensions 폴더에 복사합니다.
데이터베이스 프로젝트를 배포할 때 배포 참가자가 로드되도록 이를 등록하는 Extensions.xml 파일을 만듭니다.
MyOtherDeploymentContributor 어셈블리를 설치하려면
%Program Files%\Microsoft Visual Studio 10.0\VSTSDB\Extensions 폴더에 MyExtensions라는 폴더를 만듭니다.
서명된 어셈블리(MyOtherDeploymentContributor.dll)를 %Program Files%\Microsoft Visual Studio 10.0\VSTSDB\Extensions\MyExtensions 폴더에 복사합니다.
참고
XML 파일을 %Program Files%\Microsoft Visual Studio 10.0\VSTSDB\Extensions 폴더에 바로 복사하지 않는 것이 좋습니다. 대신 하위 폴더를 사용하면 Visual Studio에서 제공된 다른 파일을 실수로 변경하는 것을 방지할 수 있습니다.
다음에는 기능 확장의 한 형식인 어셈블리가 Visual Studio에 표시되도록 이를 등록해야 합니다.
MyOtherDeploymentContributor 어셈블리를 등록하려면
보기 메뉴에서 다른 창, 명령 창을 차례로 클릭하여 명령 창을 엽니다.
명령 창에서 다음 코드를 입력합니다. FilePath 대신 컴파일된 .dll 파일의 경로 및 파일 이름을 입력합니다. 이때 경로 및 파일 이름을 따옴표로 묶습니다.
참고
기본적으로 컴파일된 .dll 파일의 경로는 YourSolutionPath\bin\Debug 또는 YourSolutionPath\bin\Release입니다.
? System.Reflection.Assembly.LoadFrom(@"FilePath").FullName
? System.Reflection.Assembly.LoadFrom("FilePath").FullName
Enter 키를 누릅니다.
결과 줄을 클립보드로 복사합니다. 이 줄은 다음과 같습니다.
"GeneratorAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=nnnnnnnnnnnnnnnn"
메모장과 같은 일반 텍스트 편집기를 엽니다.
중요
Windows Vista 및 Microsoft Windows Server 2008에서 Program Files 폴더에 파일을 저장할 수 있도록 관리자 권한으로 편집기를 엽니다.
사용자 어셈블리 이름, 공개 키 토큰 및 확장 형식을 지정하여 다음 정보를 제공합니다.
<?xml version="1.0" encoding="utf-8" ?> <extensions assembly="" version="1" xmlns="urn:Microsoft.Data.Schema.Extensions" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:Microsoft.Data.Schema.Extensions Microsoft.Data.Schema.Extensions.xsd"> <extension type="MyOtherDeploymentContributor.SqlRestartableScriptContributor" assembly="MyOtherDeploymentContributor, Version=1.0.0.0, Culture=neutral, PublicKeyToken=<enter key here>" enabled="true" /> </extensions>
이 XML 파일을 사용하여 DeploymentPlanExecutor에서 상속하는 클래스를 등록합니다.
%Program Files%\Microsoft Visual Studio 10.0\VSTSDB\Extensions\MyExtensions 폴더에 MyOtherDeploymentContributor.extensions.xml로 파일을 저장합니다.
Visual Studio를 닫습니다.
다음에는 데이터베이스 프로젝트를 배포하여 참가자를 테스트합니다.
배포 참가자 테스트
배포 참가자를 테스트하려면 다음 작업을 수행해야 합니다.
- MSBuild를 사용하고 적절한 매개 변수를 제공하여 데이터베이스 프로젝트를 배포합니다.
이 배포 참가자는 항상 사용하도록 설정되어 있으므로 속성을 추가하기 위해 데이터베이스 프로젝트를 수정할 필요가 없습니다.
데이터베이스 프로젝트를 배포합니다.
데이터베이스 프로젝트를 배포하고 배포 보고서를 생성하려면
Visual Studio 명령 프롬프트를 엽니다. 시작 메뉴에서 모든 프로그램, Microsoft Visual Studio 2010, Visual Studio Tools, **Visual Studio 명령 프롬프트(2010)**를 차례로 클릭합니다.
명령 프롬프트에서 사용자 데이터베이스 프로젝트가 포함된 폴더로 이동합니다.
명령 프롬프트에서 다음 명령줄을 입력합니다.
MSBuild /t:Deploy MyDatabaseProject.dbproj
MyDatabaseProject를 배포할 데이터베이스 프로젝트의 이름으로 바꿉니다.
결과 배포 스크립트를 검사합니다. "Pre-Deployment Script Template"라는 레이블의 섹션 바로 앞에 다음 Transact-SQL과 유사한 내용이 있어야 합니다.
:setvar CompletedBatches __completedBatches_CompareProjectDB_cd1e348a-8f92-44e0-9a96-d25d65900fca :setvar TotalBatchCount 17 GO if OBJECT_ID(N'tempdb.dbo.$(CompletedBatches)', N'U') is null begin use tempdb create table [dbo].[$(CompletedBatches)] ( BatchId int primary key, Description nvarchar(300) ) use [$(DatabaseName)] end
배포 스크립트의 뒷부분에서 각 일괄 처리 주위에는 원래 문을 둘러싸는 IF 문이 있습니다. 예를 들어 CREATE SCHEMA 문의 경우 다음과 같이 나타날 수 있습니다.
IF NOT EXISTS (SELECT 1 FROM [tempdb].[dbo].[$(CompletedBatches)] WHERE [BatchId] = 0) BEGIN EXECUTE sp_executesql @stmt = N'CREATE SCHEMA [Sales] AUTHORIZATION [dbo]'; SET NOCOUNT ON; INSERT [tempdb].[dbo].[$(CompletedBatches)] (BatchId, Description) VALUES (0, N'CreateElementStep Sales batch 0'); SET NOCOUNT OFF; END
CREATE SCHEMA는 IF 문 내의 EXECUTE sp_executesql 문에 포함해야 하는 문 중 하나입니다. CREATE TABLE과 같은 문은 EXECUTE sp_executesql 문이 필요하지 않으며 다음 예제와 유사합니다.
IF NOT EXISTS (SELECT 1 FROM [tempdb].[dbo].[$(CompletedBatches)] WHERE [BatchId] = 1) BEGIN CREATE TABLE [Sales].[Customer] ( [CustomerID] INT IDENTITY (1, 1) NOT NULL, [CustomerName] NVARCHAR (40) NOT NULL, [YTDOrders] INT NOT NULL, [YTDSales] INT NOT NULL ); SET NOCOUNT ON; INSERT [tempdb].[dbo].[$(CompletedBatches)] (BatchId, Description) VALUES (1, N'CreateElementStep Sales.Customer batch 0'); SET NOCOUNT OFF; END
참고
대상 데이터베이스와 동일한 데이터베이스 프로젝트를 배포하면 결과 보고서에서 그다지 의미 있는 정보를 얻을 수 없습니다. 의미 있는 결과를 얻으려면 데이터베이스에 대한 변경 내용을 배포하거나 새 데이터베이스를 배포합니다.
DeploymentPlanModifier를 사용하여 모든 배포 계획의 일괄 처리 또는 문을 추가, 제거 또는 수정할 수 있습니다.
다음 단계
배포 계획을 실행하기 전에 배포 계획을 다른 방식으로 수정하여 실험할 수 있습니다. 예를 들어 버전 번호가 연결되는 모든 데이터베이스 개체에 확장 속성을 추가하거나, 배포 스크립트의 추가 진단 출력 문 또는 주석을 추가 또는 제거하는 등의 수정 작업을 수행할 수 있습니다.
참고 항목
개념
기타 리소스
빌드 및 배포 참가자를 사용하여 데이터베이스 빌드 및 배포 사용자 지정