연습: 데이터베이스 프로젝트 배포를 확장하여 배포 계획 분석

데이터베이스 프로젝트를 배포할 때 사용자 지정 작업을 수행하는 배포 참가자를 만들 수 있습니다. DeploymentPlanModifier 또는 DeploymentPlanExecutor 중 하나를 만들 수 있습니다. 계획을 실행하기 전에 변경하려면 DeploymentPlanModifier를 사용하고, 계획을 실행하는 동안 작업을 수행하려면 DeploymentPlanExecutor를 사용합니다. 이 연습에서는 데이터베이스 프로젝트를 배포할 때 수행된 작업에 대한 보고서를 만드는 DeploymentUpdateReportContributor라는 DeploymentPlanExecutor를 만듭니다. 이 빌드 참가자는 보고서 생성 여부를 제어하는 매개 변수를 받아들이므로 추가로 필요한 단계를 수행해야 합니다.

이 연습에서는 다음 주요 작업을 수행합니다.

  • DeploymentPlanExecutor 형식의 배포 참가자 만들기

  • 배포 참가자 설치

  • 배포 참가자 테스트

사전 요구 사항

이 연습을 완료하려면 다음 구성 요소가 필요합니다.

  • 컴퓨터에 Visual Studio 2010 Premium 또는 Visual Studio 2010 Ultimate가 설치되어 있어야 합니다.

  • 데이터베이스 개체가 포함된 데이터베이스 프로젝트가 있어야 합니다.

  • 데이터베이스 프로젝트를 배포할 수 있는 SQL Server 인스턴스가 있어야 합니다.

참고

이 연습은 Visual Studio의 데이터베이스 기능에 이미 친숙한 사용자를 위한 것입니다. 사용자는 클래스 라이브러리를 만드는 방법, 코드 편집기를 사용하여 클래스에 코드를 추가하는 방법 등과 같은 기본 Visual Studio 개념도 알고 있어야 합니다.

배포 참가자 만들기

배포 참가자를 만들려면 다음 작업을 수행해야 합니다.

  • 클래스 라이브러리 프로젝트를 만들고 필요한 참조를 추가합니다.

  • DeploymentPlanExecutor에서 상속하는 DeploymentUpdateReportContributor라는 클래스를 정의합니다.

  • OnPopulateArguments 및 OnExecute 메서드를 재정의합니다.

  • private 도우미 클래스를 추가합니다.

  • 결과 어셈블리를 빌드합니다.

클래스 라이브러리 프로젝트를 만들려면

  1. MyDeploymentContributor라는 Visual Basic 또는 Visual C# 클래스 라이브러리 프로젝트를 만듭니다.

  2. 솔루션 탐색기에서 참조 폴더를 마우스 오른쪽 단추로 클릭하고 참조 추가를 클릭합니다.

  3. .NET 탭을 클릭합니다.

  4. Microsoft.Data.SchemaMicrosoft.Data.Schema.Sql 항목을 강조 표시하고 확인을 클릭합니다.

    다음에는 클래스에 코드를 추가하기 시작합니다.

DeploymentUpdateReportContributor 클래스를 정의하려면

  1. 코드 편집기에서 class1.cs 파일을 다음의 using 또는 Imports 문과 같이 업데이트합니다.

    using System;
    using System.Collections.Generic;
    using System.Text;
    using Microsoft.Data.Schema.Build;
    using Microsoft.Data.Schema.Extensibility;
    using Microsoft.Data.Schema.Sql;
    using System.IO;
    using Microsoft.Data.Schema.SchemaModel;
    using System.Xml;
    using Microsoft.Data.Schema;
    using Microsoft.Data.Schema.Sql.Build;
    
    Imports System
    Imports System.Collections.Generic
    Imports System.Text
    Imports Microsoft.Data.Schema.Build
    Imports Microsoft.Data.Schema.Extensibility
    Imports Microsoft.Data.Schema.Sql
    Imports System.IO
    Imports Microsoft.Data.Schema.SchemaModel
    Imports System.Xml
    Imports Microsoft.Data.Schema
    Imports Microsoft.Data.Schema.Sql.Build
    
  2. 클래스 정의를 다음과 같이 업데이트합니다.

        [DatabaseSchemaProviderCompatibility(typeof(SqlDatabaseSchemaProvider))]
        class DeploymentUpdateReportContributor : DeploymentPlanExecutor
        {
        }
    
    ''' <summary>
    ''' The DeploymentUpdateReportContributor class demonstrates
    ''' how you can create a class that inherits DeploymentPlanExecutor
    ''' to perform actions when you execute the deployment plan
    ''' for a database project.
    ''' </summary>
    <DatabaseSchemaProviderCompatibility(GetType(SqlDatabaseSchemaProvider))>
    Public Class DeploymentUpdateReportContributor
        Inherits DeploymentPlanExecutor
    End Class
    

    빌드 참가자를 정의하고 특성을 사용하여 이 참가자가 SqlDatabaseSchemaProvider에서 상속하는 모든 데이터베이스 스키마 공급자와 호환됨을 나타냈습니다.

  3. 그런 다음 이 공급자가 명령줄 빌드 매개 변수를 받아들일 수 있도록 하는 데 사용할 다음 멤버를 추가합니다.

            private const string GenerateUpdateReport = "GenerateUpdateReport";
    
        Dim GenerateUpdateReport As String = "GenerateUpdateReport"
    

    이 멤버를 사용하면 사용자가 GenerateUpdateReport 옵션을 사용하여 보고서 생성 여부를 지정할 수 있습니다.

    다음에는 배포 참가자에 전달할 인수 목록을 빌드하도록 OnPopulateArguments 메서드를 재정의합니다.

