Share via


Exemplarische Vorgehensweise: Erweitern der Bereitstellung von Datenbankprojekten zum Ändern des Bereitstellungsplans

Sie können Bereitstellungscontributors erstellen, damit beim Bereitstellen eines Datenbankprojekts benutzerdefinierte Aktionen ausgeführt werden. Sie können entweder einen DeploymentPlanModifier oder einen DeploymentPlanExecutor erstellen. Ein DeploymentPlanModifier dient zum Ändern des Plans vor dessen Ausführung. Ein DeploymentPlanExecutor dient zum Ausführen von Vorgängen während der Planausführung. In dieser exemplarischen Vorgehensweise wird ein DeploymentPlanModifier mit dem Namen "SqlRestartableScriptContributor" erstellt, um den Batches im Bereitstellungsskript IF-Anweisungen hinzuzufügen, damit das Skript bei Auftreten eines Fehlers während der Ausführung erneut ausgeführt werden kann, bis die Batches abgeschlossen sind.

Im Verlauf dieser exemplarischen Vorgehensweise werden die folgenden Hauptaufgaben ausgeführt:

  • Erstellen eines Bereitstellungscontributors vom Typ "DeploymentPlanModifier"

  • Installieren des Bereitstellungscontributors

  • Testen des Bereitstellungscontributors

Vorbereitungsmaßnahmen

Zum Durchführen dieser exemplarischen Vorgehensweise benötigen Sie die folgenden Komponenten:

  • Installation von Visual Studio 2010 Premium oder Visual Studio 2010 Ultimate auf dem Computer

  • Ein Datenbankprojekt mit Datenbankobjekten

  • Eine Instanz von SQL Server, für die ein Datenbankprojekt bereitgestellt werden kann

Tipp

Diese exemplarische Vorgehensweise richtet sich an Benutzer, die bereits mit den Datenbankfunktionen von Visual Studio vertraut sind. Sie sollten außerdem mit grundlegenden Konzepten von Visual Studio wie dem Erstellen einer Klassenbibliothek und dem Verwenden des Code-Editors zum Hinzufügen von Code zu einer Klasse vertraut sein.

Erstellen eines Bereitstellungscontributors

Führen Sie zum Erstellen eines Bereitstellungscontributors die folgenden Aufgaben aus:

  • Erstellen eines Klassenbibliothekprojekts und Hinzufügen erforderlicher Verweise

  • Definieren einer Klasse mit dem Namen "SqlRestartableScriptContributor" und Vererbung von DeploymentPlanModifier

  • Überschreiben der OnExecute-Methode

  • Hinzufügen privater Hilfsmethoden

  • Erstellen der resultierenden Assembly

So erstellen Sie ein Klassenbibliotheksprojekt

  1. Erstellen Sie ein Visual C#- oder ein Visual Basic-Klassenbibliothekprojekt mit dem Namen "MyOtherDeploymentContributor".

  2. Klicken Sie im Projektmappen-Explorer mit der rechten Maustaste auf das Projekt, und klicken Sie anschließend auf Verweis hinzufügen.

  3. Klicken Sie auf die Registerkarte .NET.

  4. Markieren Sie die Einträge Microsoft.Data.Schema, Microsoft.Data.Schema.Sql, Microsoft.Data.Schema.ScriptDom und Microsoft.Data.Schema.ScriptDom.Sql, und klicken Sie auf OK.

    Fügen Sie der Klasse nun Code hinzu.

So definieren Sie die SqlRestartableScriptContributor-Klasse

  1. Aktualisieren Sie im Code-Editor die Datei "class1.cs" mit den folgenden using-Anweisungen:

    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
    
  2. Aktualisieren Sie die Klassendefinition gemäß dem folgenden Beispiel:

            /// <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
    

    Sie haben nun den Buildcontributor definiert, der von DeploymentPlanModifier erbt. Mithilfe des DatabaseSchemaProviderCompatibilityAttribute-Attributs haben Sie angegeben, dass dieser Contributor mit jedem beliebigen Datenbankschema-Anbieter kompatibel ist, der von SqlDatabaseSchemaProvider erbt.

  3. Fügen Sie die folgenden Memberdeklarationen hinzu:

            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 & "]"
    

    Im nächsten Schritt wird die OnExecute-Methode überschrieben, um den Code hinzuzufügen, der beim Bereitstellen eines Datenbankprojekts ausgeführt werden soll.

