Compartir a través de


Redacción de reglas de análisis de código personalizadas

Se aplica a:SQL ServerAzure SQL DatabaseAzure SQL Managed InstanceBase de datos de Azure SQL de Microsoft Fabric

En este tutorial se muestran los pasos utilizados para crear una regla de Code Analysis de SQL Server. La regla creada en este tutorial se utiliza para evitar las instrucciones WAITFOR DELAY en procedimientos almacenados, desencadenadores y funciones.

En este tutorial, crearás una regla personalizada de análisis de código estático de Transact-SQL mediante el uso de los siguientes pasos:

  1. Cree un proyecto de biblioteca de clases, habilite la firma para el proyecto y agregue las referencias necesarias.
  2. Crea dos clases auxiliares de C#.
  3. Crea una clase de regla personalizada de C#.
  4. Compile el proyecto de biblioteca de clases.
  5. Instale y pruebe la nueva regla de Code Analysis.

Excepto para las instrucciones de Visual Studio (SQL Server Data Tools), la guía se centra en proyectos SQL de estilo SDK.

Requisitos previos

Necesitará los componentes siguientes para completar este tutorial:

  • Una versión de Visual Studio instalada que incluya SQL Server Data Tools y admita el desarrollo de C# .NET Framework.
  • Un proyecto de SQL que contenga objetos de SQL Server.
  • Una instancia de SQL Server a la que pueda implementar un proyecto de base de datos.

Este tutorial está destinado a usuarios que ya están familiarizados con las características de SQL Server de SQL Server Data Tools. Debes estar familiarizado con los conceptos de Visual Studio, por ejemplo, cómo crear una biblioteca de clases, agregar paquetes NuGet o cómo utilizar el editor de código para agregar código a una clase.

Nota:

Debido a las limitaciones de la versión preliminar de SQL Server Data Tools de estilo SDK, se requieren varias instalaciones de Visual Studio para completar este tutorial. La primera instalación es necesaria para crear el proyecto de biblioteca de clases; la segunda instalación es necesaria para crear el proyecto de base de datos SQL de estilo SDK.

Este tutorial está destinado a usuarios que ya están familiarizados con las características de SQL Server de SQL Server Data Tools. Debes estar familiarizado con los conceptos de Visual Studio, por ejemplo, cómo crear una biblioteca de clases, agregar paquetes NuGet o cómo utilizar el editor de código para agregar código a una clase.

Este tutorial está diseñado para los usuarios que ya están familiarizados con la extensión Proyectos de base de datos SQL en Visual Studio Code. Debes estar familiarizado con los conceptos de desarrollo, por ejemplo, cómo crear una biblioteca de clases, agregar paquetes o cómo utilizar el editor de código para editar código.

  • Un editor de texto, como el editor de archivos de Visual Studio Code.
  • Un proyecto de base de datos SQL que contenga objetos de SQL.
  • SDK de .NET 8

Este tutorial está destinado a usuarios que ya están familiarizados con los proyectos de SQL. Debes estar familiarizado con los conceptos de desarrollo, por ejemplo, cómo crear una biblioteca de clases, agregar paquetes o cómo utilizar el editor de código para editar código.

Paso 1: cree un nuevo proyecto de biblioteca de clases

En primer lugar, cree una biblioteca de clases. Para crear un proyecto de biblioteca de clases:

  1. Cree un proyecto de biblioteca de clases (.NET Framework) en C# llamado SampleRules.

  2. Cambie el nombre del archivo Class1.cs a AvoidWaitForDelayRule.cs.

  3. En el Explorador de soluciones, haz clic con el botón derecho en el nodo de proyecto y, a continuación, selecciona Agregar; después, selecciona Referencia.

  4. Selecciona System.ComponentModel.Composition en la pestaña Ensamblados/Frameworks.

  5. En el Explorador de soluciones, haz clic con el botón derecho en el nodo del proyecto y, después, selecciona Administrar paquetes NuGet. Busca e instala el paquete de NuGet Microsoft.SqlServer.DacFx. La versión seleccionada debe ser 162.x.x (por ejemplo, 162.2.111) con Visual Studio 2022.

A continuación, agrega las clases auxiliares que utilizará la regla.

En primer lugar, cree una biblioteca de clases. Para crear un proyecto de biblioteca de clases:

  1. Cree un proyecto de biblioteca de clases (.NET Framework) en C# llamado SampleRules.

  2. Cambie el nombre del archivo Class1.cs a AvoidWaitForDelayRule.cs.

  3. En el Explorador de soluciones, haz clic con el botón derecho en el nodo de proyecto y, a continuación, selecciona Agregar; después, selecciona Referencia.

  4. Selecciona System.ComponentModel.Composition en la pestaña Ensamblajes/Frameworks.

  5. En el Explorador de soluciones, haz clic con el botón derecho en el nodo del proyecto y, después, selecciona Administrar paquetes NuGet. Busca e instala el paquete de NuGet Microsoft.SqlServer.DacFx. La versión seleccionada debe ser 162.x.x (por ejemplo, 162.2.111) con Visual Studio 2022.

A continuación, agrega las clases auxiliares que utilizará la regla.

  1. Inicie Visual Studio Code y abra la carpeta donde desea crear el proyecto.

  2. Abra una ventana Terminal en Visual Studio Code seleccionando el menú Visualizar y, a continuación, Terminal.

  3. En el Terminal, escriba los siguientes comandos para crear una nueva solución y un nuevo proyecto:

    dotnet new sln
    dotnet new classlib -n SampleRules -o SampleRules
    dotnet sln add SampleRules/SampleRules.csproj
    
  4. Cambie al directorio SampleRules:

    cd SampleRules
    
  5. Añada el paquete NuGet necesario:

    dotnet add package Microsoft.SqlServer.DacFx
    

