Tutorial: Crear un ensamblado de regla de análisis de código estático personalizada para SQL
Este tema paso a paso muestra los pasos utilizados para crear una regla de análisis de código SQL. La regla creada en este tutorial se utiliza para evitar la presencia de instrucciones WAITFOR DELAY en procedimientos almacenados, desencadenadores y funciones.
En este tutorial, creará una regla personalizada para análisis de código estático Transact-SQL utilizando los procesos siguientes:
Crear una biblioteca de clases, habilitar la firma para ese proyecto y agregar las referencias necesarias.
Crear dos clases de C# auxiliares.
Crear una clase de regla personalizada de C#.
Crear un archivo XML que se utiliza para registrar el ensamblado.
Copiar la DLL y el archivo XML resultantes que se crean en el directorio Extensions para registrarlo.
Comprobar que se aplica la nueva regla de análisis de código.
Requisitos previos
Para poder completar este tutorial, debe tener instalado Visual Studio Premium o Visual Studio Ultimate.
Crear una regla de análisis de código personalizada para SQL
En primer lugar, creará una biblioteca de clases.
Para crear una biblioteca de clases
En el menú Archivo, haga clic en Nuevo y, a continuación, haga clic en Proyecto.
En el cuadro de diálogo Nuevo proyecto, en la lista de Plantillas instaladas, haga clic en Visual C#.
En el recuadro de detalles, seleccione Biblioteca de clases.
En el cuadro de texto Nombre, escriba SampleRules y, a continuación, haga clic en Aceptar.
A continuación, firmará el proyecto.
Para habilitar la firma para el proyecto
Con el nodo del proyecto SampleRules seleccionado en el Explorador de soluciones, en el menú Proyecto, haga clic en Propiedades (o haga clic con el botón secundario en el nodo del proyecto en el Explorador de soluciones y, a continuación, haga clic en Propiedades).
Haga clic en la ficha Firma.
Active la casilla Firmar el ensamblado.
Especifique un nuevo archivo de claves. En la lista desplegable Elija un archivo de clave de nombre completo, seleccione <Nuevo...>.
Aparecerá el cuadro de diálogo Crear clave de nombre seguro. Para obtener más información, vea Crear clave de nombre seguro (Cuadro de diálogo).
En el cuadro de diálogo Crear clave de nombre seguro, escriba SampleRulesKey en el cuadro de texto Nombre del nuevo archivo de clave. No tiene que proporcionar una contraseña para este tutorial. Para obtener más información, vea Administrar la firma de ensamblados y manifiestos.
A continuación, agregará las referencias necesarias al proyecto.
Para agregar las referencias aplicables al proyecto
En el Explorador de soluciones, seleccione el proyecto SampleRules.
Haga clic en la opción Agregar referencia del menú Proyecto.
Se abrirá el cuadro de diálogo Agregar referencia. Para obtener más información, vea Cómo: Agregar o quitar referencias en Visual Studio.
Seleccione la pestaña .NET.
En la columna Nombre de componente, busque los componentes siguientes:
Nota
Para seleccionar varios componentes, mantenga presionada la tecla CTRL y haga clic en cada uno de ellos.
Haga clic en Aceptar una vez seleccionados todos los componentes necesarios.
Las referencias seleccionadas aparecerán bajo el nodo Referencias del proyecto en el Explorador de soluciones.
Crear las clases auxiliares de la regla de análisis de código personalizada
Antes de crear la clase para la propia regla, agregará una clase visitante y una clase auxiliar al proyecto.
Sugerencia |
---|
Estas clases pueden ser útiles para crear reglas personalizadas adicionales. |
La primera clase que debe definir es la clase WaitForDelayVisitor, derivada de TSqlConcreteFragmentVisitor. Esta clase proporciona acceso a las instrucciones WAITFOR DELAY del modelo.
Para definir la clase WaitForDelayVisitor
En el Explorador de soluciones, seleccione el proyecto SampleRules.
En el menú Proyecto, elija Agregar clase.
Aparecerá el cuadro de diálogo Agregar nuevo elemento.
En el cuadro de texto Nombre, escriba WaitForDelayVisitor.cs y haga clic en el botón Agregar.
El archivo WaitForDelayVisitor.cs se agrega al proyecto en el Explorador de soluciones.
Abra el archivo WaitForDelayVisitor.cs y actualice el contenido de modo que coincida con el siguiente código:
using System.Collections.Generic; using Microsoft.Data.Schema.ScriptDom.Sql; namespace SampleRules { class WaitForDelayVistor { } }
En la declaración de clase, cambie el modificador de acceso a internal y derive la clase de TSqlConcreteFragmentVisitor:
internal class WaitForDelayVisitor : TSqlConcreteFragmentVisitor { }
Agregue el siguiente código para definir la variable miembro List:
private List<WaitForStatement> _waitForDelayStatments;
Defina el constructor de clase agregando el código siguiente:
#region ctor public WaitForDelayVisitor() { _waitForDelayStatments = new List<WaitForStatement>(); } #endregion
Defina la propiedad de solo lectura WaitForDelayStatements agregando el siguiente código:
#region properties public List<WaitForStatement> WaitForDelayStatements { get { return _waitForDelayStatments; } } #endregion
Reemplace el método ExplicitVisit agregando el siguiente código:
#region overrides public override void ExplicitVisit(WaitForStatement node) { // We are only interested in WAITFOR DELAY occurrences if (node.WaitForOption == WaitForOption.Delay) { _waitForDelayStatments.Add(node); } } #endregion
Este método visita a las instrucciones WAITFOR del modelo y agrega aquéllas que tienen la opción DELAY especificada a la lista de instrucciones WAITFOR DELAY. La clase clave a la que se hace referencia aquí es WaitForStatement.
Haga clic en la opción Guardar del menú Archivo.
La segunda clase es SqlRuleUtils.cs, que contiene algunos métodos de utilidad que serán utilizados por la clase de regla de análisis de código personalizada que creará más adelante en este tutorial, en la sección Crear la clase de regla de análisis de código personalizada. Estos métodos incluyen:
GetElementName Se utiliza para obtener el nombre completo con escape de un elemento del modelo
UpdateProblemPosition Se utiliza para calcular información de línea y columna.
ReadFileContent Se utiliza para leer el contenido de un archivo.
GetElementSourceFile Se utiliza para adquirir el archivo de código fuente.
ComputeLineColumn Se utiliza para convertir el desplazamiento de ScriptDom a línea y columna en archivos de script.
Para agregar el archivo SqlRuleUtils.cs al proyecto
En el Explorador de soluciones, seleccione el proyecto SampleRules.
En el menú Proyecto, elija Agregar clase.
Aparecerá el cuadro de diálogo Agregar nuevo elemento.
En el cuadro de texto Nombre, escriba SqlRuleUtils.cs y haga clic en el botón Agregar.
El archivo SqlRuleUtils.cs se agrega al proyecto en el Explorador de soluciones.
Abra el archivo SqlRuleUtils.cs y agregue las instrucciones using siguientes al archivo:
using System; using System.Diagnostics; using System.IO; using Microsoft.Data.Schema.SchemaModel; using Microsoft.Data.Schema.Sql.SchemaModel; using Microsoft.Data.Schema.StaticCodeAnalysis; using Microsoft.Data.Schema; namespace SampleRules { }
En la declaración de clase SqlRuleUtils, cambie el modificador de acceso a public static:
public static class SqlRuleUtils { }
Agregue el código siguiente para crear el método GetElementName, que utiliza SqlSchemaModel y ISqlModelElement como parámetros de entrada:
/// <summary> /// Get escaped fully qualified name of a model element /// </summary> /// <param name="sm">schema model</param> /// <param name="element">model element</param> /// <returns>name of the element</returns> public static string GetElementName(SqlSchemaModel sm, ISqlModelElement element) { return sm.DatabaseSchemaProvider.UserInteractionServices.GetElementName(element, ElementNameStyle.EscapedFullyQualifiedName); }
Agregue el código siguiente para crear el método ReadFileContent:
/// <summary> /// Read file content from a file. /// </summary> /// <param name="filePath"> file path </param> /// <returns> file content in a string </returns> public static string ReadFileContent(string filePath) { // Verify 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; }
Agregue el código siguiente para crear el método GetElementSourceFile, que utiliza IModelElement como parámetro de entrada y String para recuperar el nombre de archivo. El método convierte IModelElement como IScriptSourcedModelElement y, a continuación, utiliza ISourceInformation para determinar la ruta de acceso del archivo de script del elemento de modelo.
/// <summary> /// Get the corresponding script file path from a model element. /// </summary> /// <param name="element">model element</param> /// <param name="fileName">file path of the scripts corresponding to the model element</param> /// <returns></returns> private static Boolean GetElementSourceFile(IModelElement element, out String fileName) { fileName = null; IScriptSourcedModelElement scriptSourcedElement = element as IScriptSourcedModelElement; if (scriptSourcedElement != null) { ISourceInformation elementSource = scriptSourcedElement.PrimarySource; if (elementSource != null) { fileName = elementSource.SourceName; } } return String.IsNullOrEmpty(fileName) == false; }
Agregue el código siguiente para crear el método ComputeLineColumn:
/// This method converts offset from ScriptDom to line\column in script files. /// A line is defined as a sequence of characters followed by a carriage return ("\r"), /// a line feed ("\n"), or a carriage return immediately followed by a line feed. public static bool ComputeLineColumn(string text, Int32 offset, Int32 length, out Int32 startLine, out Int32 startColumn, out Int32 endLine, out Int32 endColumn) { const char LF = '\n'; const char CR = '\r'; // Setting the initial value of line and column to 0 since VS auto-increments by 1. startLine = 0; startColumn = 0; endLine = 0; endColumn = 0; int textLength = text.Length; if (offset < 0 || length < 0 || offset + length > textLength) { return false; } for (int charIndex = 0; charIndex < length + offset; ++charIndex) { char currentChar = text[charIndex]; Boolean afterOffset = charIndex >= offset; if (currentChar == LF) { ++endLine; endColumn = 0; if (afterOffset == false) { ++startLine; startColumn = 0; } } else if (currentChar == CR) { // CR/LF combination, consuming LF. if ((charIndex + 1 < textLength) && (text[charIndex + 1] == LF)) { ++charIndex; } ++endLine; endColumn = 0; if (afterOffset == false) { ++startLine; startColumn = 0; } } else { ++endColumn; if (afterOffset == false) { ++startColumn; } } } return true; }
Agregue el código siguiente para crear el método UpdateProblemPosition, que utiliza DataRuleProblem como parámetro de entrada:
/// <summary> /// Compute the start Line/Col and the end Line/Col to update problem info /// </summary> /// <param name="problem">problem found</param> /// <param name="offset">offset of the fragment having problem</param> /// <param name="length">length of the fragment having problem</param> public static void UpdateProblemPosition(DataRuleProblem problem, int offset, int length) { if (problem.ModelElement != null) { String fileName = null; int startLine = 0; int startColumn = 0; int endLine = 0; int endColumn = 0; bool ret = GetElementSourceFile(problem.ModelElement, out fileName); if (ret) { string fullScript = ReadFileContent(fileName); if (fullScript != null) { if (ComputeLineColumn(fullScript, offset, length, out startLine, out startColumn, out endLine, out endColumn)) { problem.FileName = fileName; problem.StartLine = startLine + 1; problem.StartColumn = startColumn + 1; problem.EndLine = endLine + 1; problem.EndColumn = endColumn + 1; } else { Debug.WriteLine("Could not compute line and column"); } } } } }
Haga clic en la opción Guardar del menú Archivo.
A continuación, agregue un archivo de recursos que definirá el nombre y la descripción de la regla y la categoría en la que aparecerá ésta en la interfaz de configuración de regla.
Para agregar un archivo de recursos y tres cadenas de recursos
En el Explorador de soluciones, seleccione el proyecto SampleRules.
En el menú Proyecto, seleccione Agregar nuevo elemento.
Aparecerá el cuadro de diálogo Agregar nuevo elemento.
En la lista de Plantillas instaladas, haga clic en General.
En el recuadro de detalles, haga clic en Archivo de recursos.
En Nombre, escriba SampleRuleResource.resx.
Aparece el editor de recursos, sin recursos definidos todavía.
Defina tres cadenas de recursos como sigue:
Nombre
Valor
AvoidWaitForDelay_ProblemDescription
La instrucción WAITFOR DELAY se encontró en {0}.
AvoidWaitForDelay_RuleName
Evite usar instrucciones WaitFor en procedimientos almacenados, funciones y desencadenadores.
CategorySamples
SamplesCategory
En el menú Archivo, haga clic en Guardar SampleRuleResource.resx.
A continuación, defina una clase que haga referencia a los recursos del archivo de recursos que Visual Studio utiliza para mostrar información sobre su regla en la interfaz de usuario.
Para definir la clase SampleConstants
En el Explorador de soluciones, seleccione el proyecto SampleRules.
En el menú Proyecto, elija Agregar clase.
Aparecerá el cuadro de diálogo Agregar nuevo elemento.
En el cuadro de texto Nombre, escriba SampleRuleConstants.cs y haga clic en el botón Agregar.
El archivo SampleRuleConstants.cs se agrega al proyecto en el Explorador de soluciones.
Abra el archivo SampleRuleConstants.cs y agregue las instrucciones using siguientes al archivo:
namespace SampleRules { internal class SampleConstants { public const string NameSpace = "SamplesRules"; public const string ResourceBaseName = "SampleRules.SampleRuleResource"; public const string CategorySamples = "CategorySamples"; public const string AvoidWaitForDelayRuleId = "SR1004"; public const string AvoidWaitForDelay_RuleName = "AvoidWaitForDelay_RuleName"; public const string AvoidWaitForDelay_ProblemDescription = "AvoidWaitForDelay_ProblemDescription"; } }
Haga clic en la opción Guardar del menú Archivo.
Crear la clase de regla de análisis de código personalizada
Ahora que ha agregado las clases auxiliares que la regla de análisis de código personalizada utilizará, creará una clase de regla personalizada y la denominará AvoidWaitForDelayRule. La regla personalizada AvoidWaitForDelayRule se utilizará para ayudar a los desarrolladores de base de datos a evitar la presencia de instrucciones WAITFOR DELAY en procedimientos almacenados, desencadenadores y funciones.
Para crear la clase AvoidWaitForDelayRule
En el Explorador de soluciones, seleccione el proyecto SampleRules.
En el menú Proyecto, seleccione Nueva carpeta.
Aparece una nueva carpeta en el Explorador de soluciones. Asigne el nombre AvoidWaitForDelayRule a la carpeta.
En el Explorador de soluciones, compruebe que la carpeta AvoidWaitForDelayRule está seleccionada.
En el menú Proyecto, elija Agregar clase.
Aparecerá el cuadro de diálogo Agregar nuevo elemento.
En el cuadro de texto Nombre, escriba AvoidWaitForDelayRule.cs y haga clic en el botón Agregar.
El archivo AvoidWaitForDelayRule.cs se agrega a la carpeta AvoidWaitForDelayRule del proyecto en el Explorador de soluciones.
Abra el archivo AvoidWaitForDelayRule.cs y agregue las instrucciones using siguientes al archivo:
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.Data.Schema.StaticCodeAnalysis; namespace SampleRules { public class AvoidWaitForDelayRule { } }
Nota
Debe cambiar el espacio de nombres SampleRules.AvoidWaitForDelayRule a SampleRules.
En la declaración de clase AvoidWaitForDelayRule, cambie el modificador de acceso a public:
/// <summary> /// This is a SQL rule which returns a warning message /// whenever there is a WAITFOR DELAY statement appears inside a subroutine body. /// This rule only applies to SQL stored procedures, functions and triggers. /// </summary> public class AvoidWaitForDelayRule
Derive la clase AvoidWaitForDelayRule de la clase base StaticCodeAnalysisRule:
public class AvoidWaitForDelayRule : StaticCodeAnalysisRule
Agregue DatabaseSchemaProviderCompatibilityAttribute, DataRuleAttribute y SupportedElementTypeAttribute a su clase. Para obtener más información sobre la compatibilidad de extensión de características, vea Extender las características de base de datos de Visual Studio.
[DatabaseSchemaProviderCompatibility(typeof(SqlDatabaseSchemaProvider))] [DataRuleAttribute( SampleConstants.NameSpace, SampleConstants.AvoidWaitForDelayRuleId, SampleConstants.ResourceBaseName, SampleConstants.AvoidWaitForDelay_RuleName, SampleConstants.CategorySamples, DescriptionResourceId = SampleConstants.AvoidWaitForDelay_ProblemDescription)] [SupportedElementType(typeof(ISqlProcedure))] [SupportedElementType(typeof(ISqlTrigger))] [SupportedElementType(typeof(ISqlFunction))] public class AvoidWaitForDelayRule : StaticCodeAnalysisRule
DataRuleAttribute especifica información que aparece en Visual Studio al configurar reglas de análisis de código de base de datos. SupportedElementTypeAttribute define los tipos de elementos a los que se aplicará esta regla. En este caso, la regla se aplicará a procedimientos almacenados, desencadenadores y funciones.
Agregue un reemplazo para el método Analyze, que utiliza DataRuleSetting y DataRuleExecutionContext como parámetros de entrada. Este método devuelve una lista de posibles problemas.
El método obtiene IModelElement y TSqlFragment del parámetro de contexto. SqlSchemaModel y ISqlModelElement se obtienen del elemento del modelo. A continuación, se utiliza la clase WaitForDelayVisitor para obtener una lista de todas las instrucciones WAITFOR DELAY del modelo.
Para cada WaitForStatement de esa lista, se crea DataRuleProblem.
#region Overrides /// <summary> /// Analyze the model element /// </summary> public override IList<DataRuleProblem> Analyze(DataRuleSetting ruleSetting, DataRuleExecutionContext context) { List<DataRuleProblem> problems = new List<DataRuleProblem>(); IModelElement modelElement = context.ModelElement; // this rule does not apply to inline table-valued function // we simply do not return any problem if (modelElement is ISqlInlineTableValuedFunction) { return problems; } // casting to SQL specific SqlSchemaModel sqlSchemaModel = modelElement.Model as SqlSchemaModel; Debug.Assert(sqlSchemaModel!=null, "SqlSchemaModel is expected"); ISqlModelElement sqlElement = modelElement as ISqlModelElement; Debug.Assert(sqlElement != null, "ISqlModelElement is expected"); // Get ScriptDom for this model element TSqlFragment sqlFragment = context.ScriptFragment as TSqlFragment; Debug.Assert(sqlFragment != null, "TSqlFragment is expected"); // visitor to get the ocurrences of WAITFOR DELAY statements WaitForDelayVisitor visitor = new WaitForDelayVisitor(); sqlFragment.Accept(visitor); List<WaitForStatement> waitforDelayStatements = visitor.WaitForDelayStatements; // Create problems for each WAITFOR DELAY statement found foreach (WaitForStatement waitForStatement in waitforDelayStatements) { DataRuleProblem problem = new DataRuleProblem(this, String.Format(CultureInfo.CurrentCulture, this.RuleProperties.Description, SqlRuleUtils.GetElementName(sqlSchemaModel, sqlElement)), sqlElement); SqlRuleUtils.UpdateProblemPosition(problem, waitForStatement.StartOffset, waitForStatement.FragmentLength); problems.Add(problem); } return problems; } #endregion
Haga clic en la opción Guardar del menú Archivo.
A continuación, compilará el proyecto.
Para compilar el proyecto
- En el menú Generar, haga clic en Generar solución.
A continuación, recopilará la información del ensamblado generada en el proyecto, incluidas la versión, referencia cultural y PublicKeyToken.
Para recopilar información del ensamblado
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, FilePath es Proyectos\SampleRules\SampleRules\bin\Debug\suDLL o Proyectos\SampleRules\SampleRules\bin\Release\suDLL.
? System.Reflection.Assembly.LoadFrom(@"FilePath")
Presione ENTRAR. La línea debe tener un aspecto similar al siguiente con el PublicKeyToken concreto:
"SampleRules, Version=1.0.0.0, Culture=neutral, PublicKeyToken=nnnnnnnnnnnnnnnn"
Anote o copie esta información de ensamblado; se usará en el procedimiento siguiente.
A continuación, creará un archivo XML usando la información del ensamblado que recopiló en el procedimiento anterior.
Para crear el archivo XML
En el Explorador de soluciones, seleccione el proyecto SampleRules.
En el menú Proyecto, seleccione Agregar nuevo elemento.
En el recuadro Plantillas, busque y seleccione el elemento Archivo XML.
En el cuadro de texto Nombre, escriba SampleRules.Extensions.xml y haga clic en el botón Agregar.
El archivo SampleRules.Extensions.xml se agrega al proyecto en el Explorador de soluciones.
Abra el archivo SampleRules.Extensions.xml y actualícelo para que coincida con el XML siguiente. Reemplace los valores de versión, referencia cultural y PublicKeyToken por los que recuperó en el procedimiento anterior.
<?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="SampleRules.AvoidWaitForDelayRule" assembly="SampleRules, Version=1.0.0.0, Culture=neutral, PublicKeyToken=b4deb9b383d021b0" enabled="true"/> </extensions>
Haga clic en la opción Guardar del menú Archivo.
A continuación, copiará la información del ensamblado y el archivo XML en el directorio Extensions. Cuando Visual Studio se inicie, identificará las extensiones en el directorio Microsoft Visual Studio 10.0\VSTSDB\Extensions y sus subdirectorios, y las registrará para su uso en la sesión.
Para copiar la información del ensamblado y el archivo XML en el directorio Extensions
Cree una nueva carpeta denominada CustomRules en el directorio Microsoft Visual Studio 10.0\VSTSDB\Extensions\.
Copie el archivo de ensamblado SampleRules.dll del directorio Proyectos\SampleRules\SampleRules\bin\Debug\ al directorio Microsoft Visual Studio 10.0\VSTSDB\Extensions\CustomRules que ha creado.
Copie el archivo SampleRules.Extensions.xml del directorio Proyectos\SampleRules\SampleRules al directorio Microsoft Visual Studio 10.0\VSTSDB\Extensions\CustomRules que ha creado.
Nota
Un procedimiento recomendado es colocar los ensamblados de extensión en una carpeta del directorio Microsoft Visual Studio 10.0\VSTSDB\Extensions. Esto le ayudará a identificar qué extensiones se incluían con el producto, y cuáles son creaciones personalizadas suyas. Se recomienda el uso de carpetas para organizar las extensiones por categorías específicas.
A continuación, iniciará una nueva sesión de Visual Studio y creará un proyecto de base de datos.
Para iniciar una nueva sesión de Visual Studio y crear un proyecto de base de datos
Inicie una segunda sesión de Visual Studio.
En el menú Archivo, haga clic en Nuevo y, a continuación, haga clic en Proyecto.
En el cuadro de diálogo Nuevo proyecto, en la lista de Plantillas instaladas, expanda el nodo Proyectos de base de datos y, a continuación, haga clic en SQL Server.
En el recuadro de detalles, seleccione Proyecto de base de datos de SQL Server 2008.
En el cuadro de texto Nombre, escriba SampleRulesDB y, a continuación, haga clic en Aceptar.
Finalmente, verá la nueva regla en el proyecto de SQL Server.
Para ver la nueva regla de análisis de código AvoidWaitForRule
En el Explorador de soluciones, seleccione el proyecto SampleRulesDB.
En el menú Proyecto, haga clic en Propiedades.
Se muestra la página de propiedades de SampleRulesDB.
Haga clic en Análisis de código.
Debe ver una nueva categoría denominada CategorySamples.
Expanda CategorySamples.
Debe ver SR1004: Evite la instrucción WAITFOR DELAY en procedimientos almacenados, desencadenadores y funciones.
Vea también
Tareas
Cómo: Registrar y administrar las extensiones de características
Cómo: Distribuir extensiones de características personalizadas a los miembros del equipo
Referencia
Conceptos
Extender las características de base de datos de Visual Studio
Analizar el código de base de datos para mejorar la calidad del código
Analizar la calidad del código administrado mediante el análisis de código