OnPopulateArguments를 재정의하려면

  • DeploymentUpdateReportContributor 클래스에 다음과 같은 재정의 메서드를 추가합니다.

        /// <summary>
        /// Override the OnPopulateArgument method to build a list of arguments from the input
        /// configuration information.
        /// </summary>
            protected override IList<ContributorArgumentConfiguration> OnPopulateArguments()
            {
                List<ContributorArgumentConfiguration> args = new List<ContributorArgumentConfiguration>();
    
                // Generate reports when in debug configuration
                args.Add(new ContributorArgumentConfiguration( GenerateUpdateReport, "true", "'$(Configuration)' == 'Debug'"));
                return args;
            }
    
        ''' <summary>
        ''' Override the OnPopulateArgument method to build a list of arguments from the input
        ''' configuration information.
        ''' </summary>
        Protected Overloads Overrides Function OnPopulateArguments() As IList(Of ContributorArgumentConfiguration)
            Dim args As New List(Of ContributorArgumentConfiguration)()
    
            ' Generate reports when in debug configuration 
            args.Add(New ContributorArgumentConfiguration(GenerateUpdateReport, "true", "'$(Configuration)' == 'Debug'"))
            Return args
        End Function
    

    ContributorArgumentConfiguration 개체를 빌드하여 인수 목록에 추가합니다. 기본적으로 디버그 빌드를 생성할 때 보고서가 생성됩니다.

    다음에는 OnExecute 메서드를 재정의하여 데이터베이스 프로젝트가 배포될 때 실행할 코드를 추가합니다.

OnExecute를 재정의하려면

  • DeploymentUpdateReportContributor 클래스에 다음 메서드를 추가합니다.

        /// <summary>
        /// Override the OnExecute method to perform actions when you execute the deployment plan for
        /// a database project.
        /// </summary>
            protected override void OnExecute(DeploymentPlanContributorContext context)
            {
                // determine whether the user specified a report is to be generated
                bool generateReport = false;
                string generateReportValue;
                if (context.Arguments.TryGetValue(GenerateUpdateReport, out generateReportValue) == false)
                {
                    // couldn't find the GenerateUpdateReport argument, so do not generate
                    generateReport = false;
                }
                else
                {
                    // GenerateUpdateReport argument was specified, try to parse the value
                    if (bool.TryParse(generateReportValue, out generateReport))
                    {
                        // if we end up here, the value for the argument was not valid.
                        // default is false, so do nothing.
                    }
                }
    
                if (generateReport == false)
                {
                    // if user does not want to generate a report, we are done
                    return;
                }
    
                // We will output to the same directory where the deployment script
                // is output or to the current directory
                string reportPrefix = context.Options.TargetDatabaseName;
                string reportPath;
                if (string.IsNullOrEmpty(context.DeploymentScriptPath))
                {
                    reportPath = Environment.CurrentDirectory;
                }
                else
                {
                    reportPath = Path.GetDirectoryName(context.DeploymentScriptPath);
                }
                FileInfo summaryReportFile = new FileInfo(Path.Combine(reportPath, reportPrefix + ".summary.xml"));
                FileInfo detailsReportFile = new FileInfo(Path.Combine(reportPath, reportPrefix + ".details.xml"));
    
                // Generate the reports by using the helper class DeploymentReportWriter
                DeploymentReportWriter writer = new DeploymentReportWriter(context);
                writer.WriteReport(summaryReportFile);
                writer.IncludeScripts = true;
                writer.WriteReport(detailsReportFile);
    
                string msg = "Deployment reports ->"
                    + Environment.NewLine + summaryReportFile.FullName
                    + Environment.NewLine + detailsReportFile.FullName;
    
                DataSchemaError reportMsg = new DataSchemaError(msg, ErrorSeverity.Message);
                base.PublishMessage(reportMsg);
            }
    
        ''' <summary>
        ''' Override the OnExecute method to perform actions when you execute the deployment plan for
        ''' a database project.
        ''' </summary>
        Protected Overloads Overrides Sub OnExecute(ByVal context As DeploymentPlanContributorContext)
            ' output the names and values for any provided arguments 
            For Each arg As KeyValuePair(Of String, String) In context.Arguments
                Dim argMsg As New DataSchemaError((arg.Key & "=") + arg.Value, ErrorSeverity.Message)
                Me.PublishMessage(argMsg)
            Next
            ' determine whether the user specified a report is to be generated 
            Dim generateReport As Boolean = False
            Dim generateReportValue As String
            If context.Arguments.TryGetValue(GenerateUpdateReport, generateReportValue) = False Then
                ' couldn't find the GenerateUpdateReport argument, so do not generate 
                generateReport = False
            Else
                ' GenerateUpdateReport argument was specified, try to parse the value 
                If Boolean.TryParse(generateReportValue, generateReport) Then
                    ' if we end up here, the value for the argument was not valid. 
                    ' default is false, so do nothing. 
                End If
            End If
    
            If generateReport = False Then
                ' if user does not want to generate a report, we are done 
                Exit Sub
            End If
    
            ' We will output to the same directory where the deployment script 
            ' is output or to the current directory 
            Dim reportPrefix As String = context.Options.TargetDatabaseName
            Dim reportPath As String
            If String.IsNullOrEmpty(context.DeploymentScriptPath) Then
                reportPath = Environment.CurrentDirectory
            Else
                reportPath = Path.GetDirectoryName(context.DeploymentScriptPath)
            End If
            Dim summaryReportFile As New FileInfo(Path.Combine(reportPath, reportPrefix & ".summary.xml"))
            Dim detailsReportFile As New FileInfo(Path.Combine(reportPath, reportPrefix & ".details.xml"))
    
            ' Generate the reports by using the helper class DeploymentReportWriter 
            Dim writer As New DeploymentReportWriter(context)
            writer.WriteReport(summaryReportFile)
            writer.IncludeScripts = True
            writer.WriteReport(detailsReportFile)
    
            Dim msg As String = ("Deployment reports ->" & Environment.NewLine) + summaryReportFile.FullName + Environment.NewLine + detailsReportFile.FullName
    
            Dim reportMsg As New DataSchemaError(msg, ErrorSeverity.Message)
            MyBase.PublishMessage(reportMsg)
        End Sub
    

    OnExecute 메서드에는 지정된 인수에 대한 액세스, 소스 및 대상 데이터베이스 모델, 빌드 속성, 확장 파일을 제공하는 DeploymentPlanContributorContext 개체가 전달됩니다. 이 예제에서는 모델을 가져온 다음 도우미 함수를 호출하여 모델에 대한 정보를 출력합니다. 이 메서드에는 발생하는 오류를 보고하는 데 사용할 ErrorManager가 전달됩니다.

    주목할 추가 형식 및 메서드로는 DataSchemaModel, ModelStore, ModelComparisonResult, DatabaseSchemaProvider, DeploymentPlanHandleSchemaDeploymentOptions가 있습니다.

    다음에는 배포 계획을 세부적으로 조사하는 도우미 클래스를 정의합니다.