A continuación, agrega las clases auxiliares que utilizará la regla.

  1. Abra una ventana de consola o terminal y diríjase a la carpeta donde desea crear el proyecto.

  2. En el Terminal, escriba los siguientes comandos para crear una nueva solución y un nuevo proyecto:

    dotnet new sln
    dotnet new classlib -n SampleRules -o SampleRules
    dotnet sln add SampleRules/SampleRules.csproj
    
  3. Cambie al directorio SampleRules:

    cd SampleRules
    
  4. Añada el paquete NuGet necesario:

    dotnet add package Microsoft.SqlServer.DacFx
    

Paso 2: Creación de clases auxiliares de reglas personalizadas

Antes de crear la clase para la regla en sí, deberás agregar al proyecto una clase de visitante y una clase de atributos. Estas clases pueden ser útiles para crear reglas personalizadas adicionales.

Paso 2.1: Definición de la clase WaitForDelayVisitor

La primera clase que debes definir es la clase WaitForDelayVisitor, que se deriva de TSqlConcreteFragmentVisitor. Esta clase proporciona acceso a las instrucciones WAITFOR DELAY en el modelo. Las clases de visitante hacen uso de la API ScriptDom, proporcionada por SQL Server. En esta API, el código Transact-SQL se representa como un árbol de sintaxis abstracta (AST) y las clases de visitante pueden ser útiles para buscar objetos de sintaxis específica, como instrucciones WAITFOR DELAY. Puede ser difícil encontrar estas instrucciones utilizando el modelo de objetos, ya que no están asociadas a una propiedad de objeto o relación específicas, pero puedes encontrarlas mediante el patrón de visitante y la API ScriptDom.

  1. En el Explorador de soluciones, seleccione el proyecto SampleRules.

  2. En el menú Proyecto, seleccione Agregar clase. Aparecerá el cuadro de diálogo Agregar nuevo elemento . En el cuadro de texto Nombre, escribe WaitForDelayVisitor.cs y, después, selecciona Agregar. El archivoWaitForDelayVisitor.cs se agrega al proyecto en el Explorador de soluciones.

  1. En el Explorador de soluciones, seleccione el proyecto SampleRules.

  2. En el menú Proyecto, seleccione Agregar clase. Aparecerá el cuadro de diálogo Agregar nuevo elemento . En el cuadro de texto Nombre, escribe WaitForDelayVisitor.cs y, después, selecciona Agregar. El archivoWaitForDelayVisitor.cs se agrega al proyecto en el Explorador de soluciones.

  1. En Visual Studio Code, abra la vista Explorer (Explorador).

  2. En la carpeta WaitForDelayVisitor.cs, cree un nuevo archivo denominado SampleRules.

  1. Vaya al directorio SampleRules.
  2. Cree un nuevo archivo llamado WaitForDelayVisitor.cs.
  1. Abre el archivoWaitForDelayVisitor.cs y actualiza el contenido para que coincida con el código siguiente:

    using System.Collections.Generic;
    using Microsoft.SqlServer.TransactSql.ScriptDom;
    namespace SampleRules {
        class WaitForDelayVisitor {}
    }
    
  2. En la declaración de clase, cambia el modificador de acceso a internal y deriva la clase de TSqlConcreteFragmentVisitor:

    internal class WaitForDelayVisitor : TSqlConcreteFragmentVisitor {}
    
  3. Agregue el código siguiente para definir la variable de miembro de lista:

    public IList<WaitForStatement> WaitForDelayStatements { get; private set; }
    
  4. Defina el constructor de clase agregando el código siguiente:

    public WaitForDelayVisitor() {
       WaitForDelayStatements = new List<WaitForStatement>();
    }
    
  5. Anula el método ExplicitVisit al agregar el código siguiente:

    public override void ExplicitVisit(WaitForStatement node) {
       // We are only interested in WAITFOR DELAY occurrences
       if (node.WaitForOption == WaitForOption.Delay)
          WaitForDelayStatements.Add(node);
    }
    

    Este método pasa por las instrucciones WAITFOR del modelo y agrega las que tienen la opción DELAY especificada a la lista de instrucciones WAITFOR DELAY. La clase clave a la que se hace referencia es WaitForStatement.

  6. En el menú Archivo, seleccione Guardar.

Paso 2.2: Agregar un archivo de recursos y tres cadenas de recursos

