Demonstra Passo a passo: Estendendo o banco de dados renomear a refatoração para operar em arquivos de texto

Neste tópico passo a passo, você irá criar, instalar, registrar e testar um novo Colaborador para refatoração Renomear. Esta refatoração destino será estender os recursos do Visual Studio Premium ou Visual Studio Ultimate, para permitir que seu refatoração de banco de dados para renomear referências para objetos de banco de dados que estão contidas em arquivos de texto no seu o projeto de banco de dados.

Quando você adiciona um novo Colaborador de refatoração para um tipo existente de refatoração, ele deve usar uma classe existente de entrada de Colaborador.

Essa explicação passo a passo ilustra as seguintes tarefas:

  1. Crie um novo assembly que contém as classes para um destino de refatoração personalizado.

  2. Instalar e registrar o assembly, para que o destino da refatoração está disponível em Visual Studio Premium ou Visual Studio Ultimate.

  3. Crie um projeto de banco de dados simples para testar se o destino de refatoração funciona conforme esperado.

Pré-requisitos

Para completar este passo a passo, são necessários os seguintes componentes:

  • Você deve ter instalado Visual Studio 2010 Premium ou Visual Studio 2010 Ultimate.

  • Você também deve ter o SDK do Visual Studio 2010 instalado no seu computador. Para baixar esse kit, consulte esta página no site da Microsoft: SDK de 2010 Visual Studio.

Criando um Assembly com um personalizado refatoração de destino

Para criar um destino de refatoração personalizado que permite que a renomeação de refatoração para operar em arquivos de texto, você deve implementar uma classe para fornecer uma nova RefactoringContributor:

  • RenameReferenceTextContributorContributor — Essa classe cria a lista de propostas de alteração para o símbolo renomeado. As propostas de alteração são para cada referência que está contida em um arquivo de texto que está no projeto de banco de dados.

Antes de criar essa classe, irá criar uma biblioteca de classe, adicionar referências necessárias e adicione algum código auxiliar que simplifica a parte do código que você codificará posteriormente nesta explicação passo a passo.