보고서 본문을 생성하는 도우미 클래스를 추가하려면

  • 먼저 다음 코드를 추가하여 도우미 클래스와 해당 메서드에 대한 기본 구조를 추가합니다.

            /// <summary>
            /// This class is used to generate a deployment
            /// report. 
            /// </summary>
            private class DeploymentReportWriter
            {
                /// <summary>
                /// The constructor accepts the same context info
                /// that was passed to the OnExecute method of the
                /// deployment contributor.
                /// </summary>
                public DeploymentReportWriter(DeploymentPlanContributorContext context)
                {
               }
                /// <summary>
                /// Property indicating whether script bodies
                /// should be included in the report.
                /// </summary>
                public bool IncludeScripts { get; set; }
    
                /// <summary>
                /// Drives the report generation, opening files, 
                /// writing the beginning and ending report elements,
                /// and calling helper methods to report on the
                /// plan operations.
                /// </summary>
                internal void WriteReport(FileInfo reportFile)
                {
                }
    
                /// <summary>
                /// Writes details for the various operation types
                /// that could be contained in the deployment plan.
                /// Optionally writes script bodies, depending on
                /// the value of the IncludeScripts property.
                /// </summary>
                private void ReportPlanOperations(XmlWriter xmlw)
                {
                }
    
                /// <summary>
                /// Returns the category of the specified element
                /// in the source model
                /// </summary>
                private string GetElementCategory(IModelElement element)
                {
                }
    
                /// <summary>
                /// Returns the name of the specified element
                /// in the source model
                /// </summary>
                private string GetElementName(IModelElement element)
                {
                }
            }
    
    ''' <summary>
    ''' This class is used to generate a deployment
    ''' report. 
    ''' </summary>
    Private Class DeploymentReportWriter
    
        Public Sub New(ByVal context As DeploymentPlanContributorContext)
        End Sub
    
        Private _includeScripts As Boolean
        ''' <summary>
        ''' Property indicating whether script bodies
        ''' should be included in the report.
        ''' </summary>
        Public Property IncludeScripts() As Boolean
            Get
                IncludeScripts = _includeScripts
            End Get
            Set(ByVal value As Boolean)
                _includeScripts = value
            End Set
        End Property
    
    
        ''' <summary> 
        ''' Drives the report generation, opening files, 
        ''' writing the beginning and ending report elements, 
        ''' and calling helper methods to report on the 
        ''' plan operations. 
        ''' </summary> 
        Friend Sub WriteReport(ByVal reportFile As FileInfo)
        End Sub
    
        ''' <summary> 
        ''' Writes details for the various operation types 
        ''' that could be contained in the deployment plan. 
        ''' Optionally writes script bodies, depending on 
        ''' the value of the IncludeScripts property. 
        ''' </summary> 
        Private Sub ReportPlanOperations(ByVal xmlw As XmlWriter)
        End Sub
        ''' <summary>
        ''' Returns the category of the specified element
        ''' in the source model
        ''' </summary> 
        Private Function GetElementCategory(ByVal element As IModelElement) As String
            Return ""
        End Function
    
        ''' <summary>
        ''' Returns the name of the specified element
        ''' in the source model
        ''' </summary>
        Private Function GetElementName(ByVal element As IModelElement) As String
            Return ""
        End Function
    End Class
    
  • 변경 내용을 Class1.cs에 저장합니다.

    그런 다음 클래스 멤버와 메서드 본문을 추가합니다.