So überschreiben Sie OnExecute

  1. Fügen Sie der SqlRestartableScriptContributor-Klasse die folgende Methode hinzu:

            /// <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
    

    Sie überschreiben die OnExecute-Methode aus der Basisklasse DeploymentPlanContributor; dies ist sowohl die Basisklasse für DeploymentPlanModifier als auch für DeploymentPlanExecutor. An die OnExecute-Methode wird ein DeploymentPlanContributorContext-Objekt übergeben, das den Zugriff auf beliebige angegebene Argumente, auf das Quell- und Zieldatenbankmodell, auf Buildeigenschaften sowie auf Erweiterungsdateien ermöglicht. In diesem Beispiel werden der Bereitstellungsplan und der Zieldatenbankname abgerufen.

  2. Fügen Sie der OnExecute-Methode nun den Anfang eines Texts hinzu:

            // 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)
    

    In diesem Code werden einige lokale Variablen definiert. Darüber hinaus wird die Schleife zum Verarbeiten aller Schritte des Bereitstellungsplans eingerichtet. Nach Abschluss der Schleife sind noch einige Nachverarbeitungsschritte erforderlich. Anschließend wird die temporäre Tabelle gelöscht, die während der Bereitstellung erstellt wurde, um den Status während der Planausführung zu verfolgen. Die wichtigsten Typen sind hierbei "DeploymentStep" und DeploymentScriptStep. Eine wichtige Methode ist AddAfter.

  3. Fügen Sie nun die zusätzliche Schrittverarbeitung hinzu, indem Sie den Kommentar "Add additional step processing here" ersetzen:

                // 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
    

    Die Codekommentare dienen zur Erläuterung der Verarbeitung. Von diesem Code wird auf hoher Ebene nach den Schritten gesucht, die Ihnen wichtig sind. Andere Schritte werden übersprungen. Bei Erreichen der Schritte nach der Bereitstellung wird der Vorgang angehalten. Sollte der Schritt Anweisungen enthalten, die in Bedingungen eingeschlossen werden müssen, wird eine zusätzliche Verarbeitung ausgeführt. Wichtige Typen, Methoden und Eigenschaften: BeginPreDeploymentScriptStep, BeginPostDeploymentScriptStep, IModelElement, TSqlScript, Script, DeploymentScriptDomStep und SqlPrintStep.

  4. Fügen Sie nun den Batchverarbeitungscode hinzu, indem Sie den Kommentar "Add batch processing here" ersetzen:

                        // 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
    

    Mit diesem Code wird eine IF-Anweisung zusammen mit einem BEGIN/END-Block erstellt. Die Anweisungen im Batch werden anschließend zusätzlich verarbeitet. Nach Abschluss dieses Vorgangs wird eine INSERT-Anweisung hinzugefügt, um der temporären Tabelle zum Verfolgen des Skriptausführungsstatus Informationen hinzuzufügen. Und schließlich wird der Batch aktualisiert, indem die normalerweise vorhandenen Anweisungen durch die neue IF-Anweisung ersetzt werden, in der diese Anweisungen enthalten sind.

    Wichtige Typen, Methoden und Eigenschaften: IfStatement, BeginEndBlockStatement, StatementList, TSqlBatch, PredicateSetStatement, SetOptions und InsertStatement.

  5. Fügen Sie nun den Text der Schleife für die Anweisungsverarbeitung hinzu. Ersetzen Sie den Kommentar "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)
    

    Nehmen Sie für jede Anweisung im Batch, bei der es sich um einen Typ handelt, der von einer sp_executesql-Anweisung umschlossen sein muss, eine entsprechende Änderung vor. Die Anweisung wird dann der Anweisungsliste für den erstellten BEGIN/END-Block hinzugefügt. Wichtige Typen, Methoden und Eigenschaften: TSqlStatement und ExecuteStatement.

  6. Fügen Sie schließlich den Abschnitt für die Nachverarbeitung ein, indem Sie den Kommentar "Add additional post-processing here" ersetzen:

                    // 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)
    

    Wenn bei der Verarbeitung Schritte gefunden wurden, die von einer Bedingungsanweisung umschlossen sind, müssen dem Bereitstellungsskript Anweisungen zum Definieren von SQLCMD-Variablen hinzugefügt werden. Die erste Variable ("CompletedBatches") enthält einen eindeutigen Namen für die temporäre Tabelle, mit der nachverfolgt wird, welche Batches bei der Skriptausführung erfolgreich abgeschlossen wurden. Die zweite Variable ("TotalBatchCount") enthält die Gesamtanzahl der Batches im Bereitstellungsskript.

    Weitere interessante Typen, Eigenschaften und Methoden:

    StringBuilder, DeploymentScriptStep und AddBefore.

    Im nächsten Schritt werden die von dieser Methode aufgerufenen Hilfsmethoden definiert.

