Compartir vía


Creación de reglas de Code Analysis personalizadas

Se aplica a: SQL Server Azure SQL Database Azure SQL Managed Instance Base 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 del asistente 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\Marcos.

  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 Ensamblados\Marcos.

  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 del símbolo del sistema o del terminal y vaya a la carpeta donde quiere 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 versión integrada Microsoft.SqlServer.Dac.CodeAnalysis.ExportCodeAnalysisRuleAttribute proporcionada por el marco de trabajo y admite la lectura de DisplayName y Description usadas 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 archivo SampleRuleConstants.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 archivo SampleRuleConstants.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 archivo AvoidWaitForDelayRule.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 archivo AvoidWaitForDelayRule.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 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. Agrega un constructor que configure 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 la lista 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. 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 la lista 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

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.

Copia el archivo de ensamblado SampleRules.dll del directorio de salida en el 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 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 usa la acción compilar 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

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.

Copia el archivo de ensamblado SampleRules.dll del directorio de salida en el 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 la acción compilar el proyecto, la regla se ejecutará y las instrucciones WAITFOR DELAY encontradas se notificarán como advertencias.

Hay disponible una solución alternativa para instalar reglas personalizadas en proyectos de estilo SDK hasta que se admita la compatibilidad con las referencias de paquetes.

  1. Ejecute dotnet restore para restaurar las dependencias del proyecto de SQL, asegurándose de que la memoria caché de los paquetes NuGet locales contiene Microsoft.Build.Sql.
  2. Tenga en cuenta la versión de Microsoft.Build.Sql que se usa en el archivo de proyecto de SQL, como 0.1.19-preview.
  3. Copie el archivo de ensamblado SampleRules.dll del directorio de salida al directorio ~/.nuget/packages/microsoft.build.sql/0.1.19-preview/tools/netstandard2.1. La ruta de acceso exacta del directorio puede variar en función de la versión de Microsoft.Build.Sql usada en el archivo de proyecto SQL.
  4. Habilite el análisis de código en la compilación en el archivo de proyecto SQL estableciendo la propiedad RunSqlCodeAnalysis como true.
  5. Ejecute dotnet build para compilar el proyecto SQL y ejecutar la regla personalizada.

Hay disponible una solución alternativa para instalar reglas personalizadas en proyectos de estilo SDK hasta que se admita la compatibilidad con las referencias de paquetes.

  1. Ejecute dotnet restore para restaurar las dependencias del proyecto de SQL, asegurándose de que la memoria caché de los paquetes NuGet locales contiene Microsoft.Build.Sql.
  2. Tenga en cuenta la versión de Microsoft.Build.Sql que se usa en el archivo de proyecto de SQL, como 0.1.19-preview.
  3. Copie el archivo de ensamblado SampleRules.dll del directorio de salida al directorio ~/.nuget/packages/microsoft.build.sql/0.1.19-preview/tools/netstandard2.1. La ruta de acceso exacta del directorio puede variar en función de la versión de Microsoft.Build.Sql usada en el archivo de proyecto SQL.
  4. Habilite el análisis de código en la compilación en el archivo de proyecto SQL estableciendo la propiedad RunSqlCodeAnalysis como true.
  5. Ejecute dotnet build para compilar el proyecto SQL y ejecutar la regla personalizada.