클래스 멤버를 추가하려면

  • 코드 편집기에서 DeploymentReportWriter 클래스에 다음 코드를 추가합니다.

                readonly DataSchemaModel _sourceModel;
                readonly ModelComparisonResult _diff;
                readonly DeploymentStep _planHead;
    
            ReadOnly _sourceModel As DataSchemaModel
            ReadOnly _diff As ModelComparisonResult
            ReadOnly _planHead As DeploymentStep
    

    주목할 형식으로는 DataSchemaModel, ModelComparisonResultDeploymentStep이 있습니다.

    다음에는 클래스 생성자에 본문을 추가합니다.

생성자에 메서드 본문을 추가하려면

  • 다음 코드를 생성자의 본문으로 추가합니다.

                    if (context == null)
                    {
                        throw new ArgumentNullException("context");
                    }
    
                    // save the source model, source/target differences,
                    // and the beginning of the deployment plan.
                    _sourceModel = context.Source;
                    _diff = context.ComparisonResult;
                    _planHead = context.PlanHandle.Head;
    
                If context Is Nothing Then
                    Throw New ArgumentNullException("context")
                End If
    
                ' save the source model, source/target differences, 
                ' and the beginning of the deployment plan. 
                _sourceModel = context.Source
                _diff = context.ComparisonResult
                _planHead = context.PlanHandle.Head
    

    다음에는 WriteReport 메서드에 메서드 본문을 추가합니다.

WriteReport 메서드에 메서드 본문을 추가하려면

  • 다음 코드를 WriteReport 메서드의 본문으로 추가합니다.

                    // Assumes that we have a valid report file
                    if (reportFile == null)
                    {
                        throw new ArgumentNullException("reportFile");
                    }
    
                    // set up the XML writer
                    XmlWriterSettings xmlws = new XmlWriterSettings();
                    // Indentation makes it a bit more readable
                    xmlws.Indent = true;
                    FileStream fs = new FileStream(reportFile.FullName, FileMode.Create, FileAccess.Write, FileShare.ReadWrite);
                    XmlWriter xmlw = XmlWriter.Create(fs, xmlws);
    
                    try
                    {
                        xmlw.WriteStartDocument(true);
                        xmlw.WriteStartElement("DeploymentReport");
    
                        // Summary report of the operations that
                        // are contained in the plan.
                        ReportPlanOperations(xmlw);
    
                        // You could add a method call here
                        // to produce a detailed listing of the 
                        // differences between the source and
                        // target model.
                        xmlw.WriteEndElement();
                        xmlw.WriteEndDocument();
                        xmlw.Flush();
                        fs.Flush();
                    }
                    finally
                    {
                        xmlw.Close();
                        fs.Dispose();
                    }
    
                ' Assumes that we have a valid report file 
                If reportFile Is Nothing Then
                    Throw New ArgumentNullException("reportFile")
                End If
    
                ' set up the XML writer 
                Dim xmlws As New XmlWriterSettings()
                ' Indentation makes it a bit more readable 
                xmlws.Indent = True
                Dim fs As New FileStream(reportFile.FullName, FileMode.Create, FileAccess.Write, FileShare.ReadWrite)
                Dim xmlw As XmlWriter = XmlWriter.Create(fs, xmlws)
    
                Try
                    xmlw.WriteStartDocument(True)
                    xmlw.WriteStartElement("DeploymentReport")
    
                    ' Summary report of the operations that 
                    ' are contained in the plan. 
                    ReportPlanOperations(xmlw)
    
                    ' You could add a method call here 
                    ' to produce a detailed listing of the 
                    ' differences between the source and 
                    ' target model. 
                    xmlw.WriteEndElement()
                    xmlw.WriteEndDocument()
                    xmlw.Flush()
                    fs.Flush()
                Finally
                    xmlw.Close()
                    fs.Dispose()
                End Try
    

    주목할 형식으로는 XmlWriterXmlWriterSettings가 있습니다.

    다음에는 ReportPlanOperations 메서드에 본문을 추가합니다.