So fügen Sie die CreateExecuteSql-Methode hinzu

  • Fügen Sie den folgenden Code hinzu, um die CreateExecuteSQL-Methode zu definieren, damit eine angegebene Anweisung mit einer EXEC sp_executesql-Anweisung umschlossen werden kann:

            /// <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
    

    Wichtige Typen, Methoden und Eigenschaften: ExecuteStatement, ExecutableProcedureReference, SchemaObjectName, ProcedureReference und ExecuteParameter.

So fügen Sie die CreateLiteral-Methode hinzu

  • Fügen Sie den folgenden Code hinzu, um die CreateLiteral-Methode zu definieren. Von dieser Methode wird auf Basis der angegebenen Zeichenfolge ein Literal-Objekt des angegebenen Typs erstellt:

            /// <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
    

    Wichtige Typen, Methoden und Eigenschaften: Literal und LiteralType.

So fügen Sie die CreateIdentifier-Methode hinzu

  • Fügen Sie den folgenden Code hinzu, um die CreateIdentifier-Methode zu definieren. Von dieser Methode wird auf Basis der angegebenen Zeichenfolge ein Identifier-Objekt mit dem angegebenen Anführungszeichentyp erstellt:

            /// <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
    

    Wichtige Typen, Methoden und Eigenschaften: Identifier und QuoteType.

So fügen Sie die CreateCompletedBatchesName-Methode hinzu

  • Fügen Sie den folgenden Code hinzu, um die CreateCompletedBatchesName-Methode zu definieren. Von dieser Methode wird der Name erstellt, der für einen Batch in die temporäre Tabelle eingefügt wird:

            /// <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
    

    Wichtige Typen, Methoden und Eigenschaften: SchemaObjectName.

So fügen Sie die IsStatementEscaped-Methode hinzu

  • Fügen Sie den folgenden Code hinzu, um die IsStatementEscaped-Methode zu definieren. Von dieser Methode wird bestimmt, ob die Anweisung aufgrund des Typs des Modellelements von einer EXEC sp_executesql-Anweisung umschlossen sein muss, damit sie in eine IF-Anweisung eingeschlossen werden kann:

            /// <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
    

    Wichtige Typen, Methoden und Eigenschaften: IModelElement, ISql90Schema, ISqlProcedure, ISqlView, ISqlFunction und ISqlTrigger.

So fügen Sie die CreateBatchCompleteInsert-Methode hinzu

  • Fügen Sie den folgenden Code hinzu, um die CreateBatchCompleteInsert-Methode zu definieren. Von dieser Methode wird die INSERT-Anweisung erstellt, die dem Bereitstellungsskript hinzugefügt wird, um den Status der Skriptausführung zu verfolgen:

            /// <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
    

    Wichtige Typen, Methoden und Eigenschaften: InsertStatement, SchemaObjectDataModificationTarget, Column, ValuesInsertSource und RowValue.

So fügen Sie die CreateIfNotExecutedStatement-Methode hinzu

  • Fügen Sie den folgenden Code hinzu, um die CreateIfNotExecutedStatement-Methode zu definieren. Von dieser Methode wird eine IF-Anweisung generiert, mit der überprüft wird, ob dieser Batch laut Angabe in der temporären Tabelle für ausgeführte Batches bereits ausgeführt wurde:

            /// <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
    

    Wichtige Typen, Methoden und Eigenschaften: IfStatement, ExistsPredicate, Subquery, SchemaObjectTableSource, WhereClause, Column, Literal, BinaryExpression und UnaryExpression