A continuación, agrega un archivo de recursos en el que se defina el nombre de regla, su descripción y la categoría en la que aparecerá la regla en la interfaz de configuración de reglas.

  1. En el Explorador de soluciones, seleccione el proyecto SampleRules. En el menú Proyecto, selecciona Agregar y, a continuación, Nuevo elemento. Aparecerá el cuadro de diálogo Agregar nuevo elemento .

  2. En la lista de Plantillas instaladas, selecciona General. En el panel de detalles, selecciona Archivo de recursos.

  3. En Nombre, escriba RuleResources.resx. Aparece el editor de recursos, sin recursos definidos.

  4. Defina cuatro cadenas de recursos, como sigue:

    Nombre Valor
    AvoidWaitForDelay_ProblemDescription WAITFOR DELAY statement was found in {0}.
    AvoidWaitForDelay_RuleName Avoid using WaitFor Delay statements in stored procedures, functions and triggers.
    CategorySamples SamplesCategory
    CannotCreateResourceManager Can't create ResourceManager for {0} from {1}.
  5. En el menú Archivo, selecciona Guardar RuleResources.resx.

  1. En el Explorador de soluciones, seleccione el proyecto SampleRules. En el menú Proyecto, selecciona Agregar y, a continuación, Nuevo elemento. Aparecerá el cuadro de diálogo Agregar nuevo elemento .

  2. En la lista de Plantillas instaladas, selecciona General. En el panel de detalles, selecciona Archivo de recursos.

  3. En Nombre, escriba RuleResources.resx. Aparece el editor de recursos, sin recursos definidos.

  4. Defina cuatro cadenas de recursos, como sigue:

    Nombre Valor
    AvoidWaitForDelay_ProblemDescription WAITFOR DELAY statement was found in {0}.
    AvoidWaitForDelay_RuleName Avoid using WaitFor Delay statements in stored procedures, functions and triggers.
    CategorySamples SamplesCategory
    CannotCreateResourceManager Can't create ResourceManager for {0} from {1}.
  5. En el menú Archivo, selecciona Guardar RuleResources.resx.

  1. En el directorio SampleRules, cree un nuevo archivo denominado RuleResources.resx.

  2. Abra el archivo RuleResources.resx y agregue el código siguiente:

    <?xml version="1.0" encoding="utf-8"?>
    <root>
      <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
        <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
        <xsd:element name="root" msdata:IsDataSet="true">
          <xsd:complexType>
            <xsd:choice maxOccurs="unbounded">
              <xsd:element name="metadata">
                <xsd:complexType>
                  <xsd:sequence>
                    <xsd:element name="value" type="xsd:string" minOccurs="0" />
                  </xsd:sequence>
                  <xsd:attribute name="name" use="required" type="xsd:string" />
                  <xsd:attribute name="type" type="xsd:string" />
                  <xsd:attribute name="mimetype" type="xsd:string" />
                  <xsd:attribute ref="xml:space" />
                </xsd:complexType>
              </xsd:element>
              <xsd:element name="assembly">
                <xsd:complexType>
                  <xsd:attribute name="alias" type="xsd:string" />
                  <xsd:attribute name="name" type="xsd:string" />
                </xsd:complexType>
              </xsd:element>
              <xsd:element name="data">
                <xsd:complexType>
                  <xsd:sequence>
                    <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
                    <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
                  </xsd:sequence>
                  <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
                  <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
                  <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
                  <xsd:attribute ref="xml:space" />
                </xsd:complexType>
              </xsd:element>
              <xsd:element name="resheader">
                <xsd:complexType>
                  <xsd:sequence>
                    <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
                  </xsd:sequence>
                  <xsd:attribute name="name" type="xsd:string" use="required" />
                </xsd:complexType>
              </xsd:element>
            </xsd:choice>
          </xsd:complexType>
        </xsd:element>
      </xsd:schema>
      <resheader name="resmimetype">
        <value>text/microsoft-resx</value>
      </resheader>
      <resheader name="version">
        <value>2.0</value>
      </resheader>
      <resheader name="reader">
        <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
      </resheader>
      <resheader name="writer">
        <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
      </resheader>
      <data name="AvoidWaitForDelay_ProblemDescription" xml:space="preserve">
        <value>WAITFOR DELAY statement was found in {0}</value>
      </data>
      <data name="AvoidWaitFormDelay_RuleName" xml:space="preserve">
        <value>Avoid using WaitFor Delay statements in stored procedures, functions and triggers.</value>
      </data>
      <data name="CategorySamples" xml:space="preserve">
        <value>SamplesCategory</value>
      </data>
      <data name="CannotCreateResourceManager" xml:space="preserve">
        <value>Can't create ResourceManager for {0} from {1}</value>
      </data>
    </root>
    
  3. Guarde el archivo RuleResources.resx.

  4. Abra el archivo SampleRules.csproj y agregue el código siguiente para actualizar e incluir el contenido del recurso en el proyecto:

    <ItemGroup>
      <Compile Update="RuleResources.Designer.cs">
        <DesignTime>True</DesignTime>
        <AutoGen>True</AutoGen>
        <DependentUpon>RuleResources.resx</DependentUpon>
      </Compile>
    </ItemGroup>
    <ItemGroup>
      <EmbeddedResource Include="RuleResources.resx">
        <Generator>PublicResXFileCodeGenerator</Generator>
        <LastGenOutput>RuleResources.Designer.cs</LastGenOutput>
      </EmbeddedResource>
    </ItemGroup>
    
  5. Guarde el archivo SampleRules.csproj.

  1. En el directorio SampleRules, cree un nuevo archivo denominado RuleResources.resx.

  2. Abra el archivo RuleResources.resx y agregue el código siguiente:

    <?xml version="1.0" encoding="utf-8"?>
    <root>
      <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
        <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
        <xsd:element name="root" msdata:IsDataSet="true">
          <xsd:complexType>
            <xsd:choice maxOccurs="unbounded">
              <xsd:element name="metadata">
                <xsd:complexType>
                  <xsd:sequence>
                    <xsd:element name="value" type="xsd:string" minOccurs="0" />
                  </xsd:sequence>
                  <xsd:attribute name="name" use="required" type="xsd:string" />
                  <xsd:attribute name="type" type="xsd:string" />
                  <xsd:attribute name="mimetype" type="xsd:string" />
                  <xsd:attribute ref="xml:space" />
                </xsd:complexType>
              </xsd:element>
              <xsd:element name="assembly">
                <xsd:complexType>
                  <xsd:attribute name="alias" type="xsd:string" />
                  <xsd:attribute name="name" type="xsd:string" />
                </xsd:complexType>
              </xsd:element>
              <xsd:element name="data">
                <xsd:complexType>
                  <xsd:sequence>
                    <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
                    <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
                  </xsd:sequence>
                  <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
                  <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
                  <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
                  <xsd:attribute ref="xml:space" />
                </xsd:complexType>
              </xsd:element>
              <xsd:element name="resheader">
                <xsd:complexType>
                  <xsd:sequence>
                    <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
                  </xsd:sequence>
                  <xsd:attribute name="name" type="xsd:string" use="required" />
                </xsd:complexType>
              </xsd:element>
            </xsd:choice>
          </xsd:complexType>
        </xsd:element>
      </xsd:schema>
      <resheader name="resmimetype">
        <value>text/microsoft-resx</value>
      </resheader>
      <resheader name="version">
        <value>2.0</value>
      </resheader>
      <resheader name="reader">
        <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
      </resheader>
      <resheader name="writer">
        <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
      </resheader>
      <data name="AvoidWaitForDelay_ProblemDescription" xml:space="preserve">
        <value>WAITFOR DELAY statement was found in {0}</value>
      </data>
      <data name="AvoidWaitFormDelay_RuleName" xml:space="preserve">
        <value>Avoid using WaitFor Delay statements in stored procedures, functions and triggers.</value>
      </data>
      <data name="CategorySamples" xml:space="preserve">
        <value>SamplesCategory</value>
      </data>
      <data name="CannotCreateResourceManager" xml:space="preserve">
        <value>Can't create ResourceManager for {0} from {1}</value>
      </data>
    </root>
    
  3. Guarde el archivo RuleResources.resx.

  4. Abra el archivo SampleRules.csproj y agregue el código siguiente para actualizar e incluir el contenido del recurso en el proyecto:

    <ItemGroup>
      <Compile Update="RuleResources.Designer.cs">
        <DesignTime>True</DesignTime>
        <AutoGen>True</AutoGen>
        <DependentUpon>RuleResources.resx</DependentUpon>
      </Compile>
    </ItemGroup>
    <ItemGroup>
      <EmbeddedResource Include="RuleResources.resx">
        <Generator>PublicResXFileCodeGenerator</Generator>
        <LastGenOutput>RuleResources.Designer.cs</LastGenOutput>
      </EmbeddedResource>
    </ItemGroup>
    
  5. Guarde el archivo SampleRules.csproj.