ReportPlanOperations 메서드에 메서드 본문을 추가하려면

  • 다음 코드를 ReportPlanOperations 메서드의 본문으로 추가합니다.

                    // write the node to indicate the start
                    // of the list of operations.
                    xmlw.WriteStartElement("Operations");
    
                    // Loop through the steps in the plan,
                    // starting at the beginning.
                    DeploymentStep currentStep = _planHead;
                    while (currentStep != null)
                    {
                        // Report the type of step
                        xmlw.WriteStartElement(currentStep.GetType().Name);
    
                        // based on the type of step, report
                        // the relevant information.
                        // Note that this procedure only handles 
                        // a subset of all step types.
                        if (currentStep is SqlRenameStep)
                        {
                            SqlRenameStep renameStep = (SqlRenameStep)currentStep;
                            xmlw.WriteAttributeString("OriginalName", renameStep.OldName);
                            xmlw.WriteAttributeString("NewName", renameStep.NewName);
                            xmlw.WriteAttributeString("Category", GetElementCategory(renameStep.RenamedElement));
                        }
                        else if (currentStep is SqlMoveSchemaStep)
                        {
                            SqlMoveSchemaStep moveStep = (SqlMoveSchemaStep)currentStep;
                            xmlw.WriteAttributeString("OrignalName", moveStep.PreviousName);
                            xmlw.WriteAttributeString("NewSchema", moveStep.NewSchema);
                            xmlw.WriteAttributeString("Category", GetElementCategory(moveStep.MovedElement));
                        }
                        else if (currentStep is SqlTableMigrationStep)
                        {
                            SqlTableMigrationStep dmStep = (SqlTableMigrationStep)currentStep;
                            xmlw.WriteAttributeString("Name", GetElementName(dmStep.SourceTable));
                            xmlw.WriteAttributeString("Category", GetElementCategory(dmStep.SourceElement));
                        }
                        else if (currentStep is CreateElementStep)
                        {
                            CreateElementStep createStep = (CreateElementStep)currentStep;
                            xmlw.WriteAttributeString("Name", GetElementName(createStep.SourceElement));
                            xmlw.WriteAttributeString("Category", GetElementCategory(createStep.SourceElement));
                        }
                        else if (currentStep is AlterElementStep)
                        {
                            AlterElementStep alterStep = (AlterElementStep)currentStep;
                            xmlw.WriteAttributeString("Name", GetElementName(alterStep.SourceElement));
                            xmlw.WriteAttributeString("Category", GetElementCategory(alterStep.SourceElement));
                        }
                        else if (currentStep is DropElementStep)
                        {
                            DropElementStep dropStep = (DropElementStep)currentStep;
                            xmlw.WriteAttributeString("Name", GetElementName(dropStep.TargetElement));
                            xmlw.WriteAttributeString("Category", GetElementCategory(dropStep.TargetElement));
                        }
    
                        // If the script bodies are to be included,
                        // add them to the report.
                        if (this.IncludeScripts)
                        {
                            string tsqlBody = currentStep.Action();
                            if (string.IsNullOrEmpty(tsqlBody) == false)
                            {
                                xmlw.WriteCData(tsqlBody);
                            }
                        }
    
                        // close off the current step
                        xmlw.WriteEndElement();
                        currentStep = currentStep.Next;
                    }
                    xmlw.WriteEndElement();
    
                ' write the node to indicate the start 
                ' of the list of operations. 
                xmlw.WriteStartElement("Operations")
    
                ' Loop through the steps in the plan, 
                ' starting at the beginning. 
                Dim currentStep As DeploymentStep = _planHead
                While currentStep IsNot Nothing
                    ' Report the type of step 
                    xmlw.WriteStartElement(currentStep.[GetType]().Name)
    
                    ' based on the type of step, report 
                    ' the relevant information. 
                    If TypeOf currentStep Is SqlRenameStep Then
                        Dim renameStep As SqlRenameStep = DirectCast(currentStep, SqlRenameStep)
                        xmlw.WriteAttributeString("OriginalName", renameStep.OldName)
                        xmlw.WriteAttributeString("NewName", renameStep.NewName)
                        xmlw.WriteAttributeString("Category", GetElementCategory(renameStep.RenamedElement))
                    ElseIf TypeOf currentStep Is SqlMoveSchemaStep Then
                        Dim moveStep As SqlMoveSchemaStep = DirectCast(currentStep, SqlMoveSchemaStep)
                        xmlw.WriteAttributeString("OrignalName", moveStep.PreviousName)
                        xmlw.WriteAttributeString("NewSchema", moveStep.NewSchema)
                        xmlw.WriteAttributeString("Category", GetElementCategory(moveStep.MovedElement))
                    ElseIf TypeOf currentStep Is SqlTableMigrationStep Then
                        Dim dmStep As SqlTableMigrationStep = DirectCast(currentStep, SqlTableMigrationStep)
                        xmlw.WriteAttributeString("Name", GetElementName(dmStep.SourceTable))
                        xmlw.WriteAttributeString("Category", GetElementCategory(dmStep.SourceElement))
                    ElseIf TypeOf currentStep Is CreateElementStep Then
                        Dim createStep As CreateElementStep = DirectCast(currentStep, CreateElementStep)
                        xmlw.WriteAttributeString("Name", GetElementName(createStep.SourceElement))
                        xmlw.WriteAttributeString("Category", GetElementCategory(createStep.SourceElement))
                    ElseIf TypeOf currentStep Is AlterElementStep Then
                        Dim alterStep As AlterElementStep = DirectCast(currentStep, AlterElementStep)
                        xmlw.WriteAttributeString("Name", GetElementName(alterStep.SourceElement))
                        xmlw.WriteAttributeString("Category", GetElementCategory(alterStep.SourceElement))
                    ElseIf TypeOf currentStep Is DropElementStep Then
                        Dim dropStep As DropElementStep = DirectCast(currentStep, DropElementStep)
                        xmlw.WriteAttributeString("Name", GetElementName(dropStep.TargetElement))
                        xmlw.WriteAttributeString("Category", GetElementCategory(dropStep.TargetElement))
                    End If
    
                    ' If the script bodies are to be included, 
                    ' add them to the report. 
                    If Me.IncludeScripts Then
                        Dim tsqlBody As String = currentStep.Action()
                        If String.IsNullOrEmpty(tsqlBody) = False Then
                            xmlw.WriteCData(tsqlBody)
                        End If
                    End If
    
                    ' close off the current step 
                    xmlw.WriteEndElement()
                    currentStep = currentStep.[Next]
                End While
                xmlw.WriteEndElement()
    

    주목할 형식으로는 DeploymentStep, SqlRenameStep, SqlMoveSchemaStep, SqlTableMigrationStep, CreateElementStep, AlterElementStepDropElementStep이 있습니다. 이 예제에 표시되지 않은 추가 단계 형식으로는 BeginPostDeploymentScriptStep, BeginPreDeploymentScriptStep, DeploymentScriptDomStep, DeploymentScriptStep, EndPostDeploymentScriptStepEndPreDeploymentScriptStep이 있습니다. SQL Server 관련 단계인 SqlBeginAltersStep, SqlBeginDropsStep, SqlBeginPreservationStep, SqlBeginTransactionStep, SqlEndAltersStep, SqlEndDropsStep, SqlEndPreservationStep, SqlEndTransactionStep, SqlFinalizeDatabaseAccessStep, SqlMoveSchemaStep, SqlPrintStep, SqlRenameStepSqlTableMigrationStep을 찾을 수도 있습니다.

    다음에는 GetElementCategory 메서드의 본문을 추가합니다.