So fügen Sie die GetStepInfo-Methode hinzu

  1. Fügen Sie den folgenden Code hinzu, um die GetStepInfo-Methode zu definieren. Diese Methode führt Folgendes durch:

            /// <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
    

    Relevante Typen und Methoden: DeploymentPlanContributorContext, DeploymentScriptDomStep, IModelElement, CreateElementStep, AlterElementStep und DropElementStep.

  2. Speichern Sie die Änderungen in der Datei "Class1.cs".

    Im nächsten Schritt wird die Klassenbibliothek erstellt.

So signieren und erstellen Sie die Assembly

  1. Klicken Sie im Menü Projekt auf Eigenschaften von MyOtherDeploymentContributor.

  2. Klicken Sie auf die Registerkarte Signierung.

  3. Klicken Sie auf Assembly signieren.

  4. Klicken Sie im Feld Schlüsseldatei mit starkem Namen auswählen auf <Neu>.

  5. Geben Sie im Dialogfeld Schlüssel für einen starken Namen erstellen unter Schlüsseldateiname den Namen MyRefKey ein.

  6. (optional) Sie können für die Schlüsseldatei mit starkem Namen ein Kennwort angeben.

  7. Klicken Sie auf OK.

  8. Klicken Sie im Menü Datei auf Alle speichern.

  9. Klicken Sie im Menü Erstellen auf Projektmappe erstellen.

    Danach müssen Sie die Assembly installieren und registrieren, damit sie geladen wird, wenn Sie Datenbankprojekte bereitstellen.

Installieren eines Bereitstellungscontributors

Führen Sie zum Installieren eines Bereitstellungscontributors die folgenden Aufgaben aus:

  • Kopieren der Assembly und der zugeordneten PDB-Datei in den Ordner "Extensions"

  • Erstellen einer Datei vom Typ "Extensions.xml", um den Bereitstellungscontributor zu registrieren, damit er bei der Bereitstellung von Datenbankprojekten geladen wird

So installieren Sie die Assembly "MyOtherDeploymentContributor"

  1. Erstellen Sie im Verzeichnis "%Programme%\Microsoft Visual Studio 10.0\VSTSDB\Extensions" den Ordner MyExtensions.

  2. Kopieren Sie die signierte Assembly ("MyOtherDeploymentContributor.dll") in das Verzeichnis "%Programme%\Microsoft Visual Studio 10.0\VSTSDB\Extensions\MyExtensions".

    Tipp

    Es empfiehlt sich, die XML-Dateien nicht direkt in das Verzeichnis "%Programme%\Microsoft Visual Studio 10.0\VSTSDB\Extensions" zu kopieren. Wenn Sie stattdessen einen Unterordner verwenden, vermeiden Sie versehentliche Änderungen an den anderen Dateien, die mit Visual Studio bereitgestellt werden.

    Im nächsten Schritt muss die Assembly (eine Art Funktionserweiterung) registriert werden, damit sie in Visual Studio angezeigt wird.

So registrieren Sie die Assembly "MyOtherDeploymentContributor"

  1. Klicken Sie im Menü Ansicht auf Weitere Fenster, und klicken Sie anschließend auf Befehlsfenster, um das Befehlsfenster zu öffnen.

  2. Geben Sie im Befehlsfenster folgenden Code ein. Ersetzen Sie FilePath durch den Pfad und Dateinamen der kompilierten DLL-Datei. Schließen Sie Pfad und Dateiname in Anführungszeichen ein.

    Tipp

    Der Pfad der kompilierten DLL-Datei lautet standardmäßig Projektmappenpfad\bin\Debug oder Projektmappenpfad\bin\Release.

    ? System.Reflection.Assembly.LoadFrom(@"FilePath").FullName
    
    ? System.Reflection.Assembly.LoadFrom("FilePath").FullName
    
  3. Drücken Sie die EINGABETASTE.

  4. Kopieren Sie die resultierende Zeile in die Zwischenablage. Die Zeile sollte der folgenden entsprechen:

    "GeneratorAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=nnnnnnnnnnnnnnnn"
    
  5. Öffnen Sie einen Text-Editor, z. B. Editor.

    Wichtig

    Öffnen Sie den Editor unter Windows Vista und unter Microsoft Windows Server 2008 als Administrator, damit Sie die Datei im Ordner "Programme" speichern können.

  6. Stellen Sie die folgenden Informationen bereit, und geben Sie dabei eigene Werte für Assemblyname, öffentliches Schlüsseltoken und Erweiterungstyp an:

    <?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>
    

    Diese XML-Datei dient zum Registrieren der Klasse, die von DeploymentPlanExecutor erbt.

  7. Speichern Sie die Datei im Verzeichnis "%Programme%\Microsoft Visual Studio 10.0\VSTSDB\Extensions\MyExtensions" als "MyOtherDeploymentContributor.extensions.xml".

  8. Schließen Sie Visual Studio.

    Im nächsten Schritt wird ein Datenbankprojekt bereitgestellt, um den Contributor zu testen.