Paso 2.3: Definir la clase LocalizedExportCodeAnalysisRuleAttribute

La segunda clase es LocalizedExportCodeAnalysisRuleAttribute.cs. Se trata de una extensión de la Microsoft.SqlServer.Dac.CodeAnalysis.ExportCodeAnalysisRuleAttribute integrada proporcionada por el marco de trabajo y admite la lectura de los DisplayName y Description usados por la regla desde un archivo de recursos. Esta clase es útil si desea que las reglas puedan utilizarse en varios idiomas.

  1. En el Explorador de soluciones, seleccione el proyecto SampleRules.

  2. En el menú Proyecto, seleccione Agregar clase. Aparecerá el cuadro de diálogo Agregar nuevo elemento . En el cuadro de texto Nombre, escribe LocalizedExportCodeAnalysisRuleAttribute.cs y, después, selecciona Agregar. El archivo se agrega al proyecto en el Explorador de soluciones.

  1. En el Explorador de soluciones, seleccione el proyecto SampleRules.

  2. En el menú Proyecto, seleccione Agregar clase. Aparecerá el cuadro de diálogo Agregar nuevo elemento . En el cuadro de texto Nombre, escribe LocalizedExportCodeAnalysisRuleAttribute.cs y, después, selecciona Agregar. El archivo se agrega al proyecto en el Explorador de soluciones.

  1. Vaya al directorio SampleRules en la vista Explorador de Visual Studio Code.
  2. Cree un nuevo archivo llamado LocalizedExportCodeAnalysisRuleAttribute.cs.
  1. Vaya al directorio SampleRules.
  2. Cree un nuevo archivo llamado LocalizedExportCodeAnalysisRuleAttribute.cs.
  1. Abre el archivo y actualiza el contenido para que coincida con el código siguiente:

    using Microsoft.SqlServer.Dac.CodeAnalysis;
    using System;
    using System.Globalization;
    using System.Reflection;
    using System.Resources;
    
    namespace SampleRules
    {
    
        internal class LocalizedExportCodeAnalysisRuleAttribute : ExportCodeAnalysisRuleAttribute
        {
            private readonly string _resourceBaseName;
            private readonly string _displayNameResourceId;
            private readonly string _descriptionResourceId;
    
            private ResourceManager _resourceManager;
            private string _displayName;
            private string _descriptionValue;
    
            /// <summary>
            /// Creates the attribute, with the specified rule ID, the fully qualified
            /// name of the resource file that will be used for looking up display name
            /// and description, and the Ids of those resources inside the resource file.
            /// </summary>
            public LocalizedExportCodeAnalysisRuleAttribute(
                string id,
                string resourceBaseName,
                string displayNameResourceId,
                string descriptionResourceId)
                : base(id, null)
            {
                _resourceBaseName = resourceBaseName;
                _displayNameResourceId = displayNameResourceId;
                _descriptionResourceId = descriptionResourceId;
            }
    
            /// <summary>
            /// Rules in a different assembly would need to overwrite this
            /// </summary>
            /// <returns></returns>
            protected virtual Assembly GetAssembly()
            {
                return GetType().Assembly;
            }
    
            private void EnsureResourceManagerInitialized()
            {
                var resourceAssembly = GetAssembly();
    
                try
                {
                    _resourceManager = new ResourceManager(_resourceBaseName, resourceAssembly);
                }
                catch (Exception ex)
                {
                    var msg = String.Format(CultureInfo.CurrentCulture, RuleResources.CannotCreateResourceManager, _resourceBaseName, resourceAssembly);
                    throw new RuleException(msg, ex);
                }
            }
    
            private string GetResourceString(string resourceId)
            {
                EnsureResourceManagerInitialized();
                return _resourceManager.GetString(resourceId, CultureInfo.CurrentUICulture);
            }
    
            /// <summary>
            /// Overrides the standard DisplayName and looks up its value inside a resources file
            /// </summary>
            public override string DisplayName
            {
                get
                {
                    if (_displayName == null)
                    {
                        _displayName = GetResourceString(_displayNameResourceId);
                    }
                    return _displayName;
                }
            }
    
            /// <summary>
            /// Overrides the standard Description and looks up its value inside a resources file
            /// </summary>
            public override string Description
            {
                get
                {
                    if (_descriptionValue == null)
                    {
                        _descriptionValue = GetResourceString(_descriptionResourceId);
                    }
                    return _descriptionValue;
                }
            }
        }
    }
    