GetElementCategory 메서드에 메서드 본문을 추가하려면

  • 다음 코드를 GetElementCategory 메서드의 본문으로 추가합니다.

                    return _sourceModel.DatabaseSchemaProvider.UserInteractionServices.GetElementTypeDescription(
                        element.ElementClass);
    
                Return _sourceModel.DatabaseSchemaProvider.UserInteractionServices.GetElementTypeDescription(element.ElementClass)
    

    주목할 형식 및 메서드로는 DataSchemaModel, DatabaseSchemaProvider, UserInteractionServicesGetElementTypeDescription이 있습니다.

    다음에는 GetElementName 메서드의 본문을 추가합니다.

GetElementName 메서드에 메서드 본문을 추가하려면

  • 다음 코드를 GetElementName 메서드의 본문으로 추가합니다.

                    return _sourceModel.DatabaseSchemaProvider.UserInteractionServices.GetElementName(
                        element, 
                        ElementNameStyle.FullyQualifiedName);
    
                Return _sourceModel.DatabaseSchemaProvider.UserInteractionServices.GetElementName(element, ElementNameStyle.FullyQualifiedName)
    

    주목할 형식 및 메서드로는 DataSchemaModel, DatabaseSchemaProvider, UserInteractionServices, GetElementNameElementNameStyle이 있습니다.

    변경 내용을 클래스에 저장합니다. 다음에는 클래스 라이브러리를 빌드합니다.

어셈블리에 서명하고 빌드하려면

  1. 프로젝트 메뉴에서 MyDeploymentContributor 속성을 클릭합니다.

  2. 서명 탭을 클릭합니다.

  3. 어셈블리 서명을 클릭합니다.

  4. 강력한 이름 키 파일 선택에서 **<새로 만들기>**를 클릭합니다.

  5. 강력한 이름 키 만들기 대화 상자의 키 파일 이름에 MyRefKey를 입력합니다.

  6. (옵션) 강력한 이름 키 파일의 암호를 지정할 수 있습니다.

  7. 확인을 클릭합니다.

  8. 파일 메뉴에서 모두 저장을 클릭합니다.

  9. 빌드 메뉴에서 솔루션 빌드를 클릭합니다.

    다음에는 데이터베이스 프로젝트를 배포할 때 어셈블리가 로드되도록 어셈블리를 설치하고 등록해야 합니다.

배포 참가자 설치

배포 참가자를 설치하려면 다음 작업을 수행해야 합니다.

  • 어셈블리 및 연결된 .pdb 파일을 Extensions 폴더에 복사합니다.

  • 데이터베이스 프로젝트를 배포할 때 배포 참가자가 로드되도록 이를 등록하는 Extensions.xml 파일을 만듭니다.

MyDeploymentContributor 어셈블리를 설치하려면

  1. %Program Files%\Microsoft Visual Studio 10.0\VSTSDB\Extensions 폴더에 MyExtensions라는 폴더를 만듭니다.

  2. 서명된 어셈블리(MyDeploymentContributor.dll) 및 연관된 .pdb 파일(MyDeploymentContributor.pdb)을 %Program Files%\Microsoft Visual Studio 10.0\VSTSDB\Extensions\MyExtensions 폴더에 복사합니다.

    참고

    XML 파일을 %Program Files%\Microsoft Visual Studio 10.0\VSTSDB\Extensions 폴더에 바로 복사하지 않는 것이 좋습니다. 대신 하위 폴더를 사용하면 Visual Studio Premium에서 제공된 다른 파일을 실수로 변경하는 것을 방지할 수 있습니다.

    다음에는 기능 확장의 한 형식인 어셈블리가 Visual Studio Premium에 표시되도록 이를 등록해야 합니다.

