Tutorial: Crear un nuevo tipo de refactorización de base de datos para cambiar las mayúsculas y minúsculas
En este tema paso a paso creará, instalará, registrará y probará un nuevo tipo de refactorización de base de datos. Esta operación de refactorización convertirá a mayúsculas el primer carácter del nombre del objeto especificado y actualizará todas las referencias a ese nombre actualizado.
En este tutorial se muestran las tareas siguientes:
Crear un nuevo ensamblado que contenga las clases para un tipo de refactorización personalizado.
Instalar y registrar el ensamblado para que el tipo de refactorización esté disponible en Visual Studio Premium o Visual Studio Ultimate.
Crear un proyecto de base de datos sencillo para probar que el tipo de refactorización funciona según lo esperado.
Requisitos previos
Necesita los componentes siguientes para completar este tutorial:
Debe tener instalado Visual Studio 2010 Premium o Visual Studio 2010 Ultimate.
También debe tener instalado el Kit de desarrollo de software (SDK) de Visual Studio 2010 en el equipo. Para descargar este kit, vea esta página en el sitio web de Microsoft: Visual Studio 2010 SDK.
Crear un ensamblado con un tipo de refactorización personalizado
Para crear un tipo de refactorización personalizado que pueda convertir a mayúsculas la primera letra de un nombre de objeto y a continuación actualizar todas las referencias a ese objeto, debe implementar seis clases:
CasingRefactoringCommand: esta clase proporciona el nombre de comando para el menú de refactorización, especifica en qué elementos de modelo está disponible su tipo de refactorización y llama a la operación de refactorización cuando el usuario hace clic en el comando.
CasingRefactoringOperation: esta clase especifica cómo interactúa la operación de refactorización con la ventana de vista previa, especifica las propiedades que describen la operación y crea la clase CasingContributorInput.
CasingContributorInput: esta clase almacena los datos de entrada en la clase CasingSymbolContributor.
CasingSymbolContributor: esta clase crea la lista de propuestas de cambio para el símbolo cuyo nombre se ha cambiado y también crea la clase CasingReferenceContributorInput para administrar la actualización de las referencias al objeto cuyo nombre se está cambiando.
CasingReferenceContributorInput: esta clase almacena los datos de entrada en la clase CasingReferenceContributor.
CasingReferenceContributor: esta clase crea la lista de propuestas de cambio asociadas a la actualización de las referencias al símbolo cuyo nombre se ha cambiado.
Antes de crear estas clases, creará una biblioteca de clases, agregará las referencias necesarias y agregará código auxiliar que simplifique una parte del código que escribirá más adelante en este tutorial.
Para crear la biblioteca de clases y el código auxiliar
Cree un nuevo proyecto de biblioteca de clases de C# y denomínelo CasingRefactoringType.csproj.
Agregue referencias a las siguientes bibliotecas de clases:
Microsoft.Data.Schema.dll
Microsoft.Data.Schema.ScriptDom.dll
Microsoft.Data.Schema.ScriptDom.sql.dll
Microsoft.Data.Schema.Sql.dll
Microsoft.VisualStudio.Data.Schema.Package.dll
Microsoft.VisualStudio.Data.Schema.Package.Sql.dll
Agregue referencias a los siguientes ensamblados del Kit de desarrollo de software (SDK) de Visual Studio 2010:
Microsoft.VisualStudio.OLE.Interop.dll
Microsoft.VisualStudio.Shell.10.0.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
Microsoft.VisualStudio.TextManager.Interop.dll
En el Explorador de soluciones, cambie el nombre de Class1.cs a SampleHelper.cs.
Haga doble clic en SampleHelper.cs para abrirlo en el editor de código.
Reemplace el contenido del editor de código por el código siguiente:
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. e.g. 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; } } }
En el menú Archivo, haga clic en Guardar SampleHelper.cs.
Agregue al proyecto una clase denominada RawChangeInfo.
En el editor de código, actualice el código para que coincida con el siguiente:
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; } } } }
En el menú Archivo, haga clic en Guardar RawChangeInfo.cs.
A continuación, definirá la clase CasingRefactoringCommand.
Para definir la clase CasingRefactoringCommand
Agregue al proyecto una clase denominada CasingRefactorCommand.
En el editor de código, actualice las instrucciones using para que coincidan con las siguientes:
using Microsoft.Data.Schema.Extensibility; using Microsoft.Data.Schema.SchemaModel; using Microsoft.Data.Schema.SchemaModel.Abstract; using Microsoft.Data.Schema.Sql; using Microsoft.Data.Schema.Sql.SchemaModel; using Microsoft.VisualStudio.Data.Schema.Package.Project; using Microsoft.VisualStudio.Data.Schema.Package.Refactoring;
Cambie el espacio de nombres a MySamples.Refactoring:
namespace MySamples.Refactoring
Actualice la definición de clase para que coincida con la siguiente:
[DatabaseSchemaProviderCompatibility(typeof(SqlDatabaseSchemaProvider))] internal class CasingRefactorCommand : RefactoringSchemaViewNodeCommand { }
El atributo se usa para especificar los proveedores de esquema de base de datos con los que este tipo de refactorización es compatible. Para este ejemplo, su nuevo tipo de refactorización funcionará con cualquier proveedor que se derive de SqlDatabaseSchemaProvider. Su clase hereda RefactoringSchemaViewNodeCommand. La herencia de esta clase base indica que su tipo de refactorización está disponible en los nodos especificados en Vista de esquema. Es posible definir otros tipos de refactorización que funcionan en nodos de archivo y en nodos de proyecto.
Después, agregue el siguiente método de reemplazo a su clase:
public override void Execute(IDatabaseProjectNode currentProject, IModelElement selectedModelElement) { CasingRefactorOperation operation = new CasingRefactorOperation(currentProject, selectedModelElement); operation.DoOperation(); }
Este método proporciona el comportamiento cuando el usuario aplica su comando de refactorización en Vista de esquema.
Después, agregue el siguiente método de reemplazo a su clase:
public override QueryStatusResult QueryStatus(IModelElement selectedModelElement) { if (selectedModelElement is IDatabaseColumnSource || selectedModelElement is ISqlSimpleColumn || selectedModelElement is ISqlProcedure || selectedModelElement is ISqlFunction || selectedModelElement is ISqlIndex || selectedModelElement is ISqlConstraint) { return QueryStatusResult.Enabled; } else { return QueryStatusResult.Invisible; } }
Este método determina en qué nodos de Vista de esquema está disponible su comando de refactorización.
Por último, agregue el siguiente método de reemplazo a su clase:
public override string Text { get { return "Make First Letter Uppercase"; } }
Este método proporciona el nombre descriptivo para el comando de refactorización que aparece en el menú de refactorización.
En el menú Archivo, haga clic en Guardar CasingRefactoringCommand.cs.
A continuación, definirá la clase CasingRefactoringOperation.
Para definir la clase CasingRefactoringOperation
Agregue al proyecto una clase denominada CasingRefactoringOperation.
En el editor de código, actualice las instrucciones using para que coincidan con las siguientes:
using System; using System.Diagnostics; using System.Globalization; using Microsoft.Data.Schema.SchemaModel; using Microsoft.Data.Schema.Sql.SchemaModel; using Microsoft.VisualStudio.Data.Schema.Package.Project; using Microsoft.VisualStudio.Data.Schema.Package.Refactoring;
Cambie el espacio de nombres a MySamples.Refactoring:
namespace MySamples.Refactoring
Actualice la definición de clase para que coincida con la siguiente:
internal class CasingRefactorOperation : RefactoringOperation { }
Su clase debe heredar de RefactoringOperation.
Agregue las siguientes declaraciones de variable miembro y constante a su clase:
#region Const private const string CasingRefactorOperationName = @"Make First Letter Uppercase"; private const string OperationDescription = @"Make First Letter Uppercase"; private const string OperationTextViewDescription = @"Preview changes:"; private const string PreviewDialogTitle = @"Preview Changes - {0}"; private const string ConfirmButtonText = @"&Apply"; private const string CasingUndoDescription = @"Make first letter uppercase - {0}"; #endregion private string _operationName; private PreviewWindowInfo _previewWindowInfo; private ISqlModelElement _modelElement;
Las constantes privadas proporcionan información sobre esta operación que se mostrará en la ventana de vista previa.
Agregue el constructor para su clase:
public CasingRefactorOperation(IDatabaseProjectNode currentProject, IModelElement selectedModelElement) : base(currentProject) { _operationName = CasingRefactorOperationName; if (selectedModelElement as ISqlModelElement != null) { _modelElement = selectedModelElement as ISqlModelElement; } }
El constructor inicializa el nombre de la operación y el elemento de modelo, si se especificaron.
Reemplace la propiedad PreviewWindowInfo para obtener los valores que aparecerán en la ventana de vista previa cuando el usuario aplique su tipo de refactorización:
/// <summary> /// Preview dialog information for this RenameRefactorOperation. /// </summary> protected override PreviewWindowInfo PreviewWindowInfo { get { if (_previewWindowInfo == null) { _previewWindowInfo = new PreviewWindowInfo(); _previewWindowInfo.ConfirmButtonText = ConfirmButtonText; _previewWindowInfo.Description = OperationDescription; _previewWindowInfo.HelpContext = String.Empty; _previewWindowInfo.TextViewDescription = OperationTextViewDescription; _previewWindowInfo.Title = string.Format(CultureInfo.CurrentCulture,PreviewDialogTitle, CasingRefactorOperationName); } return _previewWindowInfo; } }
Proporcione definiciones de propiedad adicionales:
protected override string OperationName { get { return _operationName; } } /// <summary> /// Undo Description used in undo stack /// </summary> protected override string UndoDescription { get { return string.Format(CultureInfo.CurrentCulture, CasingUndoDescription, SampleHelper.GetModelElementName(this.ModelElement)); } } /// <summary> /// SchemaIdentifier of currently selected schema object /// </summary> public ISqlModelElement ModelElement { get { return _modelElement; } set { _modelElement = value; } }
Por último, reemplace el método OnGetContributorInput:
/// <summary> /// According to different selected node, create different CasingContributorInput /// </summary> /// <returns></returns> protected override ContributorInput OnGetContributorInput() { ContributorInput input = null; SqlSchemaModel dataSchemaModel = this.CurrentDataSchemaModel as SqlSchemaModel; // You might choose to throw an exception here if // schemaModel is null. Debug.Assert(dataSchemaModel != null, "DataSchemaModel is null."); // create contributor input used in this operation input = new CasingContributorInput(this.ModelElement); return input; }
Este método crea el ContributorInput que se pasa a los colaboradores de refactorización para este tipo de refactorización.
En el menú Archivo, haga clic en Guardar CasingRefactoringOperation.cs.
A continuación, definirá la clase CasingContributorInput.
Para definir la clase CasingContributorInput
Agregue al proyecto una clase denominada CasingContributorInput.
En el editor de código, actualice las instrucciones using para que coincidan con las siguientes:
using System; using Microsoft.Data.Schema.Sql.SchemaModel; using Microsoft.VisualStudio.Data.Schema.Package.Refactoring;
Cambie el espacio de nombres a MySamples.Refactoring:
namespace MySamples.Refactoring
Actualice la definición de clase para que coincida con la siguiente:
internal class CasingContributorInput: ContributorInput { }
Su clase debe heredar de ContributorInput.
Defina una variable miembro privada adicional:
private ISqlModelElement _modelElement;
Este miembro se usa para realizar un seguimiento del elemento de modelo en el que está funcionando.
Agregue el constructor de clase:
public CasingContributorInput(ISqlModelElement modelElement) { _modelElement = modelElement; }
El constructor inicializa el elemento de modelo.
Agregue una propiedad pública de solo lectura para el elemento de modelo:
/// <summary> /// Selected model element /// </summary> public ISqlModelElement ModelElement { get { return _modelElement; } }
El método Reemplazar Equals proporciona una comparación que determina si dos objetos CasingContributorInput son iguales:
/// <summary> /// Override Equals /// </summary> /// <param name="obj"></param> /// <returns></returns> public override bool Equals(object obj) { CasingContributorInput other = obj as CasingContributorInput; return _modelElement.Equals(other.ModelElement); }
Para este colaborador, las entradas se considerarán iguales si se aplican al mismo elemento de modelo.
Reemplace el método GetHashCode:
/// <summary> /// Override GetHashCode /// </summary> /// <returns></returns> public override int GetHashCode() { Int32 hash = _modelElement.GetHashCode(); return hash; }
En el menú Archivo, haga clic en Guardar CasingContributorInput.cs.
A continuación, definirá la clase CasingSymbolContributor.
Para definir la clase CasingSymbolContributor
Agregue al proyecto una clase denominada CasingSymbolContributor.
En el editor de código, actualice las instrucciones using para que coincidan con las siguientes:
using System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using Microsoft.Data.Schema.Extensibility; using Microsoft.Data.Schema.SchemaModel; using Microsoft.Data.Schema.ScriptDom.Sql; using Microsoft.Data.Schema.Sql.SchemaModel; using Microsoft.Data.Schema.Sql; using Microsoft.VisualStudio; using Microsoft.VisualStudio.Data.Schema.Package.Refactoring;
Cambie el espacio de nombres a MySamples.Refactoring:
namespace MySamples.Refactoring
Actualice la definición de clase para que coincida con la siguiente:
[DatabaseSchemaProviderCompatibility(typeof(SqlDatabaseSchemaProvider))] internal class CasingSymbolContributor : RefactoringContributor<CasingContributorInput> { }
Especifique el atributo para declarar que este colaborador es compatible con los proveedores de esquema de base de datos derivados de SqlDatabaseSchemaProvider. Su clase debe heredar RefactoringContributor para la clase CasingContributorInput.
Defina constantes adicionales y variables miembro privadas:
#region Const private const string PreviewGroupFriendlyName = @"Schema Objects"; private const string PreviewDescription = @"Uppercasing the first letter of schema object name and all references to this schema object."; private const string PreviewWarning = @"Changing casing of the schema object name may cause errors and warnings when your project is using case-sensitive collations. "; #endregion private RefactoringPreviewGroup _previewGroup;
Las constantes proporcionan información que aparecerá en la ventana de vista previa. El miembro adicional se usa para realizar un seguimiento del grupo de vista previa.
Agregue el constructor de clase:
#region ctor public CasingSymbolContributor() { _previewGroup = new RefactoringPreviewGroup(PreviewGroupFriendlyName); _previewGroup.Description = PreviewDescription; _previewGroup.WarningMessage = PreviewWarning; _previewGroup.EnableChangeGroupUncheck = false; _previewGroup.IncludeInCurrentProject = true; // the default icon will be used if do not register and icon for your file extensions //RefactoringPreviewGroup.RegisterIcon("sql", "SqlFileNode.ico"); //RefactoringPreviewGroup.RegisterIcon(".dbproj", "DatabaseProjectNode.ico"); // For some contributors, you might register a // language service here. //_previewGroup.RegisterLanguageService(".sql", ); base.RegisterGeneratedInputType(typeof(CasingReferenceContributorInput)); } #endregion
El constructor inicializa el elemento de modelo, creando un nuevo grupo de vista previa e inicializando sus propiedades. También podría registrar aquí los iconos que se van a mostrar en la ventana de vista previa para determinadas extensiones de nombre de archivo y registrar un servicio de lenguaje que se emplea para proporcionar coloreado de sintaxis para los archivos que tienen una extensión especificada.
Reemplace la propiedad PreviewGroup para devolver el grupo que se creó cuando se creó este colaborador:
#region overrides /// <summary> /// Preview group for schema object files /// </summary> public override RefactoringPreviewGroup PreviewGroup { get { return _previewGroup; } set { _previewGroup = value; } }
Reemplace el método ContributeChanges(Boolean) para devolver una lista de propuestas de cambios:
/// <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(CasingContributorInput input) { CasingContributorInput casingInput = input as CasingContributorInput; if (casingInput == null) { throw new ArgumentNullException("input"); } string projectFullName; casingInput.RefactoringOperation.CurrentProjectHierarchy.GetCanonicalName(VSConstants.VSITEMID_ROOT, out projectFullName); Tuple<IList<ChangeProposal>, IList<ContributorInput>> changes = GetChangesFromCurrentSymbolScript( projectFullName, casingInput, casingInput.ModelElement, true); return changes; } #endregion
Después, cree otro tipo de ContributorInput:
/// <summary> /// Create a CasingReferenceContributorInput according to passed in CasingContributorInput /// </summary> /// <param name="orginalInput"></param> /// <returns></returns> internal ContributorInput CreateCasingReferenceInput(ContributorInput orginalInput) { CasingContributorInput casingInput = orginalInput as CasingContributorInput; Debug.Assert(casingInput != null, "casingInput is null"); CasingReferenceContributorInput referenceInput = new CasingReferenceContributorInput(casingInput.ModelElement); referenceInput.SchemaObjectsPreviewGroup = this.PreviewGroup; referenceInput.RefactoringOperation = casingInput.RefactoringOperation; return referenceInput; }
Este tipo adicional de ContributorInput se usa para administrar todas las referencias al elemento cuyo símbolo se actualizó. El método siguiente llamará a este método.
Agregue un método para crear una lista de cambios para el script que contiene la definición del símbolo que se está refactorizando:
public Tuple<IList<ChangeProposal>, IList<ContributorInput>> GetChangesFromCurrentSymbolScript( string projectFullName, ContributorInput input, ISqlModelElement modelElement, Boolean defaultChecked) { SampleHelper.CheckNullArgument(input, "input"); SampleHelper.CheckNullArgument(modelElement, "modelElement"); SqlSchemaModel dataSchemaModel = input.RefactoringOperation.CurrentDataSchemaModel as SqlSchemaModel; Debug.Assert(dataSchemaModel != null, "DataSchemaModel is null."); List<ChangeProposal> allChanges = new List<ChangeProposal>(); // list to hold all side effect contributor inputs List<ContributorInput> inputs = new List<ContributorInput>(); string fileFullPath = null; ISourceInformation elementSource = modelElement.PrimarySource; if (elementSource != null) { fileFullPath = elementSource.SourceName; } if (!string.IsNullOrEmpty(fileFullPath)) { List<RawChangeInfo> changes = AnalyzeScript(dataSchemaModel, modelElement); // Convert the offsets returned from parser to the line based offsets allChanges.AddRange(SampleHelper.ConvertOffsets(projectFullName, fileFullPath, changes, defaultChecked)); // Create a CasingReferenceContributorInput, anything reference this schema object // need to contribute changes for this input. inputs.Add(CreateCasingReferenceInput(input)); } return new Tuple<IList<ChangeProposal>, IList<ContributorInput>>(allChanges, inputs); }
Este método llama al método AnalyzeScript para procesar los elementos de script.
Agregue el método AnalyzeScript:
public static List<RawChangeInfo> AnalyzeScript(SqlSchemaModel dataSchemaModel, ISqlModelElement modelElement) { SampleHelper.CheckNullArgument(dataSchemaModel, "dataSchemaModel"); // get element source ISourceInformation elementSource = modelElement.PrimarySource; if (elementSource == null) { throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, "Cannot retrieve element source of {0}", SampleHelper.GetModelElementName(modelElement))); } // get sql fragment TSqlFragment fragment = elementSource.ScriptDom as TSqlFragment; if (fragment == null) { throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, "Cannot retrieve script fragment of {0}", SampleHelper.GetModelElementName(modelElement))); } List<RawChangeInfo> changes = new List<RawChangeInfo>(); Identifier id = null; if (fragment is CreateTableStatement) // Table { id = ((CreateTableStatement)fragment).SchemaObjectName.BaseIdentifier; } else if (fragment is CreateViewStatement) // View { id = ((CreateViewStatement)fragment).SchemaObjectName.BaseIdentifier; } else if (fragment is ColumnDefinition) // Column { id = ((ColumnDefinition)fragment).ColumnIdentifier; } else if (fragment is CreateProcedureStatement) // Proc { ProcedureReference procRef = ((CreateProcedureStatement)fragment).ProcedureReference; if (procRef != null) { id = procRef.Name.BaseIdentifier; } } else if (fragment is CreateFunctionStatement) // Function { id = ((CreateFunctionStatement)fragment).Name.BaseIdentifier; } else if (fragment is CreateIndexStatement) // Index { id = ((CreateIndexStatement)fragment).Name; } else if (fragment is Constraint) // inline constraint { id = ((Constraint)fragment).ConstraintIdentifier; } else if (fragment is AlterTableAddTableElementStatement) // default/check constraints { IList<Constraint> constraints = ((AlterTableAddTableElementStatement)fragment).TableConstraints; Debug.Assert(constraints.Count == 1, string.Format("Only one constraint expected, actual {0}", constraints.Count)); id = constraints[0].ConstraintIdentifier; } else // anything NYI { Debug.WriteLine(string.Format("Uppercasing symbol of type {0} is not implemented yet.", fragment.GetType().Name)); } string oldName = SampleHelper.GetModelElementSimpleName(modelElement); if (id != null && oldName.Length > 0) { string newName = oldName.Substring(0, 1).ToUpper() + oldName.Substring(1); // upper casing the first letter if (string.CompareOrdinal(oldName, newName) != 0) { RawChangeInfo change = SampleHelper.AddOffsestFromIdentifier(id, oldName, newName, true); if (change != null) { changes.Add(change); } } } return changes; }
Este método adquiere el script de origen para el elemento que se está refactorizando. A continuación, el método recupera el fragmento de SQL para ese script de origen. Después, el método crea una nueva lista de cambios, determina el identificador para el elemento (según el tipo de fragmento) y agrega el nuevo cambio a la lista de cambios.
En el menú Archivo, haga clic en Guardar CasingSymbolContributor.cs.
A continuación, definirá la clase CasingReferenceContributorInput.
Para definir la clase CasingReferenceContributorInput
Agregue al proyecto una clase denominada CasingReferenceContributorInput.
En el editor de código, actualice las instrucciones using para que coincidan con las siguientes:
using System; using Microsoft.Data.Schema.Sql.SchemaModel; using Microsoft.VisualStudio.Data.Schema.Package.Refactoring;
Cambie el espacio de nombres a MySamples.Refactoring:
namespace MySamples.Refactoring
Actualice la definición de clase para que coincida con la siguiente:
internal class CasingReferenceContributorInput: ContributorInput { }
Su clase debe heredar de ContributorInput.
Defina variables miembro privadas adicionales:
private ISqlModelElement _modelElement; private RefactoringPreviewGroup _previewGroup;
Estos miembros se usan para realizar un seguimiento del elemento de modelo en el que está funcionando y el grupo de vista previa al que pertenecen los cambios.
Agregue el constructor de clase:
public CasingReferenceContributorInput(ISqlModelElement modelElement) { _modelElement = modelElement; }
El constructor inicializa el elemento de modelo.
Agregue una propiedad pública de solo lectura para el elemento de modelo:
/// <summary> /// Selected model element /// </summary> public ISqlModelElement ModelElement { get { return _modelElement; } }
Defina un grupo de vista previa adicional para los cambios identificados por este colaborador:
/// <summary> /// Preview group that change proposals belong to /// </summary> public RefactoringPreviewGroup SchemaObjectsPreviewGroup { get { return _previewGroup; } set { _previewGroup = value; } }
El método Reemplazar Equals proporciona una comparación que determina si dos objetos CasingReferenceContributorInput son iguales:
/// <summary> /// Override Equals /// </summary> /// <param name="obj"></param> /// <returns></returns> public override bool Equals(object obj) { CasingContributorInput other = obj as CasingContributorInput; return _modelElement.Equals(other.ModelElement); }
Para este colaborador, las entradas se considerarán iguales si se aplican al mismo elemento de modelo.
Reemplace el método GetHashCode:
/// <summary> /// Override GetHashCode /// </summary> /// <returns></returns> public override int GetHashCode() { Int32 hash = _modelElement.GetHashCode(); return hash; }
En el menú Archivo, haga clic en Guardar CasingReferenceContributorInput.cs.
A continuación, definirá la clase CasingReferenceContributor.
Para definir la clase CasingReferenceContributor
Agregue al proyecto una clase denominada CasingReferenceContributor.
En el editor de código, actualice las instrucciones using para que coincidan con las siguientes:
using System; using System.Collections.Generic; using System.Diagnostics; using Microsoft.Data.Schema.Extensibility; using Microsoft.Data.Schema.SchemaModel; using Microsoft.Data.Schema.ScriptDom.Sql; using Microsoft.Data.Schema.Sql.SchemaModel; using Microsoft.Data.Schema.Sql; using Microsoft.VisualStudio; using Microsoft.VisualStudio.Data.Schema.Package.Refactoring;
Cambie el espacio de nombres a MySamples.Refactoring:
namespace MySamples.Refactoring
Actualice la definición de clase para que coincida con la siguiente:
[DatabaseSchemaProviderCompatibility(typeof(SqlDatabaseSchemaProvider))] internal class CasingReferenceContributor : RefactoringContributor<CasingReferenceContributorInput> { }
Especifique el atributo para declarar que este colaborador es compatible con los proveedores de esquema de base de datos derivados de SqlDatabaseSchemaProvider. Su clase debe heredar RefactoringContributor para la clase CasingReferenceContributorInput.
Defina constantes adicionales y variables miembro privadas:
#region Const private const string PreviewGroupFriendlyName = @"Schema Objects"; private const string PreviewDescription = @"Uppercasing the name of this schema object and all references to this schema object."; private const string PreviewWarning = @"Changing casing of the schema object name may cause errors and warnings when your project is using case-sensitive collations. "; #endregion private RefactoringPreviewGroup _previewGroup;
Las constantes proporcionan información que aparecerá en la ventana de vista previa. El miembro adicional se usa para realizar un seguimiento del grupo de vista previa.
Agregue el constructor de clase:
public CasingReferenceContributor() { }
Reemplace la propiedad PreviewGroup para devolver el grupo que se creó cuando se creó este colaborador:
#region overrides /// <summary> /// Preview group for text files /// </summary> public override RefactoringPreviewGroup PreviewGroup { get { return _previewGroup; } set { _previewGroup = value; } } #endregion
Reemplace el método ContributeChanges(Boolean) para devolver una lista de propuestas de cambios:
/// <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(CasingReferenceContributorInput input) { // cast input into reference input CasingReferenceContributorInput casingReferenceInput = input as CasingReferenceContributorInput; if (casingReferenceInput == null) { throw new ArgumentNullException("input"); } // Make sure CasingReferenceContributor and CasingSymbolContributor for a same refactoring operation // share the same preview group instance. if (casingReferenceInput.SchemaObjectsPreviewGroup != null) { _previewGroup = casingReferenceInput.SchemaObjectsPreviewGroup; } string projectFullName; casingReferenceInput.RefactoringOperation.CurrentProjectHierarchy.GetCanonicalName(VSConstants.VSITEMID_ROOT, out projectFullName); Tuple<IList<ChangeProposal>, IList<ContributorInput>> changes = GetChangesFromReferencedSymbolScripts( projectFullName, casingReferenceInput, casingReferenceInput.ModelElement, true ); return changes; }
ContributeChangesMethod llama al método GetChangesFromReferencedSymbolScripts.
Implemente el método GetChangesFromReferencedSymbolScripts para devolver una lista de propuestas de cambios en scripts que contienen referencias al símbolo que se está actualizado:
public static Tuple<IList<ChangeProposal>, IList<ContributorInput>> GetChangesFromReferencedSymbolScripts( string projectFullName, ContributorInput input, ISqlModelElement modelElement, bool defaultChecked // if the preview group is by default checked in the preview window ) { SampleHelper.CheckNullArgument(input, "input"); SqlSchemaModel dataSchemaModel = input.RefactoringOperation.CurrentDataSchemaModel as SqlSchemaModel; Debug.Assert(dataSchemaModel != null, "The DataSchemaModel is null for current Database project."); // Get all the changes for these schema objects that referencing the changed IModelElement. List<ChangeProposal> allChanges = new List<ChangeProposal>(); Dictionary<string, List<RawChangeInfo>> fileChanges = new Dictionary<string, List<RawChangeInfo>>(); List<RelationshipEntrySource> relationshipEntrySources = GetDependentEntries(dataSchemaModel, modelElement, true, true); foreach (var entry in relationshipEntrySources) { string fileFullPath = entry.Item1.SourceName; if (!string.IsNullOrEmpty(fileFullPath)) { IList<RawChangeInfo> result = AnalyzeRelationshipEntrySource(dataSchemaModel, modelElement, entry.Item2, entry.Item1); if (result != null) { List<RawChangeInfo> fileChange = null; if (!fileChanges.TryGetValue(fileFullPath, out fileChange)) { fileChange = new List<RawChangeInfo>(); fileChanges.Add(fileFullPath, fileChange); } fileChange.AddRange(result); } } } // Convert the offsets returned from ScriptDom to the line based offsets foreach (string fileFullPath in fileChanges.Keys) { allChanges.AddRange(SampleHelper.ConvertOffsets(projectFullName, fileFullPath, fileChanges[fileFullPath], defaultChecked)); } // Change propagation is not considered in this sample. // Thus the second value in the returned Tuple is set to null return new Tuple<IList<ChangeProposal>, IList<ContributorInput>>(allChanges, null); }
Este método recupera una lista de todas las dependencias del símbolo actualizado. A continuación, llama al método AnalyzeRelationshipEntrySource para cada referencia con el fin de identificar cualquier cambio adicional que sea necesario.
Agregue el método AnalyzeRelationshipEntrySource:
public static IList<RawChangeInfo> AnalyzeRelationshipEntrySource( SqlSchemaModel dataSchemaModel, ISqlModelElement modelElement, ISourceInformation relationshipEntrySource) { SampleHelper.CheckNullArgument(dataSchemaModel, "dataSchemaModel"); List<Identifier> identifiers = new List<Identifier>(); TSqlFragment fragment = relationshipEntrySource.ScriptDom as TSqlFragment; // handle expressions if (fragment is SelectColumn) { Expression exp = ((SelectColumn)fragment).Expression;// as Expression; fragment = exp as TSqlFragment; } else if (fragment is ExpressionWithSortOrder) { Expression exp = ((ExpressionWithSortOrder)fragment).Expression; // as Expression; fragment = exp as TSqlFragment; } else if (fragment is ExpressionGroupingSpecification) { Expression exp = ((ExpressionGroupingSpecification)fragment).Expression; // as Expression; fragment = exp as TSqlFragment; } // handle different fragment if (fragment is Identifier) { identifiers.Add((Identifier)fragment); ; } else if (fragment is Column) { identifiers.AddRange(((Column)fragment).Identifiers); } else if (fragment is ColumnWithSortOrder) { identifiers.Add(((ColumnWithSortOrder)fragment).ColumnIdentifier); } else if (fragment is SchemaObjectName) { identifiers.Add(((SchemaObjectName)fragment).BaseIdentifier); } else if (fragment is SchemaObjectTableSource) { identifiers.Add(((SchemaObjectTableSource)fragment).SchemaObject.BaseIdentifier); } else if (fragment is SchemaObjectDataModificationTarget) { identifiers.Add(((SchemaObjectDataModificationTarget)fragment).SchemaObject.BaseIdentifier); } else if (fragment is FunctionCall) { FunctionCall funcCall = (FunctionCall)fragment; IdentifiersCallTarget identsCallTarget = funcCall.CallTarget as IdentifiersCallTarget; if (identsCallTarget != null) { identifiers.AddRange(identsCallTarget.Identifiers); } identifiers.Add(funcCall.FunctionName); } else if (fragment is ProcedureReference) { SchemaObjectName procRefName = ((ProcedureReference)fragment).Name; if (procRefName != null) { identifiers.Add(procRefName.BaseIdentifier); } } else if (fragment is TriggerObject) { SchemaObjectName triggerName = ((TriggerObject)fragment).Name; if (triggerName != null) { identifiers.Add(triggerName.BaseIdentifier); } } else if (fragment is FullTextIndexColumn) { identifiers.Add(((FullTextIndexColumn)fragment).Name); } else if (fragment is SecurityTargetObject) { identifiers.AddRange(((SecurityTargetObject)fragment).ObjectName.Identifiers); } else // other types of fragments are not handled in this sample { Debug.WriteLine(string.Format("Uppercasing referencing object of type {0} is not implemented yet.", fragment.GetType().Name)); } List<RawChangeInfo> changes = new List<RawChangeInfo>(); string oldName = SampleHelper.GetModelElementSimpleName(modelElement); if (identifiers.Count > 0 && oldName.Length > 0) { string newName = oldName.Substring(0, 1).ToUpper() + oldName.Substring(1); // upper casing the first letter if (string.CompareOrdinal(oldName, newName) != 0) { // list of changes for this relationship entry RawChangeInfo change = null; foreach (Identifier idf in identifiers) { change = SampleHelper.AddOffsestFromIdentifier(idf, oldName, newName, true); if (change != null) { changes.Add(change); } } } } return changes; }
Este método recupera una lista de cambios que se deben realizar en los fragmentos de script que dependen del símbolo actualizado.
Agregue el método GetDependentEntries:
/// <summary> /// Get all relating relationship entries for the model element and its composing and hierarchical children /// </summary> internal static List<System.Tuple<ISourceInformation, IModelRelationshipEntry>> GetDependentEntries( SqlSchemaModel dataSchemaModel, ISqlModelElement modelElement, bool ignoreComposedRelationship, bool includeChildDependencies) { SampleHelper.CheckNullArgument(dataSchemaModel, "dataSchemaModel"); SampleHelper.CheckNullArgument(modelElement, "modelElement"); var dependencies = new List<System.Tuple<ISourceInformation, IModelRelationshipEntry>>(); List<IModelRelationshipEntry> relatingRelationships = new List<IModelRelationshipEntry>(); GetDependentEntries(modelElement, dataSchemaModel, new Dictionary<IModelElement, Object>(), relatingRelationships, includeChildDependencies); foreach (IModelRelationshipEntry entry in relatingRelationships) { ModelRelationshipType relationshipType = entry.RelationshipClass.ModelRelationshipType; if (!ignoreComposedRelationship || (relationshipType != ModelRelationshipType.Composing)) { ISqlModelElement relatingElement = entry.FromElement as ISqlModelElement; Debug.Assert(relatingElement != null, "Relating element got from ModelStore is null."); foreach (var si in relatingElement.GetRelationshipEntrySources(entry)) { dependencies.Add(new System.Tuple<ISourceInformation, IModelRelationshipEntry>(si, entry)); } } } return dependencies; } private static void GetDependentEntries( IModelElement modelElement, DataSchemaModel dataSchemaModel, Dictionary<IModelElement, Object> visitElement, List<IModelRelationshipEntry> relationshipEntries, Boolean includeChildDependencies) { if (modelElement != null && !visitElement.ContainsKey(modelElement)) { visitElement[modelElement] = null; IList<IModelRelationshipEntry> relatingRelationships = modelElement.GetReferencingRelationshipEntries(); relationshipEntries.AddRange(relatingRelationships); if (includeChildDependencies) { // First loop through all composed children of this element, and get their relationship entries as well foreach (IModelRelationshipEntry entry in modelElement.GetReferencedRelationshipEntries()) { if (entry.RelationshipClass.ModelRelationshipType == ModelRelationshipType.Composing) { GetDependentEntries(entry.Element, dataSchemaModel, visitElement, relationshipEntries, includeChildDependencies); } } // Then loop through all hierarchical children of this element, add their dependents to the list. foreach (IModelRelationshipEntry entry in relatingRelationships) { if (entry.RelationshipClass.ModelRelationshipType == ModelRelationshipType.Hierarchical) { GetDependentEntries(entry.FromElement, dataSchemaModel, visitElement, relationshipEntries, includeChildDependencies); } } } } }
En el menú Archivo, haga clic en Guardar CasingReferenceContributor.cs.
A continuación, configurará y compilará el ensamblado.
Para firmar y compilar el ensamblado
En el menú Proyecto, haga clic en Propiedades de CasingRefactoringType.
Haga clic en la ficha Firma.
Haga clic en Firmar el ensamblado.
En Elija un archivo de clave de nombre seguro, haga clic en <Nuevo>.
En el cuadro de diálogo Crear clave de nombre seguro, en Nombre del archivo de clave, escriba MyRefKey.
(opcional) Puede especificar una contraseña para el archivo de clave de nombre seguro.
Haga clic en Aceptar.
En el menú Archivo, haga clic en Guardar todo.
En el menú Generar, haga clic en Generar solución.
A continuación, debe instalar y registrar el ensamblado para que aparezca como una condición de prueba disponible.
Instalar y registrar el ensamblado
Para instalar el ensamblado CasingRefactoringType
Cree una carpeta denominada MyExtensions en la carpeta %Program Files%\Microsoft Visual Studio 10.0\VSTSDB\Extensions.
Copie el ensamblado firmado (CasingRefactoringType.dll) a la carpeta %Program Files%\Microsoft Visual Studio 10.0\VSTSDB\Extensions\MyExtensions.
Nota
Se recomienda no copiar directamente los archivos XML a la carpeta %Program Files%\Microsoft Visual Studio 10.0\VSTSDB\Extensions. Si usa una subcarpeta en su lugar, evitará cambios accidentales en los demás archivos proporcionados con Visual Studio.
A continuación, debe registrar el ensamblado, un tipo de extensión de características, para que aparezca en Visual Studio.
Para registrar el ensamblado CasingRefactoringType
En el menú Ver, haga clic en Otras ventanas y, a continuación, haga clic en Ventana Comandos para abrir la ventana Comandos.
En la ventana Comando, escriba el código siguiente. En FilePath, sustituya la ruta de acceso y el nombre de archivo por la ruta y el nombre de su archivo .dll compilado. Debe escribir la ruta de acceso y el nombre de archivo entre comillas.
Nota
De forma predeterminada, la ruta de acceso del archivo .dll compilado es rutaDeAccesoDeSolución\bin\Debug o rutaDeAccesoDeSolución\bin\Release.
? System.Reflection.Assembly.LoadFrom("FilePath").FullName
? System.Reflection.Assembly.LoadFrom(@"FilePath").FullName
Presione Entrar.
Copie la línea resultante al Portapapeles. Debe ser similar a la siguiente:
"GeneratorAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=nnnnnnnnnnnnnnnn"
Abra un editor de texto sin formato, como el Bloc de notas.
Importante En Windows Vista y Microsoft Windows Server 2008, abra el editor como un administrador para que pueda guardar el archivo en su carpeta Archivos de programa.
Proporcione la siguiente información, especificando el nombre del ensamblado, el símbolo de clave pública y el tipo de extensión:
<?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.CasingRefactorCommand" assembly=" CasingRefactoringType, Version=1.0.0.0, Culture=neutral, PublicKeyToken=<enter key here>" enabled="true" /> <extension type="MySamples.Refactoring.CasingSymbolContributor" assembly="CasingRefactoringType, Version=1.0.0.0, Culture=neutral, PublicKeyToken=<enter key here>" enabled="true" /> <extension type="MySamples.Refactoring.CasingReferenceContributor" assembly="CasingRefactoringType, Version=1.0.0.0, Culture=neutral, PublicKeyToken=<enter key here>" enabled="true" /> </extensions>
Use este archivo XML para registrar la clase que hereda de RefactoringCommand y todas las clases relacionadas que se derivan de RefactoringContributor.
Guarde el archivo como CasingRefactoringType.extensions.xml en la carpeta %Program Files%\Microsoft Visual Studio 10.0\VSTSDB\Extensions\MyExtensions.
Cierre Visual Studio.
A continuación, creará un proyecto de base de datos muy simple para probar el nuevo tipo de refactorización.
Probar el nuevo tipo de refactorización
Para crear un proyecto de base de datos
En el menú Archivo, elija Nuevo y, a continuación, haga clic en Proyecto.
En Plantillas instaladas, expanda el nodo Base de datos y, a continuación, haga clic en el nodo SQL Server.
En la lista de plantillas, haga clic en Proyecto de base de datos de SQL Server 2008.
Haga clic en Aceptar para aceptar el nombre de proyecto predeterminado y crear el proyecto.
Se creará el proyecto de base de datos vacío.
Para agregar una tabla con una clave principal
En el menú Ver, haga clic en Vista de esquema.
En Vista de esquema, expanda el nodo Esquemas, expanda el nodo dbo, haga clic con el botón secundario en el nodo Tablas, elija Agregar y haga clic en Tabla.
En el cuadro de diálogo Agregar nuevo elemento, en Nombre, escriba employee.
Nota
Usará intencionadamente una letra minúscula para comenzar el nombre de tabla.
Haga clic en Aceptar.
Expanda el nodo Tablas, haga clic con el botón secundario en el nodo employee, elija Agregar y haga clic en Clave principal.
En el cuadro de diálogo Agregar nuevo elemento, en Nombre, escriba PK_Employee_column_1.
Haga clic en Aceptar.
A continuación, usará el nuevo tipo de refactorización para cambiar el nombre de tabla y todas las referencias a él.
Para usar el nuevo tipo de refactorización para actualizar el nombre de tabla
En Vista de esquema, haga clic con el botón secundario en el nodo de la tabla employee, elija Refactorizar y haga clic en Poner primera letra en mayúscula.
En este tutorial ha definido ese nuevo tipo de refactorización.
En el cuadro de diálogo Obtener vista previa de cambios, examine los cambios y haga clic en Aplicar.
El nombre de tabla se actualizará para ser Employee. También se actualizará la referencia a esa tabla en la clave principal.
Pasos siguientes
Puede crear sus propios tipos adicionales de refactorización de base de datos. También puede agregar más colaboradores para permitir que un tipo existente de refactorización de base de datos funcione en tipos adicionales de archivos u objetos.
Vea también
Tareas
Conceptos
Crear tipos de refactorización de base de datos personalizados o destinos