Paso 2.4: Definición de la clase SampleConstants

A continuación, defina una clase que haga referencia a los recursos del archivo de recursos que Visual Studio utiliza para mostrar información acerca de la regla en la interfaz de usuario.

  1. En el Explorador de soluciones, seleccione el proyecto SampleRules.

  2. En el menú Proyecto, selecciona Agregar y, a continuación, Clase. Aparecerá el cuadro de diálogo Agregar nuevo elemento . En el cuadro de texto Nombre, escribe SampleRuleConstants.cs y selecciona el botón Agregar. El archivoSampleRuleConstants.cs se agrega al proyecto en el Explorador de soluciones.

  1. En el Explorador de soluciones, seleccione el proyecto SampleRules.

  2. En el menú Proyecto, selecciona Agregar y, a continuación, Clase. Aparecerá el cuadro de diálogo Agregar nuevo elemento . En el cuadro de texto Nombre, escribe SampleRuleConstants.cs y selecciona el botón Agregar. El archivoSampleRuleConstants.cs se agrega al proyecto en el Explorador de soluciones.

  1. Vaya al directorio SampleRules en la vista Explorador de Visual Studio Code.
  2. Cree un nuevo archivo llamado SampleRuleConstants.cs.
  1. Vaya al directorio SampleRules.
  2. Cree un nuevo archivo llamado SampleRuleConstants.cs.
  1. Abre el archivo SampleRuleConstants.cs y agrega lo siguiente al archivo usando instrucciones:

    namespace SampleRules
    {
        internal static class RuleConstants
        {
            /// <summary>
            /// The name of the resources file to use when looking up rule resources
            /// </summary>
            public const string ResourceBaseName = "SampleRules.RuleResources";
    
            /// <summary>
            /// Lookup name inside the resources file for the select asterisk rule name
            /// </summary>
            public const string AvoidWaitForDelay_RuleName = "AvoidWaitForDelay_RuleName";
            /// <summary>
            /// Lookup ID inside the resources file for the select asterisk description
            /// </summary>
            public const string AvoidWaitForDelay_ProblemDescription = "AvoidWaitForDelay_ProblemDescription";
    
            /// <summary>
            /// The design category (should not be localized)
            /// </summary>
            public const string CategoryDesign = "Design";
    
            /// <summary>
            /// The performance category (should not be localized)
            /// </summary>
            public const string CategoryPerformance = "Design";
        }
    }
    
  2. En el menú Archivo, seleccione Guardar.

Paso 3: cree una clase de regla personalizada

Después de agregar las clases del asistente que la regla de análisis de código personalizada utilizará, deberás crear una clase de reglas personalizada y asignarle el nombre AvoidWaitForDelayRule. La regla personalizada AvoidWaitForDelayRule se utilizará para ayudar a los desarrolladores de base de datos a evitar las instrucciones WAITFOR DELAY en los procedimientos, desencadenadores y funciones almacenados.