MyDeploymentContributor 어셈블리를 등록하려면

  1. 보기 메뉴에서 다른 창, 명령 창을 차례로 클릭하여 명령 창을 엽니다.

  2. 명령 창에서 다음 코드를 입력합니다. FilePath 대신 컴파일된 .dll 파일의 경로 및 파일 이름을 입력합니다. 이때 경로 및 파일 이름을 따옴표로 묶습니다.

    참고

    기본적으로 컴파일된 .dll 파일의 경로는 YourSolutionPath\bin\Debug 또는 YourSolutionPath\bin\Release입니다.

    ? System.Reflection.Assembly.LoadFrom(@"FilePath").FullName
    
    ? System.Reflection.Assembly.LoadFrom("FilePath").FullName
    
  3. Enter 키를 누릅니다.

  4. 결과 줄을 클립보드로 복사합니다. 이 줄은 다음과 같습니다.

    "MyDeploymentContributor, Version=1.0.0.0, Culture=neutral, PublicKeyToken=nnnnnnnnnnnnnnnn"
    
  5. 메모장과 같은 일반 텍스트 편집기를 엽니다.

    중요

    Windows Vista 및 Microsoft Windows Server 2008에서 Program Files 폴더에 파일을 저장할 수 있도록 관리자 권한으로 편집기를 엽니다.

  6. 사용자 어셈블리 이름, 공개 키 토큰 및 확장 형식을 지정하여 다음 정보를 제공합니다.

    <?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="MyDeploymentContributor.DeploymentUpdateReportContributor" 
    assembly="MyDeploymentContributor, Version=1.0.0.0, Culture=neutral, PublicKeyToken=<enter key here>" enabled="true" />
    </extensions>
    

    이 XML 파일을 사용하여 DeploymentPlanExecutor에서 상속하는 클래스를 등록합니다.

  7. %Program Files%\Microsoft Visual Studio 10.0\VSTSDB\Extensions\MyExtensions 폴더에 MyDeploymentContributor.extensions.xml로 파일을 저장합니다.

  8. Visual Studio를 닫습니다.

    다음에는 데이터베이스 프로젝트를 배포하여 참가자를 테스트합니다.

배포 참가자 테스트

배포 참가자를 테스트하려면 다음 작업을 수행해야 합니다.

  • 배포할 .dbproj 파일에 속성을 추가합니다.

  • MSBuild를 사용하고 적절한 매개 변수를 제공하여 데이터베이스 프로젝트를 배포합니다.

데이터베이스 프로젝트 파일(.dbproj)에 속성 추가

MSBuild에서 이 배포 참가자를 사용하려면 사용자가 MSBuild를 통해 매개 변수를 전달할 수 있도록 데이터베이스 프로젝트를 수정해야 합니다. 데이터베이스 프로젝트를 업데이트하려면 원하는 편집기에서 해당 프로젝트를 열고 .dbproj 파일에서 마지막 </ItemGroup> 노드와 </Project> 노드 사이에 다음 문을 추가합니다.

  <ItemGroup>
    <DeploymentContributorArgument Include="GenerateUpdateReport=$(GenerateUpdateReport)" />
  </ItemGroup>

.dbproj 파일을 업데이트한 후에는 MSBuild를 사용하여 명령줄 빌드를 위한 매개 변수를 전달할 수 있습니다.

데이터베이스 프로젝트를 배포합니다.

데이터베이스 프로젝트를 배포하고 배포 보고서를 생성하려면

  1. Visual Studio 명령 프롬프트를 엽니다. 시작 메뉴에서 모든 프로그램, Microsoft Visual Studio 2010, Visual Studio Tools, **Visual Studio 명령 프롬프트(2010)**를 차례로 클릭합니다.

  2. 명령 프롬프트에서 사용자 데이터베이스 프로젝트가 포함된 폴더로 이동합니다.

  3. 명령 프롬프트에서 다음 명령줄을 입력합니다.

    MSBuild /t:Rebuild MyDatabaseProject.dbproj /p:OutDir=.\
    

    MyDatabaseProject를 빌드할 데이터베이스 프로젝트의 이름으로 바꿉니다. 프로젝트를 마지막으로 빌드한 후 변경한 적이 있으면 /t:Rebuild 대신 /t:Build를 사용할 수 있습니다.

  4. 명령 프롬프트에서 다음 명령줄을 입력합니다.

    MSBuild /t:Deploy MyDatabaseProject.dbproj /p:GenerateUpdateReport=true
    

    MyDatabaseProject를 배포할 데이터베이스 프로젝트의 이름으로 바꿉니다.

    다음과 같은 출력이 나타납니다.

Microsoft (R) Build Engine Version 4.0.20817.0
[Microsoft .NET Framework, Version 4.0.20817.0]
Copyright (C) Microsoft Corporation 2007. All rights reserved.

Build started 8/26/2009 3:12:43 PM.
Project "C:\Users\UserName\Documents\Visual Studio 2010\Projects\MyDatabaseProject\Dep
TestToo\MyDatabaseProject.dbproj" on node 1 (Deploy target(s)).
DspDeploy:
  GenerateUpdateReport=true

  Deployment reports ->
  C:\Users\UserName\Documents\Visual Studio 2010\Projects\MyDatabaseProject\MyDatabaseProject\sql\debug\MyTargetDatabase.summary.xml
  C:\Users\UserName\Documents\Visual Studio 2010\Projects\MyDatabaseProject\MyDatabaseProject\sql\debug\MyTargetDatabase.details.xml

  Deployment script generated to:
  C:\Users\UserName\Documents\Visual Studio 2010\Projects\MyDatabaseProject\MyDatabaseProject\sql\debug\MyDatabaseProject.sql

Done Building Project "C:\Users\UserName\Documents\Visual Studio 2010\Projects\MyDatabaseProject\MyDatabaseProject\MyDatabaseProject.dbproj" (Deploy target(s)).


Build succeeded.
    0 Warning(s)
    0 Error(s)

