Demonstra Passo a passo: Estender a implantação de projeto de banco de dados para modificar o plano de implantação
Você pode criar os colaboradores de implantação para executar ações personalizadas quando você implanta um projeto de banco de dados. Você pode criar um DeploymentPlanModifier ou um DeploymentPlanExecutor. Use um DeploymentPlanModifier para alterar o plano, antes que ele seja executado e um DeploymentPlanExecutor para executar operações enquanto o plano é executado. Esta explicação passo a passo, você pode criar um DeploymentPlanModifier chamado SqlRestartableScriptContributor adiciona declarações IF lotes no script de implantação para ativar o script para ser executado novamente até que sejam concluídas se ocorrer um erro durante a execução.
Esta explicação passo a passo, você irá realizar as seguintes tarefas principais:
Criar o tipo de DeploymentPlanModifier de Colaborador de implantação
Instalar o colaborador de implantação
Teste seu colaborador de implantação
Pré-requisitos
Para completar este passo a passo, são necessários os seguintes componentes:
Visual Studio 2010 Premiumou Visual Studio 2010 Ultimate instalado no seu computador.
Um projeto de banco de dados que contém objetos de banco de dados
Uma instância de SQL Server para o qual você pode implantar um projeto de banco de dados
Observação |
---|
Esta explicação passo a passo destina-se a usuários que já estejam familiarizados com os recursos de banco de dados do Visual Studio. Você também deve estar familiarizado com o basic Visual Studio os conceitos, como criar uma biblioteca de classe e de como usar o editor de código para adicionar código para uma classe. |
Criar um colaborador de implantação
Para criar um colaborador de implantação, você deve executar as seguintes tarefas:
Crie um projeto de biblioteca de classe e adicionar referências necessárias
Definir uma classe chamada SqlRestartableScriptContributor que herda deDeploymentPlanModifier
Substituir o método OnExecute
Adicionar métodos auxiliares particular
Compilar o assembly resultante
Para criar um projeto de biblioteca de classe
Criar um Visual C# ou Visual Basic o projeto de biblioteca de classe denominado MyOtherDeploymentContributor.
No Solution Explorer, clique com o botão direito do projeto e clique em Adicionar referência de.
Clique na .NET guia.
Realce o Microsoft.Data.Schema, Microsoft.Data.Schema.Sql, Microsoft.Data.Schema.ScriptDom, e Microsoft.Data.Schema.ScriptDom.Sql entradas e clique em OK.
Em seguida, inicie a adicionar código à classe.
Para definir a classe SqlRestartableScriptContributor
No editor de código, atualizar o arquivo Class1. cs para coincidir com o seguinte using instruções:
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
Atualize a definição de classe para coincidir com o exemplo a seguir:
/// <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
Agora que você definiu seu colaborador de compilação que herda de DeploymentPlanModifier. Você usou o DatabaseSchemaProviderCompatibilityAttribute atributo para indicar que este colaborador é compatível com qualquer provedor de esquema de banco de dados que herda de SqlDatabaseSchemaProvider.
Adicione as seguintes declarações de membro:
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 & "]"
Em seguida, você pode substituir o método OnExecute para adicionar o código que você deseja executar quando um projeto de banco de dados é implantado.
Para substituir o OnExecute
Adicione o seguinte método à sua classe de SqlRestartableScriptContributor:
/// <summary> /// You override the OnExecute method to do the real work of the contributor. /// </summary> /// <param name="context"></param> protected override void OnExecute(DeploymentPlanContributorContext context) { // Replace this with the method body }
''' <summary> ''' You override the OnExecute method to do the real work of the contributor. ''' </summary> ''' <param name="context"></param> Protected Overloads Overrides Sub OnExecute(ByVal context As DeploymentPlanContributorContext) ' Replace this with the method body End Sub
Você pode substituir o OnExecute o método da classe base, DeploymentPlanContributor, que é a classe base para ambos DeploymentPlanModifier e DeploymentPlanExecutor. O método OnExecute é passado um DeploymentPlanContributorContext especificado do objeto que fornece acesso a qualquer argumentos, origem e modelo de banco de dados de destino, as propriedades de compilação e arquivos de extensão. Neste exemplo, obtemos o plano de implantação e o nome do banco de dados de destino.
Agora, adicione os inícios de um corpo para o método OnExecute:
// Obtain the first step in the Plan from the provided context DeploymentStep nextStep = context.PlanHandle.Head; int batchId = 0; BeginPreDeploymentScriptStep beforePreDeploy = null; // Loop through all steps in the deployment plan while (nextStep != null) { // Increment the step pointer, saving both the current and next steps DeploymentStep currentStep = nextStep; nextStep = currentStep.Next; // Add additional step processing here } // if we found steps that required processing, set up a temporary table to track the work that you are doing if (beforePreDeploy != null) { // Add additional post-processing here } // Cleanup and drop the table DeploymentScriptStep dropStep = new DeploymentScriptStep(DropCompletedBatchesTable); base.AddAfter(context.PlanHandle, context.PlanHandle.Tail, dropStep);
' Obtain the first step in the Plan from the provided context Dim nextStep As DeploymentStep = context.PlanHandle.Head Dim batchId As Integer = 0 Dim beforePreDeploy As BeginPreDeploymentScriptStep = Nothing ' Loop through all steps in the deployment plan While nextStep IsNot Nothing ' Increment the step pointer, saving both the current and next steps Dim currentStep As DeploymentStep = nextStep nextStep = currentStep.[Next] ' Add additional step processing here End While ' if we found steps that required processing, set up a temporary table to track the work that you are doing If beforePreDeploy IsNot Nothing Then ' Add additional post-processing here End If ' Cleanup and drop the table Dim dropStep As New DeploymentScriptStep(DropCompletedBatchesTable) MyBase.AddAfter(context.PlanHandle, context.PlanHandle.Tail, dropStep)
Nesse código, podemos definir algumas variáveis de locais e configurar o loop que manipulará o processamento de todas as etapas no plano de implantação. Após o loop, podemos terá que fazer alguns pós-processamento e solte a tabela temporária que criamos durante a implantação para controlar o progresso como o plano de execução. Tipos de chaves aqui são: DeploymentStep e DeploymentScriptStep. Um método principal é AddAfter .
Agora, adicione a etapa adicional de processamento, para substituir o comentário que lê "Etapa adicional adicionar processamento aqui":
// 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
Os comentários de código explicam o processamento. Um alto nível desse código procura as etapas que você se preocupa, ignorando a outras pessoas e parar quando atingir o início das etapas pós-implantação. Se a etapa contém instruções que podemos deve cercar com condicionais, podemos executará um processamento adicional. Propriedades, métodos e tipos de chave incluem o seguinte: BeginPreDeploymentScriptStep, BeginPostDeploymentScriptStep, IModelElement, TSqlScript, Script, DeploymentScriptDomStep, and SqlPrintStep.
Agora, adicione o código de processamento, substituindo o comentário que lê "Adicionar processamento em lotes aqui" de lote:
// 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
Esse código cria uma instrução IF, juntamente com um bloco BEGIN/END. Em seguida, executamos o processamento adicional sobre as instruções no lote. Quando esse procedimento for concluído, adicionamos uma instrução INSERT para adicionar informações à tabela temporária que acompanha o andamento da execução do script. Finalmente, atualize o lote, substituindo as instruções que costumavam estar lá com o novo se que contém essas instruções dentro dele.
Key types, methods, and properties include:IfStatement, BeginEndBlockStatement, StatementList, TSqlBatch, PredicateSetStatement, SetOptions, and InsertStatement.
Agora, adicione o corpo do loop de processamento de instrução. Substitua o comentário que lê "Instrução adicional adicionar processamento aqui":
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)
Para cada instrução no lote, se a instrução é de um tipo que deve ser empacotado com uma instrução sp_executesql, modifique a instrução acordo. O código, em seguida, adiciona a instrução à lista de instrução para o bloco BEGIN/END que você criou. Principais tipos, métodos e propriedades incluem TSqlStatement e ExecuteStatement.
Finalmente, adicione a seção de pós-processamento no lugar do comentário que lê "Adicionar adicional pós-processamento aqui":
// 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)
Se nosso processamento encontrado uma ou mais etapas que podemos circundados por uma instrução condicional, podemos deve adicionar instruções para o script de implantação para definir as variáveis SQLCMD. A primeira variável, CompletedBatches, contém um nome exclusivo para a tabela temporária que o script de implantação usa para controlar quais lotes foram concluídos com êxito quando o script foi executado. A segunda variável, TotalBatchCount, contém o número total de lotes no script de implantação.
Tipos adicionais, propriedades e métodos de interesse incluem:
StringBuilder, DeploymentScriptStep, and AddBefore.
Em seguida, você pode definir os métodos auxiliares, chamados por esse método.
Para adicionar o método CreateExecuteSql
Adicione o seguinte código para definir o método CreateExecuteSQL para cercar uma instrução fornecida com uma instrução de sp_executesql EXEC:
/// <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
Propriedades, métodos e tipos de chave incluem o seguinte: ExecuteStatement, ExecutableProcedureReference, , SchemaObjectName, ProcedureReference, e ExecuteParameter.
Para adicionar o método CreateLiteral
Adicione o seguinte código para definir o método CreateLiteral. Esse método cria um objeto do tipo especificado de Literal da seqüência de caracteres fornecida:
/// <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
Propriedades, métodos e tipos de chave incluem o seguinte: Literal e LiteralType.
Para adicionar o método CreateIdentifier
Adicione o seguinte código para definir o método CreateIdentifier. Esse método cria um objeto identificador que usa o tipo de cotação da seqüência de caracteres fornecida especificado:
/// <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
Propriedades, métodos e tipos de chave incluem o seguinte: Identifier e QuoteType.
Para adicionar o método CreateCompletedBatchesName
Adicione o seguinte código para definir o método CreateCompletedBatchesName. Esse método cria o nome que será inserido na tabela temporária para um lote:
/// <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
Propriedades, métodos e tipos de chave incluem o seguinte: SchemaObjectName.
Para adicionar o método IsStatementEscaped
Adicione o seguinte código para definir o método IsStatementEscaped. Este método determina se o tipo de elemento de modelo exige a instrução a ser disposto em uma instrução de sp_executesql EXEC antes que ele pode ser colocado dentro de uma instrução IF:
/// <summary> /// Helper method that determins whether the specified statement needs to /// be escaped /// </summary> /// <param name="smnt"></param> /// <returns></returns> private static bool IsStatementEscaped(IModelElement element) { return element is ISql90Schema || element is ISqlProcedure || element is ISqlView || element is ISqlFunction || element is ISqlTrigger; }
''' <summary> ''' Helper method that determins whether the specified statement needs to ''' be escaped ''' </summary> ''' <param name="element"></param> ''' <returns></returns> Private Shared Function IsStatementEscaped(ByVal element As IModelElement) As Boolean Return TypeOf element Is ISql90Schema OrElse TypeOf element Is ISqlProcedure OrElse TypeOf element Is ISqlView OrElse TypeOf element Is ISqlFunction OrElse TypeOf element Is ISqlTrigger End Function
Propriedades, métodos e tipos de chave incluem o seguinte: IModelElement, ISql90Schema, ISqlProcedure, ISqlView, ISqlFunction, and ISqlTrigger.
Para adicionar o método CreateBatchCompleteInsert
Adicione o seguinte código para definir o método CreateBatchCompleteInsert. Esse método cria a instrução de inserção será adicionada ao script de implantação para controlar o andamento da execução do script:
/// <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
Propriedades, métodos e tipos de chave incluem o seguinte: InsertStatement, SchemaObjectDataModificationTarget, , Column, ValuesInsertSource, e RowValue.
Para adicionar o método CreateIfNotExecutedStatement
Adicione o seguinte código para definir o método CreateIfNotExecutedStatement. Esse método gera uma instrução IF que verifica se os lotes temporários executa tabela indica que este lote já foi executado:
/// <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
Propriedades, métodos e tipos de chave incluem o seguinte: IfStatement, ExistsPredicate, Subquery, SchemaObjectTableSource, WhereClause, Column, Literal, BinaryExpression, and UnaryExpression
Para adicionar o método GetStepInfo
Adicione o seguinte código para definir o método GetStepInfo. Este método:
/// <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
Tipos e métodos de interesse incluem o seguinte: DeploymentPlanContributorContext, DeploymentScriptDomStep, IModelElement, CreateElementStep, AlterElementStep, and DropElementStep.
Salve as alterações Class1. cs.
Em seguida, você pode criar a biblioteca de classes.
Para assinar e compilar o assembly
Sobre o projeto menu, clique em Propriedades de MyOtherDeploymentContributor.
Clique na guia Signing.
Clique em Sign the assembly.
Em Choose a strong name key file, clique em <New>.
No Create Strong Name Key na caixa nome do arquivo de chave, tipo MyRefKey.
(opcional) Você pode especificar uma senha para seu arquivo de chave de nome forte.
Clique em OK.
No menu File, clique em Save All.
Sobre o Build menu, clique em Build Solution.
Em seguida, você deve instalar e registrar o assembly para que ele será carregado quando você implantar projetos de banco de dados.
Instalar um colaborador de implantação
Para instalar um colaborador de implantação, você deve executar as seguintes tarefas:
Copie o assembly e o arquivo. PDB de associados para a pasta de extensões
Criar um arquivo de Extensions.xml para registrar o colaborador de implantação para que ele é carregado quando você os projetos de banco de dados de implantação
Para instalar o conjunto de MyOtherDeploymentContributor
Crie uma pasta chamada MyExtensions na pasta % do programa Files%\Microsoft Visual Studio 10.0\VSTSDB\Extensions.
Copie o assembly assinado (MyOtherDeploymentContributor.dll) para a pasta de 10.0\VSTSDB\Extensions\MyExtensions de Visual Studio do programa Files%\Microsoft %.
Observação Recomendamos não copie os arquivos XML diretamente para a pasta de 10.0\VSTSDB\Extensions de Visual Studio do programa Files%\Microsoft %. Se você usar uma subpasta em vez disso, você evitará que alterações acidentais feitas para os arquivos que são fornecidos com Visual Studio.
Em seguida, você deve registrar o assembly, o que é um tipo de a extensão de recurso, de modo que ele aparecerá na Visual Studio.
Para registrar o assembly de MyOtherDeploymentContributor
No Exibir menu, clique em Other Windowse em seguida, clique em Janela de comando para abrir o comando janela.
No comando janela, digite o seguinte código. Para FilePath, substitua o caminho e o nome do arquivo. dll de compilado. Inclua as aspas ao redor do caminho e nome de arquivo.
Observação Por padrão, o caminho do seu arquivo compilado. dll é YourSolutionPath\bin\Debug ou YourSolutionPath\bin\Release.
? System.Reflection.Assembly.LoadFrom(@"FilePath").FullName
? System.Reflection.Assembly.LoadFrom("FilePath").FullName
Pressione Digite.
Copie a linha resultante para a área de transferência. A linha deve ser semelhante ao seguinte:
"GeneratorAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=nnnnnnnnnnnnnnnn"
Abra um editor de texto sem formatação, como, por exemplo, o bloco de notas.
Importante Em Windows Vista e a Microsoft Windows Server 2008, abra o editor como um administrador para que você pode salvar o arquivo em sua pasta de arquivos de programa.
Forneça as informações a seguintes, especificando o seu próprio nome de assembly, o token de chave pública e o tipo de extensão:
<?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>
Você usa este arquivo XML para registrar a classe que herda de DeploymentPlanExecutor.
Salve o arquivo como MyOtherDeploymentContributor.extensions.xml na pasta % do programa Files%\Microsoft Visual Studio 10.0\VSTSDB\Extensions\MyExtensions.
Close Visual Studio.
Em seguida, você irá implantar um projeto de banco de dados para testar seu colaborador.
Teste seu colaborador de implantação
Para testar seu colaborador de implantação, você deve executar a tarefa a seguir:
- Implantar o projeto de banco de dados usando o MSBuild e fornecer o parâmetro apropriado
Porque este Colaborador de implantação está sempre ativada, você não precisará modificar o projeto de banco de dados para adicionar propriedades.
Implantar o projeto de banco de dados
Para implantar o seu projeto de banco de dados e gerar um relatório de implantação
Abrir um Visual Studio o Prompt de comando. No Iniciar menu, clique em Todos os programas, clique em 2010 do Microsoft Visual Studio, clique em Visual Studio Toolse clique em Prompt de comando Visual Studio (2010).
No prompt de comando, navegue até a pasta que contém o seu projeto de banco de dados.
No prompt de comando, digite a seguinte linha de comando:
MSBuild /t:Deploy MyDatabaseProject.dbproj
Substitua MyDatabaseProject com o nome do projeto de banco de dados que você deseja implantar.
Examine o script de implantação resultante. Antes da seção denominada "Modelo de Script de pré-implantação", você verá algo semelhante à seguinte Transact-SQL:
:setvar CompletedBatches __completedBatches_CompareProjectDB_cd1e348a-8f92-44e0-9a96-d25d65900fca :setvar TotalBatchCount 17 GO if OBJECT_ID(N'tempdb.dbo.$(CompletedBatches)', N'U') is null begin use tempdb create table [dbo].[$(CompletedBatches)] ( BatchId int primary key, Description nvarchar(300) ) use [$(DatabaseName)] end
Posteriormente no script de implantação, ao redor de cada lote, você pode ver uma instrução IF que circunda a instrução original. Por exemplo, a seguir pode aparecer para uma instrução CREATE SCHEMA:
IF NOT EXISTS (SELECT 1 FROM [tempdb].[dbo].[$(CompletedBatches)] WHERE [BatchId] = 0) BEGIN EXECUTE sp_executesql @stmt = N'CREATE SCHEMA [Sales] AUTHORIZATION [dbo]'; SET NOCOUNT ON; INSERT [tempdb].[dbo].[$(CompletedBatches)] (BatchId, Description) VALUES (0, N'CreateElementStep Sales batch 0'); SET NOCOUNT OFF; END
Observe que criar o esquema é uma das instruções que devem ser colocadas dentro de uma instrução de sp_executesql EXECUTE dentro da instrução se. Instruções de como, por exemplo, CREATE TABLE não exigem a instrução de sp_executesql EXECUTE e parecerá com o exemplo a seguir:
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
Observação Se você implantar um projeto de banco de dados é idêntico ao banco de dados de destino, o relatório resultante não será muito significativo. Para obter resultados mais significativos, implantar alterações em um banco de dados ou implantar um novo banco de dados.
Você pode adicionar, remover ou modificar os lotes ou instruções em qualquer plano de implantação usando um DeploymentPlanModifier.
Próximas etapas
Você pode experimentar outros tipos de modificações que você pode fazer planos de implantação antes que sejam executados. Alguns outros tipos de modificações que deseja fazer a incluir o seguinte: Adicionar uma propriedade estendida a todos os objetos de banco de dados que se associar a um número de versão-los, adicionando ou removendo adicionais de diagnóstico imprimir comentários ou instruções dos scripts de implantação e assim por diante.
Consulte também
Conceitos
Estender os recursos de banco de dados de Visual Studio