Paso 3.1: Creación de la clase AvoidWaitForDelayRule

  1. En el Explorador de soluciones, seleccione el proyecto SampleRules.

  2. En el menú Proyecto, selecciona Agregar y, a continuación, Clase. Aparecerá el cuadro de diálogo Agregar nuevo elemento . En el cuadro Nombre, escribe AvoidWaitForDelayRule.cs y, después, selecciona Agregar. El archivoAvoidWaitForDelayRule.cs se agrega al proyecto en el Explorador de soluciones.

  1. En el Explorador de soluciones, seleccione el proyecto SampleRules.

  2. En el menú Proyecto, selecciona Agregar y, a continuación, Clase. Aparecerá el cuadro de diálogo Agregar nuevo elemento . En el cuadro Nombre, escribe AvoidWaitForDelayRule.cs y, después, selecciona Agregar. El archivoAvoidWaitForDelayRule.cs se agrega al proyecto en el Explorador de soluciones.

  1. Vaya al directorio SampleRules en la vista Explorador de Visual Studio Code.
  2. Cree un nuevo archivo llamado AvoidWaitForDelayRule.cs.
  1. Vaya al directorio SampleRules.
  2. Cree un nuevo archivo llamado AvoidWaitForDelayRule.cs.
  1. Abre el archivo AvoidWaitForDelayRule.cs y agrega lo siguiente al archivo usando instrucciones:

    using Microsoft.SqlServer.Dac.CodeAnalysis;
    using Microsoft.SqlServer.Dac.Model;
    using Microsoft.SqlServer.TransactSql.ScriptDom;
    using System;
    using System.Collections.Generic;
    using System.Globalization;
    namespace SampleRules {
        class AvoidWaitForDelayRule {}
    }
    
  2. En la declaración de clase AvoidWaitForDelayRule, cambia el modificador de acceso a public:

    /// <summary>
    /// This is a rule that returns a warning message
    /// whenever there is a WAITFOR DELAY statement appears inside a subroutine body.
    /// This rule only applies to stored procedures, functions and triggers.
    /// </summary>
    public sealed class AvoidWaitForDelayRule
    
  3. Deriva la clase AvoidWaitForDelayRule de la clase base Microsoft.SqlServer.Dac.CodeAnalysis.SqlCodeAnalysisRule:

    public sealed class AvoidWaitForDelayRule : SqlCodeAnalysisRule
    
  4. Agregue el LocalizedExportCodeAnalysisRuleAttribute a la clase.

    LocalizedExportCodeAnalysisRuleAttribute permite al servicio de análisis de código detectar reglas de análisis de código personalizado. Solo las clases marcadas con un ExportCodeAnalysisRuleAttribute (o un atributo que hereda de este) pueden utilizarse en el análisis de código.

    LocalizedExportCodeAnalysisRuleAttribute proporciona algunos metadatos necesarios usados por el servicio. Esto incluye un identificador único para esta regla, un nombre que se mostrará en la interfaz de usuario de Visual Studio y una Description que la regla puede utilizar para identificar los problemas.

    [LocalizedExportCodeAnalysisRule(AvoidWaitForDelayRule.RuleId,
        RuleConstants.ResourceBaseName,
        RuleConstants.AvoidWaitForDelay_RuleName,
        RuleConstants.AvoidWaitForDelay_ProblemDescription
        Category = RuleConstants.CategoryPerformance,
        RuleScope = SqlRuleScope.Element)]
    public sealed class AvoidWaitForDelayRule : SqlCodeAnalysisRule
    {
       /// <summary>
       /// The Rule ID should resemble a fully-qualified class name. In the Visual Studio UI
       /// rules are grouped by "Namespace + Category", and each rule is shown using "Short ID: DisplayName".
       /// For this rule, that means the grouping will be "Public.Dac.Samples.Performance", with the rule
       /// shown as "SR1004: Avoid using WaitFor Delay statements in stored procedures, functions and triggers."
       /// </summary>
       public const string RuleId = "RuleSamples.SR1004";
    }
    

    La propiedad RuleScope debe ser Microsoft.SqlServer.Dac.CodeAnalysis.SqlRuleScope.Element, ya que esta regla analiza elementos específicos. La regla se llamará una vez para cada elemento coincidente del modelo. Si deseas analizar un modelo completo, Microsoft.SqlServer.Dac.CodeAnalysis.SqlRuleScope.Model se puede usar en su lugar.

  5. Agregue un constructor que configure el Microsoft.SqlServer.Dac.CodeAnalysis.SqlAnalysisRule.SupportedElementTypes. Esto es necesario para las reglas de ámbito de elemento. Define los tipos de elemento a los que se aplicará esta regla. En este caso, la regla se aplicará a los procedimientos, desencadenadores y funciones almacenados. La clase Microsoft.SqlServer.Dac.Model.ModelSchema enumera todos los tipos de elementos disponibles que se pueden analizar.

    public AvoidWaitForDelayRule()
    {
       // This rule supports Procedures, Functions and Triggers. Only those objects will be passed to the Analyze method
       SupportedElementTypes = new[]
       {
          // Note: can use the ModelSchema definitions, or access the TypeClass for any of these types
          ModelSchema.ExtendedProcedure,
          ModelSchema.Procedure,
          ModelSchema.TableValuedFunction,
          ModelSchema.ScalarFunction,
    
          ModelSchema.DatabaseDdlTrigger,
          ModelSchema.DmlTrigger,
          ModelSchema.ServerDdlTrigger
       };
    }
    
  6. Agrega una invalidación para el método Microsoft.SqlServer.Dac.CodeAnalysis.SqlAnalysisRule.Analyze(Microsoft.SqlServer.Dac.CodeAnalysis.SqlRuleExecutionContext), que usa Microsoft.SqlServer.Dac.CodeAnalysis.SqlRuleExecutionContext como parámetros de entrada. Este método devuelve una lista de posibles problemas.

    El método obtiene Microsoft.SqlServer.Dac.Model.TSqlModel, Microsoft.SqlServer.Dac.Model.TSqlObject y TSqlFragment del parámetro de contexto. 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 un Microsoft.SqlServer.Dac.CodeAnalysis.SqlRuleProblem.

    /// <summary>
    /// For element-scoped rules the Analyze method is executed once for every matching
    /// object in the model.
    /// </summary>
    /// <param name="ruleExecutionContext">The context object contains the TSqlObject being
    /// analyzed, a TSqlFragment
    /// that's the AST representation of the object, the current rule's descriptor, and a
    /// reference to the model being
    /// analyzed.
    /// </param>
    /// <returns>A list of problems should be returned. These will be displayed in the Visual
    /// Studio error list</returns>
    public override IList<SqlRuleProblem> Analyze(
        SqlRuleExecutionContext ruleExecutionContext)
    {
         IList<SqlRuleProblem> problems = new List<SqlRuleProblem>();
    
         TSqlObject modelElement = ruleExecutionContext.ModelElement;
    
         // this rule does not apply to inline table-valued function
         // we simply do not return any problem in that case.
         if (IsInlineTableValuedFunction(modelElement))
         {
             return problems;
         }
    
         string elementName = GetElementName(ruleExecutionContext, modelElement);
    
         // The rule execution context has all the objects we'll need, including the
         // fragment representing the object,
         // and a descriptor that lets us access rule metadata
         TSqlFragment fragment = ruleExecutionContext.ScriptFragment;
         RuleDescriptor ruleDescriptor = ruleExecutionContext.RuleDescriptor;
    
         // To process the fragment and identify WAITFOR DELAY statements we will use a
         // visitor
         WaitForDelayVisitor visitor = new WaitForDelayVisitor();
         fragment.Accept(visitor);
         IList<WaitForStatement> waitforDelayStatements = visitor.WaitForDelayStatements;
    
         // Create problems for each WAITFOR DELAY statement found
         // When creating a rule problem, always include the TSqlObject being analyzed. This
         // is used to determine
         // the name of the source this problem was found in and a best guess as to the
         // line/column the problem was found at.
         //
         // In addition if you have a specific TSqlFragment that is related to the problem
         //also include this
         // since the most accurate source position information (start line and column) will
         // be read from the fragment
         foreach (WaitForStatement waitForStatement in waitforDelayStatements)
         {
            SqlRuleProblem problem = new SqlRuleProblem(
                String.Format(CultureInfo.CurrentCulture,
                    ruleDescriptor.DisplayDescription, elementName),
                modelElement,
                waitForStatement);
            problems.Add(problem);
        }
        return problems;
    }
    
    private static string GetElementName(
        SqlRuleExecutionContext ruleExecutionContext,
        TSqlObject modelElement)
    {
        // Get the element name using the built in DisplayServices. This provides a number of
        // useful formatting options to
        // make a name user-readable
        var displayServices = ruleExecutionContext.SchemaModel.DisplayServices;
        string elementName = displayServices.GetElementName(
            modelElement, ElementNameStyle.EscapedFullyQualifiedName);
        return elementName;
    }
    
    private static bool IsInlineTableValuedFunction(TSqlObject modelElement)
    {
        return TableValuedFunction.TypeClass.Equals(modelElement.ObjectType)
                       && FunctionType.InlineTableValuedFunction ==
            modelElement.GetMetadata<FunctionType>(TableValuedFunction.FunctionType);
    }
    
  7. En el menú Archivo , seleccione Guardar.