Time Elapsed 00:00:04.02
  1. MyTargetDatabase.summary.xml을 열고 내용을 검사합니다.

    이 파일은 새 데이터베이스 배포를 보여 주는 다음 예제와 유사합니다.

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<DeploymentReport>
  <Operations>
    <DeploymentScriptStep />
    <DeploymentScriptDomStep />
    <DeploymentScriptStep />
    <DeploymentScriptDomStep />
    <DeploymentScriptStep />
    <DeploymentScriptStep />
    <DeploymentScriptStep />
    <DeploymentScriptStep />
    <DeploymentScriptDomStep />
    <DeploymentScriptDomStep />
    <DeploymentScriptDomStep />
    <DeploymentScriptDomStep />
    <DeploymentScriptStep />
    <DeploymentScriptDomStep />
    <BeginPreDeploymentScriptStep />
    <DeploymentScriptStep />
    <EndPreDeploymentScriptStep />
    <SqlBeginPreservationStep />
    <SqlEndPreservationStep />
    <SqlBeginDropsStep />
    <SqlEndDropsStep />
    <SqlBeginAltersStep />
    <SqlPrintStep />
    <CreateElementStep Name="Sales" Category="Schema" />
    <SqlPrintStep />
    <CreateElementStep Name="Sales.Customer" Category="Table" />
    <SqlPrintStep />
    <CreateElementStep Name="Sales.PK_Customer_CustID" Category="Primary Key" />
    <SqlPrintStep />
    <CreateElementStep Name="Sales.Orders" Category="Table" />
    <SqlPrintStep />
    <CreateElementStep Name="Sales.PK_Orders_OrderID" Category="Primary Key" />
    <SqlPrintStep />
    <CreateElementStep Name="Sales.Def_Customer_YTDOrders" Category="Default Constraint" />
    <SqlPrintStep />
    <CreateElementStep Name="Sales.Def_Customer_YTDSales" Category="Default Constraint" />
    <SqlPrintStep />
    <CreateElementStep Name="Sales.Def_Orders_OrderDate" Category="Default Constraint" />
    <SqlPrintStep />
    <CreateElementStep Name="Sales.Def_Orders_Status" Category="Default Constraint" />
    <SqlPrintStep />
    <CreateElementStep Name="Sales.FK_Orders_Customer_CustID" Category="Foreign Key" />
    <SqlPrintStep />
    <CreateElementStep Name="Sales.CK_Orders_FilledDate" Category="Check Constraint" />
    <SqlPrintStep />
    <CreateElementStep Name="Sales.CK_Orders_OrderDate" Category="Check Constraint" />
    <SqlPrintStep />
    <CreateElementStep Name="Sales.uspCancelOrder" Category="Procedure" />
    <SqlPrintStep />
    <CreateElementStep Name="Sales.uspFillOrder" Category="Procedure" />
    <SqlPrintStep />
    <CreateElementStep Name="Sales.uspNewCustomer" Category="Procedure" />
    <SqlPrintStep />
    <CreateElementStep Name="Sales.uspPlaceNewOrder" Category="Procedure" />
    <SqlPrintStep />
    <CreateElementStep Name="Sales.uspShowOrderDetails" Category="Procedure" />
    <SqlEndAltersStep />
    <DeploymentScriptStep />
    <BeginPostDeploymentScriptStep />
    <DeploymentScriptStep />
    <EndPostDeploymentScriptStep />
    <DeploymentScriptDomStep />
    <DeploymentScriptDomStep />
    <DeploymentScriptDomStep />
  </Operations>
</DeploymentReport>

참고

대상 데이터베이스와 동일한 데이터베이스 프로젝트를 배포하면 결과 보고서에서 그다지 의미 있는 정보를 얻을 수 없습니다. 의미 있는 결과를 얻으려면 데이터베이스에 대한 변경 내용을 배포하거나 새 데이터베이스를 배포합니다.

  1. MyTargetDatabase.details.xml을 열고 내용을 검사합니다.

    세부 정보 파일의 작은 섹션에 Sales 스키마를 만들고, 테이블 만들기에 대한 메시지를 인쇄하고, 테이블을 만드는 항목과 스크립트가 표시됩니다.

    <CreateElementStep Name="Sales" Category="Schema"><![CDATA[CREATE SCHEMA [Sales]
    AUTHORIZATION [dbo];

]]></CreateElementStep>
    <SqlPrintStep><![CDATA[PRINT N'Creating [Sales].[Customer]...';

]]></SqlPrintStep>
    <CreateElementStep Name="Sales.Customer" Category="Table"><![CDATA[CREATE TABLE [Sales].[Customer] (
    [CustomerID]   INT           IDENTITY (1, 1) NOT NULL,
    [CustomerName] NVARCHAR (40) NOT NULL,
    [YTDOrders]    INT           NOT NULL,
    [YTDSales]     INT           NOT NULL
);

]]></CreateElementStep>

배포 계획이 실행될 때 이를 분석하면 배포에 포함된 정보에 대한 보고서를 생성하고 해당 계획의 단계에 따라 추가 작업을 수행할 수 있습니다.

다음 단계

출력 XML 파일에 대한 처리를 수행하는 추가 도구를 만들 수도 있습니다. 이 항목에서는 DeploymentPlanExecutor의 한 예를 보여 줄 뿐입니다. 배포 계획이 실행될 때 이를 변경하는 DeploymentPlanModifier를 만들 수도 있습니다.

참고 항목

개념

Visual Studio의 데이터베이스 기능 확장

기타 리소스

빌드 및 배포 참가자를 사용하여 데이터베이스 빌드 및 배포 사용자 지정

연습: 데이터베이스 프로젝트 빌드를 확장하여 모델 통계 생성

연습: 데이터베이스 프로젝트 배포를 확장하여 배포 계획 수정