Testen des Bereitstellungscontributors

Führen Sie zum Testen des Bereitstellungscontributors die folgende Aufgabe aus:

  • Bereitstellen des Datenbankprojekts mithilfe von MSBuild und Angeben des entsprechenden Parameters

Da dieser Bereitstellungscontributor immer aktiviert ist, müssen dem Datenbankprojekt keine Eigenschaften hinzufügt werden.

Bereitstellen des Datenbankprojekts

So können Sie das Datenbankprojekt bereitstellen und einen Bereitstellungsbericht generieren

  1. Öffnen Sie eine Visual Studio-Eingabeaufforderung. Klicken Sie im Startmenü auf Alle Programme, auf Microsoft Visual Studio 2010, auf Visual Studio Tools und anschließend auf Visual Studio-Eingabeaufforderung (2010).

  2. Navigieren Sie an der Eingabeaufforderung zu dem Ordner, der das Datenbankprojekt enthält.

  3. Geben Sie an der Eingabeaufforderung die folgende Befehlszeile ein:

    MSBuild /t:Deploy MyDatabaseProject.dbproj
    

    Ersetzen Sie MyDatabaseProject durch den Namen des Datenbankprojekts, das Sie bereitstellen möchten.

  4. Untersuchen Sie das resultierende Bereitstellungsskript. Kurz vor dem Abschnitt mit der Bezeichnung "Vorlage für ein Skript vor der Bereitstellung" müsste Text zu finden sein, der dem folgenden Transact-SQL-Code ähnelt:

    :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
    

    Weiter unten im Bereitstellungsskript finden Sie bei jedem Batch eine IF-Anweisung, die die ursprüngliche Anweisung umgibt. So kann für eine CREATE SCHEMA-Anweisung beispielsweise Folgendes vorhanden sein:

    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
    

    Beachten Sie, dass es sich bei der CREATE SCHEMA-Anweisung um eine der Anweisungen handelt, die innerhalb der IF-Anweisung von einer EXECUTE sp_executesql-Anweisung umschlossen sein müssen. Bei Anweisungen wie "CREATE TABLE" wird die EXECUTE sp_executesql-Anweisung nicht benötigt. Diese Anweisungen ähneln dem folgenden Beispiel:

    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
    

    Tipp

    Wenn Sie ein Datenbankprojekt bereitstellen, das mit der Zieldatenbank identisch ist, ist der daraufhin angezeigte Bericht nicht sonderlich aussagekräftig. Stellen Sie zum Erhalten aussagekräftigerer Ergebnisse entweder Änderungen an einer Datenbank oder eine neue Datenbank bereit.

    Mit dem DeploymentPlanModifier können Sie Batches oder Anweisungen in jedem beliebigen Bereitstellungsplan hinzufügen, entfernen oder ändern.

Nächste Schritte

Sie können mit anderen Arten von Änderungen experimentieren, die an Bereitstellungsplänen vor deren Ausführung vorgenommen werden können. Zu den anderen Arten möglicher Änderungen zählen unter anderem das Hinzufügen einer erweiterten Eigenschaft zu allen Datenbankobjekten mit zugeordneter Versionsnummer sowie das Hinzufügen oder Entfernen zusätzlicher, diagnosebezogener print-Anweisungen oder Kommentare aus Bereitstellungsskripts.

Siehe auch

Konzepte

Erweitern der Datenbankfunktionen von Visual Studio

Weitere Ressourcen

Anpassen von Datenbankbuild und -bereitstellung mithilfe von Build- und Bereitstellungsmitwirkenden

Exemplarische Vorgehensweise: Erweitern des Datenbankprojektbuilds zum Generieren von Modellstatistiken

Exemplarische Vorgehensweise: Erweitern der Bereitstellung von Datenbankprojekten zum Analysieren des Bereitstellungsplans