Paso 4: compile la biblioteca de clases

  1. En el menú Proyecto, haz clic en Propiedades de SampleRules.
  2. Selecciona la pestaña Firma.
  3. Seleciona la casilla Firmar el ensamblado.
  4. En Elija un archivo de clave de nombre fuerte, selecciona <Nuevo>.
  5. En el cuadro de diálogo Crear clave de nombre seguro, en el Nombre del archivo clave, escribe MyRefKey.
  6. (opcional) Puede especificar una contraseña para el archivo de clave de nombre seguro.
  7. Seleccione Aceptar.
  8. En el menú Archivo, seleccione Guardar todo.
  9. En el menú Compilar, seleccione Compilar solución.
  1. En el menú Proyecto, haz clic en Propiedades de SampleRules.
  2. Selecciona la pestaña Firma.
  3. Seleciona la casilla Firmar el ensamblado.
  4. En Elija un archivo de clave de nombre seguro, selecciona <Nuevo>.
  5. En el cuadro de diálogo Crear clave de nombre seguro, en el Nombre del archivo clave, escribe MyRefKey.
  6. (opcional) Puede especificar una contraseña para el archivo de clave de nombre seguro.
  7. Seleccione Aceptar.
  8. En el menú Archivo, seleccione Guardar todo.
  9. En el menú Compilar, seleccione Compilar solución.
  1. Abra la ventana Terminal en Visual Studio Code seleccionando el menú Visualizar y, a continuación, Terminal.

  2. En el Terminal, escriba el siguiente comando para compilar el proyecto:

    dotnet build /p:Configuration=Release
    
  1. Vaya al directorio SampleRules.

  2. Para compilar el proyecto, ejecute el siguiente comando:

    dotnet build /p:Configuration=Release
    

Paso 5: instale y pruebe la nueva regla de Code Analysis

A continuación, debe instalar el ensamblado para que se cargue cuando compile un proyecto de base de datos SQL.

Para instalar una regla que se ejecutará al compilar un proyecto SQL original con Visual Studio, debe copiar el ensamblado y el archivo .pdb asociado a la carpeta Extensiones.

Paso 5.1: Instalación del ensamblado SampleRules

  1. Después, copia la información del ensamblado en el directorio Extensions. Cuando se inicie Visual Studio, este identificará todas las extensiones en el directorio <Visual Studio Install Dir>\Common7\IDE\Extensions\Microsoft\SQLDB\DAC\Extensions y sus subdirectorios y las dejará disponibles para su uso. Para Visual Studio 2022, <Visual Studio Install Dir> normalmente es C:\Program Files\Microsoft Visual Studio\2022\Enterprise. Reemplace Enterprise por Professional o Community en función de la edición de Visual Studio que haya instalado.
  2. Copie el archivo de ensamblado SampleRules.dll del directorio de salida al directorio <Visual Studio Install Dir>\Common7\IDE\Extensions\Microsoft\SQLDB\DAC\Extensions. De manera predeterminada, la ruta de acceso del archivo compilado .dll es YourSolutionPath\YourProjectPath\bin\Debug o YourSolutionPath\YourProjectPath\bin\Release.

Nota:

Es posible que tengas que crear el directorio Extensions.

La regla deberá entonces instalarse y aparecer al reiniciar Visual Studio. Después, iniciarás una nueva sesión de Visual Studio y crearás un proyecto de base de datos.

Paso 5.2: Inicio de una nueva sesión de Visual Studio y creación de un proyecto de base de datos

  1. Inicie una segunda sesión de Visual Studio.
  2. Seleccione Archivo>Nuevo>Proyecto.
  3. En el cuadro de diálogo Nuevo proyecto, busca y selecciona Proyecto de base de datos de SQL Server.
  4. En el cuadro Nombre, escribe SampleRulesDB y selecciona Aceptar.

Paso 5.3: Habilitar la regla de análisis de código AvoidWaitForRule

  1. En el Explorador de soluciones, seleccione el proyecto SampleRulesDB.
  2. En el menú Proyecto, seleccione Propiedades. Se muestra la página de propiedades de SampleRulesDB.
  3. Selecciona Análisis de código. Deberías ver una nueva categoría denominada RuleSamples.CategorySamples.
  4. Expanda RuleSamples.CategorySamples. Deberías ver SR1004: Avoid WAITFOR DELAY statement in stored procedures, triggers, and functions.
  5. Habilite esta regla seleccionando la casilla situada junto al nombre de la regla y la casilla Habilitar el análisis de código en la compilación. Para obtener más información sobre cómo habilitar el análisis de código, consulte la introducción al análisis de código.
  6. Cuando se utiliza la acción compilar en el proyecto, la regla se ejecutará y las instrucciones WAITFOR DELAY encontradas se notificarán como advertencias.

Para instalar una regla que se ejecutará al compilar un proyecto SQL original con Visual Studio, debe copiar el ensamblado y el archivo .pdb asociado a la carpeta Extensiones.