Para criar o código de biblioteca e o auxiliar de classe

  1. Crie um novo C# classe projeto de biblioteca e nomeie- RenameTextContributor.csproj.

  2. Adicione referências para o seguinte.NET assemblies:

    • Microsoft.Data.Schema

    • Microsoft.Data.Schema.ScriptDom

    • Microsoft.Data.Schema.ScriptDom.SQL

    • Microsoft.Data.Schema.SQL

  3. Adicione referências aos seguintes assemblies que você encontrará na pasta % Visual Studio do programa Files%\Microsoft 10.0\VSTSDB:

    • Microsoft.Data.Schema.Tools.SQL.dll

    • Microsoft.VisualStudio.Data.Schema.Package.dll

    • Microsoft.VisualStudio.Data.Schema.Package.SQL.dll

  4. Adicione referências aos assemblies seguintes a partir do Visual Studio 2010 Software Development Kit (SDK):

    • Microsoft.VisualStudio.OLE.Interop.dll

    • Microsoft.VisualStudio.Shell.10.0.dll

    • Microsoft.VisualStudio.TextManager.Interop.dll

    • Microsoft.VisualStudio.Shell.Interop.dll

    • Microsoft.VisualStudio.Shell.Interop.8.0.dll

    • Microsoft.VisualStudio.Shell.Interop.9.0.dll

    • Microsoft.VisualStudio.Shell.Interop.10.0.dll

  5. Em Solution Explorer, renomeie Class1. cs para SampleHelper.cs.

  6. Clique duas vezes em SampleHelper.cs para abri-lo no editor de código.

    ObservaçãoObservação

    A classe auxiliar é idêntica ao texto auxiliar que foi usado na explicação passo a passo para tipos personalizados de refatoração. Você pode copiar o código-fonte do projeto para o novo projeto para economizar tempo.

  7. Substitua o conteúdo do editor de código com o seguinte código:

    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.IO;
    using System.Runtime.InteropServices;
    using Microsoft.Data.Schema.SchemaModel;
    using Microsoft.Data.Schema.ScriptDom.Sql;
    using Microsoft.VisualStudio;
    using Microsoft.VisualStudio.Data.Schema.Package.Refactoring;
    using Microsoft.VisualStudio.Data.Schema.Package.UI;
    using Microsoft.VisualStudio.Shell.Interop;
    using Microsoft.VisualStudio.TextManager.Interop;
    
    namespace MySamples.Refactoring
    {
        internal static class SampleHelper
        {
            public static String GetModelElementName(IModelElement modelElement)
            {
                SampleHelper.CheckNullArgument(modelElement, "modelElement");
                return modelElement.ToString();
            }
    
            /// <summary>
            /// Given a model element, returns its simple name.
            /// </summary>
            public static String GetModelElementSimpleName(IModelElement modelElement)
            {
                String separator = ".";
                String simpleName = String.Empty;
                String fullName = modelElement.ToString();
                String[] nameParts = fullName.Split(separator.ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
                if (nameParts.Length > 0)
                {
                    simpleName = nameParts[nameParts.Length - 1]; // last part 
                }
                if (simpleName.StartsWith("[") && simpleName.EndsWith("]"))
                {
                    simpleName = simpleName.Substring(1, simpleName.Length - 2);
                }
                return simpleName;
            }
    
            /// <summary>
            /// Find all files in the project with the specified file extension
            /// </summary>
            public static List<string> GetAllFilesInProject(IVsHierarchy solutionNode, string fileExtension, bool visibleNodesOnly)
            {
                List<string> files = new List<string>();
                if (null != solutionNode)
                {
                    EnumProjectItems(solutionNode, fileExtension, files,
                                    VSConstants.VSITEMID_ROOT,  // item id of solution root node
                                    0,                          // recursion from solution node
                                    true,                       // hierarchy is Solution node
                                    visibleNodesOnly);          // visibleNodesOnly
                }
                return files;
            }
    
            /// <summary>
            /// Enumerates recursively over the hierarchy items.
            /// </summary>
            /// <param name="hierarchy">hierarchy to enmerate over.</param>
            /// <param name="fileExtension">type of files we need to collect from the project</param>
            /// <param name="files">list of file paths</param>
            /// <param name="itemid">item id of the hierarchy</param>
            /// <param name="recursionLevel">Depth of recursion. For example, if recursion started with the Solution
            /// node, then : Level 0 -- Solution node, Level 1 -- children of Solution, etc.</param>
            /// <param name="hierIsSolution">true if hierarchy is Solution Node. </param>
            /// <param name="visibleNodesOnly">true if only nodes visible in the Solution Explorer should
            /// be traversed. false if all project items should be traversed.</param>
            private static void EnumProjectItems(IVsHierarchy hierarchy,
                                                string fileExtension,
                                                List<string> files,
                                                uint itemid,
                                                int recursionLevel,
                                                bool hierIsSolution,
                                                bool visibleNodesOnly)
            {
                int hr;
                IntPtr nestedHierarchyObj;
                uint nestedItemId;
                Guid hierGuid = typeof(IVsHierarchy).GUID;
    
    
                // Check first if this node has a nested hierarchy. 
                hr = hierarchy.GetNestedHierarchy(itemid, ref hierGuid, out nestedHierarchyObj, out nestedItemId);
                if (VSConstants.S_OK == hr && IntPtr.Zero != nestedHierarchyObj)
                {
                    IVsHierarchy nestedHierarchy = Marshal.GetObjectForIUnknown(nestedHierarchyObj) as IVsHierarchy;
                    Marshal.Release(nestedHierarchyObj);
                    if (nestedHierarchy != null)
                    {
                        EnumProjectItems(nestedHierarchy, fileExtension, files,
                                        nestedItemId,
                                        recursionLevel,
                                        false,
                                        visibleNodesOnly);
                    }
                }
                else
                {
                    // Check if the file extension of this node matches 
                    string fileFullPath;
                    hierarchy.GetCanonicalName(itemid, out fileFullPath);
                    if (CompareExtension(fileFullPath, fileExtension))
                    {
                        // add matched file paths into the list
                        files.Add(fileFullPath);
                    }
    
                    recursionLevel++;
    
                    //Get the first child node of the current hierarchy being walked
                    object pVar;
                    hr = hierarchy.GetProperty(itemid,
                        ((visibleNodesOnly || (hierIsSolution && recursionLevel == 1) ?
                            (int)__VSHPROPID.VSHPROPID_FirstVisibleChild : (int)__VSHPROPID.VSHPROPID_FirstChild)),
                        out pVar);
                    ErrorHandler.ThrowOnFailure(hr);
                    if (VSConstants.S_OK == hr)
                    {
                        // Use Depth first search so at each level we recurse to check if the node has any children
                        // and then look for siblings.
                        uint childId = GetItemId(pVar);
                        while (childId != VSConstants.VSITEMID_NIL)
                        {
                            EnumProjectItems(hierarchy, fileExtension, files, childId, recursionLevel, false, visibleNodesOnly);
                            hr = hierarchy.GetProperty(childId,
                                ((visibleNodesOnly || (hierIsSolution && recursionLevel == 1)) ?
                                    (int)__VSHPROPID.VSHPROPID_NextVisibleSibling : (int)__VSHPROPID.VSHPROPID_NextSibling),
                                out pVar);
                            if (VSConstants.S_OK == hr)
                            {
                                childId = GetItemId(pVar);
                            }
                            else
                            {
                                ErrorHandler.ThrowOnFailure(hr);
                                break;
                            }
                        }
                    }
                }
            }
    
            /// <summary>
            /// Gets the item id.
            /// </summary>
            /// <param name="pvar">VARIANT holding an itemid.</param>
            /// <returns>Item Id of the concerned node</returns>
            private static uint GetItemId(object pvar)
            {
                if (pvar == null) return VSConstants.VSITEMID_NIL;
                if (pvar is int) return (uint)(int)pvar;
                if (pvar is uint) return (uint)pvar;
                if (pvar is short) return (uint)(short)pvar;
                if (pvar is ushort) return (uint)(ushort)pvar;
                if (pvar is long) return (uint)(long)pvar;
                return VSConstants.VSITEMID_NIL;
            }
    
            /// <summary>
            /// Check if the file has the expected extension.
            /// </summary>
            /// <param name="filePath"></param>
            /// <param name="extension"></param>
            /// <returns></returns>
            public static bool CompareExtension(string filePath, string extension)
            {
                bool equals = false;
                if (!string.IsNullOrEmpty(filePath))
                {
                    equals = (string.Compare(System.IO.Path.GetExtension(filePath), extension, StringComparison.OrdinalIgnoreCase) == 0);
                }
                return equals;
            }
    
            /// <summary>
            /// Read file content from a file
            /// </summary>
            /// <param name="filePath"> file path </param>
            /// <returns> file content in a string </returns>
            internal static string ReadFileContent(string filePath)
            {
                //  Ensure that the file exists first.
                if (!File.Exists(filePath))
                {
                    Debug.WriteLine(string.Format("Cannot find the file: '{0}'", filePath));
                    return string.Empty;
                }
    
                string content;
                using (StreamReader reader = new StreamReader(filePath))
                {
                    content = reader.ReadToEnd();
                    reader.Close();
                }
                return content;
            }
    
            /// <summary>
            ///  Check null references and throw
            /// </summary>
            /// <param name="obj"></param>
            /// <param name="?"></param>
            public static void CheckNullArgument(object obj, string objectName)
            {
                if (obj == null)
                {
                    throw new System.ArgumentNullException(objectName);
                }
            }
    
            /// <summary>
            /// Get offset of the fragment from an Identifier if the identifier.value matches the
            /// name we are looking for.
            /// </summary>
            /// <param name="identifier"></param>
            /// <param name="expectedName"></param>
            public static RawChangeInfo AddOffsestFromIdentifier(
               Identifier identifier,
                String expectedName,
                String newName,
                Boolean keepOldQuote)
            {
                RawChangeInfo change = null;
                if (identifier != null && String.Compare(expectedName, identifier.Value, true) == 0)
                {
                    if (keepOldQuote)
                    {
                        QuoteType newQuote = QuoteType.NotQuoted;
                        newName = Identifier.DecodeIdentifier(newName, out newQuote);
                        newName = Identifier.EncodeIdentifier(newName, identifier.QuoteType);
                    }
                    change = new RawChangeInfo(identifier.StartOffset, identifier.FragmentLength, expectedName, newName);
                }
                return change;
            }
    
            public static IList<ChangeProposal> ConvertOffsets(
                string projectFullName,
                string fileFullPath,
                List<RawChangeInfo> changes,
                bool defaultIncluded)
            {
                // Get the file content into IVsTextLines
                IVsTextLines textLines = GetTextLines(fileFullPath);
    
                int changesCount = changes.Count;
                List<ChangeProposal> changeProposals = new List<ChangeProposal>(changesCount);
                for (int changeIndex = 0; changeIndex < changesCount; changeIndex++)
                {
                    int startLine = 0;
                    int startColumn = 0;
                    int endLine = 0;
                    int endColumn = 0;
    
    
                    RawChangeInfo currentChange = changes[changeIndex];
                    int startPosition = currentChange.StartOffset;
                    int endPosition = currentChange.StartOffset + currentChange.Length;
                    int result = textLines.GetLineIndexOfPosition(startPosition, out startLine, out startColumn);
                    if (result == VSConstants.S_OK)
                    {
                        result = textLines.GetLineIndexOfPosition(endPosition, out endLine, out endColumn);
                        if (result == VSConstants.S_OK)
                        {
                            TextChangeProposal changeProposal = new TextChangeProposal(projectFullName, fileFullPath, currentChange.NewText);
                            changeProposal.StartLine = startLine;
                            changeProposal.StartColumn = startColumn;
                            changeProposal.EndLine = endLine;
                            changeProposal.EndColumn = endColumn;
                            changeProposal.Included = defaultIncluded;
                            changeProposals.Add(changeProposal);
                        }
                    }
    
                    if (result != VSConstants.S_OK)
                    {
                        throw new InvalidOperationException("Failed to convert offset");
                    }
                }
                return changeProposals;
            }
    
            /// <summary>
            /// Get IVsTextLines from a file.  If that file is in RDT, get text buffer from it.
            /// If the file is not in RDT, open that file in invisible editor and get text buffer
            /// from it.
            /// If failed to get text buffer, it will return null.        
            /// </summary>
            /// <param name="fullPathFileName">File name with full path.</param>
            /// <returns>Text buffer for that file.</returns>
            private static IVsTextLines GetTextLines(string fullPathFileName)
            {
                System.IServiceProvider serviceProvider = DataPackage.Instance;
                IVsTextLines textLines = null;
                IVsRunningDocumentTable rdt = (IVsRunningDocumentTable)serviceProvider.GetService(typeof(SVsRunningDocumentTable));
    
                if (rdt != null)
                {
                    IVsHierarchy ppHier = null;
                    uint pitemid, pdwCookie;
                    IntPtr ppunkDocData = IntPtr.Zero;
                    try
                    {
                        rdt.FindAndLockDocument((uint)(_VSRDTFLAGS.RDT_NoLock), fullPathFileName, out ppHier, out pitemid, out ppunkDocData, out pdwCookie);
                        if (pdwCookie != 0)
                        {
                            if (ppunkDocData != IntPtr.Zero)
                            {
                                try
                                {
                                    // Get text lines from the doc data
                                    IVsPersistDocData docData = (IVsPersistDocData)Marshal.GetObjectForIUnknown(ppunkDocData);
    
                                    if (docData is IVsTextLines)
                                    {
                                        textLines = (IVsTextLines)docData;
                                    }
                                    else
                                    {
                                        textLines = null;
                                    }
                                }
                                catch (ArgumentException)
                                {
                                    // Do nothing here, it will return null stream at the end.
                                }
                            }
                        }
                        else
                        {
                            // The file is not in RDT, open it in invisible editor and get the text lines from it.
                            IVsInvisibleEditor invisibleEditor = null;
                            TryGetTextLinesAndInvisibleEditor(fullPathFileName, out invisibleEditor, out textLines);
                        }
                    }
                    finally
                    {
                        if (ppunkDocData != IntPtr.Zero)
                            Marshal.Release(ppunkDocData);
                    }
                }
                return textLines;
            }
    
            /// <summary>
            /// Open the file in invisible editor in the running
            /// documents table (RDT), and get text buffer from that editor.
            /// </summary>
            /// <param name="fullPathFileName">File name with full path.</param>
            /// <param name="spEditor">The result invisible editor.</param>
            /// <param name="textLines">The result text buffer.</param>
            /// <returns>True, if the file is opened correctly in invisible editor.</returns>
            private static bool TryGetTextLinesAndInvisibleEditor(string fullPathFileName, out IVsInvisibleEditor spEditor, out IVsTextLines textLines)
            {
                System.IServiceProvider serviceProvider = DataPackage.Instance;
                spEditor = null;
                textLines = null;
    
                // Need to open this file.  Use the invisible editor manager to do so.
                IVsInvisibleEditorManager spIEM;
                IntPtr ppDocData = IntPtr.Zero;
                bool result;
    
                Guid IID_IVsTextLines = typeof(IVsTextLines).GUID;
    
                try
                {
                    spIEM = (IVsInvisibleEditorManager)serviceProvider.GetService(typeof(IVsInvisibleEditorManager));
                    spIEM.RegisterInvisibleEditor(fullPathFileName, null, (uint)_EDITORREGFLAGS.RIEF_ENABLECACHING, null, out spEditor);
                    if (spEditor != null)
                    {
                        int hr = spEditor.GetDocData(0, ref IID_IVsTextLines, out ppDocData);
                        if (hr == VSConstants.S_OK && ppDocData != IntPtr.Zero)
                        {
                            textLines = Marshal.GetTypedObjectForIUnknown(ppDocData, typeof(IVsTextLines)) as IVsTextLines;
                            result = true;
                        }
                        else
                        {
                            result = false;
                        }
                    }
                    else
                    {
                        result = false;
                    }
                }
                finally
                {
                    if (ppDocData != IntPtr.Zero)
                        Marshal.Release(ppDocData);
                }
                return result;
            }
        }
    }
    
  8. Sobre o arquivo menu, clique em Salvar SampleHelper.cs.

  9. Adicionar uma classe chamada RawChangeInfo para o projeto.

  10. No editor de código, atualize o código de acordo com o seguinte:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    
    namespace MySamples.Refactoring
    {
        /// <summary>
        /// Helper class to encapsulate StartOffset, FragmentLength and change string from
        /// parser and SchemaAnalzyer.
        /// </summary>
        internal sealed class RawChangeInfo
        {
            private int _startOffset;
            private int _length;
            private string _oldText;
            private string _newText;
    
            public RawChangeInfo(int startOffset, int length, string oldText, string newText)
            {
                _startOffset = startOffset;
                _length = length;
                _oldText = oldText;
                _newText = newText;
            }
    
            public int StartOffset
            {
                get
                {
                    return _startOffset;
                }
                set
                {
                    _startOffset = value;
                }
            }
    
            public int Length
            {
                get
                {
                    return _length;
                }
            }
    
            public string OldText
            {
                get
                {
                    return _oldText;
                }
            }
    
            public string NewText
            {
                get
                {
                    return _newText;
                }
                set
                {
                    _newText = value;
                }
            }
        }
    }
    
  11. Sobre o arquivo menu, clique em Salvar RawChangeInfo.cs.

    Em seguida, você definirá a classe RenameReferenceTextContributor.

Para definir a classe RenameReferenceTextContributor

  1. Adicionar uma classe chamada RenameReferenceTextContributor ao seu projeto.

  2. No editor de código, atualizar usando as instruções para coincidir com o seguinte:

    using System;
    using System.Collections.Generic;
    using Microsoft.Data.Schema.Extensibility;
    using Microsoft.Data.Schema.Sql;
    using Microsoft.VisualStudio;
    using Microsoft.VisualStudio.Data.Schema.Package.Refactoring;
    using Microsoft.VisualStudio.Data.Schema.Package.Sql.Refactoring;
    
  3. Altere o namespace para MySamples.Refactoring:

    namespace MySamples.Refactoring
    
  4. Atualize a definição de classe para coincidir com o seguinte:

        [DatabaseSchemaProviderCompatibility(typeof(SqlDatabaseSchemaProvider))]
        internal class RenameReferenceTextContributor : RefactoringContributor<RenameReferenceContributorInput>
        {
        }
    

    Especificar o atributo para declarar que este colaborador é compatível com qualquer provedores do esquema de banco de dados que são derivados de SqlDatabaseSchemaProvider . Sua classe deve herdar de RefactoringContributor para o RenameReferenceContributorInput.

  5. Defina variáveis de membro privado e de constantes adicionais:

            #region const
            private const string TxtExtension = @".txt";
            private const string PreviewFriendlyName = @"Text Files";
            private const string PreviewDescription = @"Update text symbols in text files in the database project.";
            private const string PreviewWarningMessage = @"Updating text symbols in text files in the database project can cause inconsistency.";
    
            #endregion
    
            #region members
            private RefactoringPreviewGroup _textPreviewGroup;
            private List<String> _changingFiles;
            #endregion
    

    As constantes fornecem informações que aparecerão na janela de visualização. Os membros adicionais são usados para controlar o grupo de visualização e a lista de arquivos de texto que está sendo alterado.

  6. Adicione o construtor da classe:

            #region ctor
            public RenameReferenceTextContributor()
            {
                _textPreviewGroup = new RefactoringPreviewGroup(PreviewFriendlyName);
                _textPreviewGroup.Description = PreviewDescription;
                _textPreviewGroup.WarningMessage = PreviewWarningMessage;
                _textPreviewGroup.EnableChangeGroupUncheck = true;
                _textPreviewGroup.EnableChangeUncheck = true;
                _textPreviewGroup.DefaultChecked = false; 
                _textPreviewGroup.IncludeInCurrentProject = true;
    
                // This sample uses the default icon for the file,
                // but you could provide your own icon here.
                //RefactoringPreviewGroup.RegisterIcon(TxtExtension, "textfile.ico");
            }
            #endregion
    

    O construtor inicializa o elemento de modelo, criando um novo grupo de visualização e inicialização de suas propriedades. Você também pode registrar aqui, os ícones a serem exibidos na janela de visualização para extensões de nome de arquivo específico. Porque não há nenhuma sintaxe que realce para arquivos de texto, você não registrar um serviço de linguagem para os arquivos de texto.

  7. Substituir o PreviewGroup propriedade para retornar o grupo que foi criado quando este colaborador foi criado:

            /// <summary>
            /// Preview group for text files
            /// </summary>
            public override RefactoringPreviewGroup PreviewGroup
            {
                get
                {
                    return _textPreviewGroup;
                }
                set
                {
                    _textPreviewGroup = value;
                }
            }
    
  8. Substituir o ContributeChanges(Boolean) método para retornar uma lista de propostas de alteração:

            /// <summary>
            /// Contribute to the change proposals
            /// </summary>
            /// <param name="input">contributor input</param>
            /// <returns>List of change proposals with corresponding contributor inputs</returns>
            protected override Tuple<IList<ChangeProposal>, IList<ContributorInput>> ContributeChanges(RenameReferenceContributorInput input)
            {
                RenameReferenceContributorInput referenceInput = input as RenameReferenceContributorInput;
                if (referenceInput == null)
                {
                    throw new ArgumentNullException("input");
                }
    
                string projectFullName;
                referenceInput.RefactoringOperation.CurrentProjectHierarchy.GetCanonicalName(VSConstants.VSITEMID_ROOT, out projectFullName);
    
                return GetChangesForAllTextFiles(referenceInput, 
                                                projectFullName, 
                                                _textPreviewGroup.DefaultChecked, 
                                                out _changingFiles);
            }
    

    Este método chama o método GetAllChangesForAllTextFiles para a maioria do trabalho.

  9. Adicione o método GetChangesForAllTextFiles para iterar sobre a lista de arquivos de texto que estão contidos no projeto, obter as alterações para cada arquivo e agregar essas alterações em uma lista de propostas de alteração:

            /// <summary>
            /// Get all changes from all text files.
            /// </summary>
            private static Tuple<IList<ChangeProposal>, IList<ContributorInput>> GetChangesForAllTextFiles(
                RenameReferenceContributorInput input,
                string projectFullName,
                bool defaultChecked,
                out List<String> changingFiles)
            {
                if (input == null)
                {
                    throw new ArgumentNullException("input");
                }
    
                changingFiles = new List<String>();
                List<ChangeProposal> allChanges = new List<ChangeProposal>();
    
                List<string> files = new List<string>();
                files = SampleHelper.GetAllFilesInProject(input.RefactoringOperation.CurrentProjectHierarchy, TxtExtension, false);
    
                // process the text files one by one
                if (files != null && files.Count > 0)
                {
                    int fileCount = files.Count;
    
                    // Get all the changes for all txt files.
                    for (int fileIndex = 0; fileIndex < fileCount; fileIndex++)
                    {
                        IList<ChangeProposal> changes =
                            GetChangesForOneTextFile(
                                        input,
                                        projectFullName,
                                        files[fileIndex],
                                        defaultChecked);
                        if (changes != null && changes.Count > 0)
                        {
                            allChanges.AddRange(changes);
                            changingFiles.Add(files[fileIndex]);
                        }
                    }
                }
                return new Tuple<IList<ChangeProposal>, IList<ContributorInput>>(allChanges, null);
            }
    
  10. Implemente o método GetChangesForOneTextFileMethod para retornar uma lista de propostas de alteração que estão contidos em um arquivo de texto simples:

            /// <summary>
            /// Get all the change proposals from one text file.
            /// </summary>
            private static IList<ChangeProposal> GetChangesForOneTextFile(
                RenameReferenceContributorInput input,
                string projectFullName,
                string fileFullPath,
                bool defaultChecked)
            {
                const string separators = " \t \r \n \\()[]{}|.+-*/~!@#$%^&<>?:;";
    
                string fileContent = SampleHelper.ReadFileContent(fileFullPath);
    
                IList<ChangeProposal> changeProposals= null;
                if (string.IsNullOrEmpty(fileContent))
                {
                    // return empty change list
                    changeProposals = new List<ChangeProposal>();
                }
                else
                {
                    int startIndex = 0;
                    int maxIndex = fileContent.Length - 1;
                    string oldName = input.OldName;
                    int oldNameLength = oldName.Length;
                    List<RawChangeInfo> changes = new List<RawChangeInfo>();
                    while (startIndex < maxIndex)
                    {
                        // Text files do not understand schema object information
                        // We do just case-insensitive string match (without schema info)
                        // Only match whole word
                        int offset = fileContent.IndexOf(oldName, startIndex, StringComparison.OrdinalIgnoreCase);
    
                        // Cannot find match any more, stop the match
                        if (offset < 0)
                        {
                            break;
                        }
    
                        startIndex = offset + oldNameLength;
    
                        // match whole word: check before/after characters are separators
                        if (offset > 0)
                        {
                            char charBeforeMatch = fileContent[offset - 1];
                            // match starts in the middle of a token, discard and move on
                            if (!separators.Contains(charBeforeMatch.ToString()))
                            {
                                continue;
                            }
                        }
                        if (offset + oldNameLength < maxIndex)
                        {
                            char charAfterMatch = fileContent[offset + oldNameLength];
                            // match ends in the middle of a token, discard and move on
                            if (!separators.Contains(charAfterMatch.ToString()))
                            {
                                continue;
                            }
                        }
    
                        RawChangeInfo change = new RawChangeInfo(offset, oldNameLength, input.OldName, input.NewName);
                        changes.Add(change);
                    }
    
                    // convert index-based offsets to ChangeOffsets in ChangeProposals
                    changeProposals = SampleHelper.ConvertOffsets(
                        projectFullName,
                        fileFullPath,
                        changes,
                        defaultChecked);
                }
                return changeProposals;
            }
    

    Porque o destino de refatoração não é um Transact-SQL o script ou um esquema de objeto, esse método não usa tipos ou métodos a partir de um Microsoft.Data.Schema.ScriptDom ou Microsoft.Data.Schema.Sql.SchemaModel . Este destino refatoração fornece uma implementação de localizar e substituir arquivos de texto, porque os símbolos em arquivos de texto não tem informações de esquema disponíveis.

  11. Sobre o arquivo menu, clique em Salvar RenameTextContributor.cs.

    Em seguida, você irá configurar e compilar o assembly.

Para assinar e compilar o assembly

  1. Sobre o projeto menu, clique em Propriedades de RenameTextContributor.

  2. Clique na guia Signing.

  3. Clique em Sign the assembly.

  4. Em Choose a strong name key file, clique em <New>.

  5. No Create Strong Name Key na caixa nome do arquivo de chave, tipo MyRefKey.

  6. (opcional) Você pode especificar uma senha para seu arquivo de chave de nome forte.

  7. Clique em OK.

  8. No menu File, clique em Save All.

  9. Sobre o Build menu, clique em Build Solution.

    Em seguida, você deve instalar e registrar o assembly para que ele será exibido como uma disponível condição de teste.

Para instalar o conjunto de RenameTextContributor

  1. Crie uma pasta chamada MyExtensions na pasta % do programa Files%\Microsoft Visual Studio 10.0\VSTSDB\Extensions.

  2. Copie o assembly assinado (RenameTextContributor.dll) para a pasta de 10.0\VSTSDB\Extensions\MyExtensions de Visual Studio do programa Files%\Microsoft %.

    ObservaçãoObservaçã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 para os outros arquivos fornecidos com o Visual Studio.

    Em seguida, você deve registrar seu assembly, um tipo de a extensão de recurso, de modo que ele aparecerá na Visual Studio.

Para registrar o assembly de RenameTextContributor

  1. No Exibir menu, clique em Other Windowse em seguida, clique em Janela de comando para abrir o comando janela.

  2. 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çãoObservaçã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
    
  3. Pressione Enter.

  4. Copie a linha resultante para a área de transferência. A linha deve ser semelhante ao seguinte:

    "RenameTextContributor, Version=1.0.0.0, Culture=neutral, PublicKeyToken=nnnnnnnnnnnnnnnn"
    
  5. Abra um editor de texto sem formatação, como, por exemplo, o bloco de notas.

  6. 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="MySamples.Refactoring.RenameReferenceTextContributor" 
    assembly="RenameTextContributor, Version=1.0.0.0, Culture=neutral, PublicKeyToken=<enter key here>" enabled="true" />
    </extensions>
    

    Registrar a classe que herda de RefactoringContributor.

  7. Salve o arquivo como RenameTextContributor.extensions.xml na pasta % do programa Files%\Microsoft Visual Studio 10.0\VSTSDB\Extensions\MyExtensions.

  8. Close Visual Studio.

    Em seguida, você irá criar um projeto de banco de dados muito simples para testar o novo tipo de refatoração.

Teste o novo Colaborador de refatoração

Para criar um projeto de banco de dados

  1. Sobre o arquivo , aponte para novae clique em projeto.

  2. Em Modelos instalados, expanda o banco de dados nó e clique o SQL Server nó.

  3. Na lista de modelos, clique em Projeto de banco de dados de 2008 do SQL Server.

  4. Clique em OK para aceitar o nome de projeto padrão e criar o projeto.

    Um projeto de banco de dados vazio é criado.

Para adicionar uma tabela com uma chave primária

  1. Sobre o Exibir menu, clique em Visualização do esquema de banco de dados.

  2. Em Visualização Esquema, expandir o esquemas nó, expanda o dbo nó, com o botão direito do tabelas nó, aponte para Adicionare clique em tabela.

  3. No Add New Item na caixa nome, tipo funcionário.

    ObservaçãoObservação

    Intencionalmente, você está usando uma letra minúscula para começar o nome da tabela.

  4. Clique em OK.

  5. Expanda o tabelas nó, com o botão direito do funcionário nó, aponte para Adde clique em Chave primária.

  6. No Add New Item na caixa nome, tipo PK_Employee_column_1.

  7. Clique em OK.

    Em seguida, você irá adicionar um arquivo de texto ao seu projeto de banco de dados que contém uma referência à tabela de funcionários.

Para adicionar um arquivo de texto que contém o nome da tabela

  1. Em Solution Explorer, o botão direito do mouse no nó do projeto de banco de dados, aponte para Adde clique em Novo Item.

  2. No Add New Item na caixa de categorias lista, clique em Visual Studio modelos de.

  3. No modelos de , clique em arquivo de texto.

  4. Em nome, tipo SampleText1.txt.

  5. No editor de código, adicione o seguinte texto:

    This is documentation for the employee table.
    Any changes made to the employee table name should also be reflected in this text file.
    
  6. Sobre o arquivo menu, clique em Salvar SampleText1.txt.

    Em seguida, você usará o novo tipo de refatoração para alterar a tabela de nome e todas as referências a ele.

Para usar o novo Colaborador de refatoração para atualizar o nome da tabela

  1. Em Visualização Esquema, com o botão direito do funcionário nó da tabela, aponte para Refactore clique em Renomear.

  2. No Renomear na caixa nome do novo, tipo [pessoa].

  3. No Visualizar alterações caixa de diálogo, percorra os grupos de alteração até ver o Arquivos de texto grupo.

    As duas instâncias do funcionário no arquivo de texto serão listadas. Você definiu as classes que permitem que este novo tipo de refatoração nesta explicação. Selecione a caixa de seleção ao lado de cada alteração.

  4. Clique em Apply.

    O nome da tabela é atualizado para ser a pessoa, no modo de exibição do esquema e o conteúdo do arquivo SampleText1.txt.

Próximas etapas

Você pode criar destinos de refatoração adicionais ou você pode criar novos tipos de refatoração, para reduzir o esforço associado a alterações repetitivas ao seu projeto de banco de dados.

Consulte também

Tarefas

Demonstra Passo a passo: Criando um novo tipo de banco de dados de refatoração para alterar as maiúsculas e minúsculas

Conceitos

Criar banco de dados personalizado refatoração tipos ou destinos

Refatorar o código do banco de dados e dados