Paso 5.1: Instalación del ensamblado SampleRules

  1. Después, copia la información del ensamblado en el directorio Extensions. Cuando se inicie Visual Studio, este identificará todas las extensiones en el directorio <Visual Studio Install Dir>\Common7\IDE\Extensions\Microsoft\SQLDB\DAC\Extensions y sus subdirectorios y las dejará disponibles para su uso. Para Visual Studio 2022, <Visual Studio Install Dir> normalmente es C:\Program Files\Microsoft Visual Studio\2022\Enterprise. Reemplaza Enterprise por Professional o Community en función de la edición de Visual Studio instalada.
  2. Copie el archivo de ensamblado SampleRules.dll del directorio de salida al directorio <Visual Studio Install Dir>\Common7\IDE\Extensions\Microsoft\SQLDB\DAC\Extensions. De manera predeterminada, la ruta de acceso del archivo compilado .dll es YourSolutionPath\YourProjectPath\bin\Debug o YourSolutionPath\YourProjectPath\bin\Release.

Nota:

Es posible que tengas que crear el directorio Extensions.

La regla deberá entonces instalarse y aparecer al reiniciar Visual Studio. Después, iniciarás una nueva sesión de Visual Studio y crearás un proyecto de base de datos.

Paso 5.2: Inicio de una nueva sesión de Visual Studio y creación de un proyecto de base de datos

  1. Inicie una segunda sesión de Visual Studio.
  2. Seleccione Archivo>Nuevo>Proyecto.
  3. En el cuadro de diálogo Nuevo proyecto, busque y seleccione Proyecto de base de datos de SQL Server, estilo SDK (versión preliminar).
  4. En el cuadro Nombre, escribe SampleRulesDB y selecciona Aceptar.

Paso 5.3: Habilitar la regla de análisis de código AvoidWaitForRule

  1. En el Explorador de soluciones, seleccione el proyecto SampleRulesDB.
  2. Haga doble clic en el nodo de proyecto para abrir el archivo de proyecto. El archivo de proyecto SampleRulesDB se muestra en un editor de texto.
  3. Habilite el análisis de código en la compilación en el archivo de proyecto SQL estableciendo la propiedad RunSqlCodeAnalysis como true.
  4. Cuando se usa el proyecto acción de compilación, se ejecutará la regla y las instrucciones WAITFOR DELAY encontradas se notificarán como advertencias.

Paso 5.1: Colocar el ensamblado SampleRules en un origen de NuGet local

  1. Si no tiene un origen local para paquetes NuGet, agregue una carpeta en el equipo local para almacenar los paquetes NuGet para las pruebas locales. Para comprobar los orígenes actuales de NuGet, ejecute el siguiente comando:
dotnet nuget list source
  1. Si no aparece un origen local, puede agregar uno mediante el siguiente comando, sustituyendo <local folder path> por la ruta de acceso a la carpeta local, como C:\NuGetPackages o ~/NuGetPackages:
dotnet nuget add source <local folder path>
  1. Copie el archivo de ensamblado SampleRules.dll del directorio de salida en el directorio de origen local de NuGet. De manera predeterminada, la ruta de acceso del archivo compilado .dll es YourSolutionPath\YourProjectPath\bin\Debug o YourSolutionPath\YourProjectPath\bin\Release.

Paso 5.2: Uso de SampleRules en un proyecto de base de datos

  1. Cree un nuevo proyecto Microsoft.Build.Sql o abra uno existente.
  2. Agregue una referencia de paquete al paquete NuGet SampleRules en el archivo del proyecto. En el ejemplo siguiente se muestra cómo agregar una referencia al paquete NuGet SampleRules en el archivo .sqlproj:
<ItemGroup>
  <PackageReference Include="SampleRules" Version="1.0.0" />
</ItemGroup>

Paso 5.3: Habilitación del análisis de código en la compilación

  1. Habilite el análisis de código en la compilación en el archivo de proyecto SQL estableciendo la propiedad RunSqlCodeAnalysis como true. El paquete NuGet SampleRules se restaurará cuando el proyecto se compila e incluye de forma predeterminada.
  2. Cuando se usa el proyecto acción de compilación, se ejecutará la regla y las instrucciones WAITFOR DELAY encontradas se notificarán como advertencias.

Paso 5.1: Colocar el ensamblado SampleRules en un origen de NuGet local

  1. Si no tiene un origen local para paquetes NuGet, agregue una carpeta en el equipo local para almacenar los paquetes NuGet para las pruebas locales. Para comprobar los orígenes actuales de NuGet, ejecute el siguiente comando:
dotnet nuget list source
  1. Si no aparece un origen local, puede agregar uno mediante el siguiente comando, sustituyendo <local folder path> por la ruta de acceso a la carpeta local, como C:\NuGetPackages o ~/NuGetPackages:
dotnet nuget add source <local folder path>
  1. Copie el archivo de ensamblado SampleRules.dll del directorio de salida en el directorio de origen local de NuGet. De manera predeterminada, la ruta de acceso del archivo compilado .dll es YourSolutionPath\YourProjectPath\bin\Debug o YourSolutionPath\YourProjectPath\bin\Release.

Paso 5.2: Uso de SampleRules en un proyecto de base de datos

  1. Cree un nuevo proyecto Microsoft.Build.Sql o abra uno existente.
  2. Agregue una referencia de paquete al paquete NuGet SampleRules en el archivo del proyecto. En el ejemplo siguiente se muestra cómo agregar una referencia al paquete NuGet SampleRules en el archivo .sqlproj:
<ItemGroup>
  <PackageReference Include="SampleRules" Version="1.0.0" />
</ItemGroup>

Paso 5.3: Habilitación del análisis de código en la compilación

  1. Habilite el análisis de código en la compilación en el archivo de proyecto SQL estableciendo la propiedad RunSqlCodeAnalysis como true. El paquete NuGet SampleRules se restaurará cuando se compile el proyecto y se incluya de forma predeterminada.
  2. Cuando se usa el proyecto acción de compilación, se ejecutará la regla y las instrucciones WAITFOR DELAY encontradas se notificarán como advertencias.