次の方法で共有


カスタム コード分析ルールを作成

適用対象: SQL Server Azure SQL Database Azure SQL Managed Instance

このチュートリアルでは、SQL Server のコード分析ルールを作成する手順について説明します。 このチュートリアルで作成するルールは、ストアド プロシージャ、トリガー、および関数で WAITFOR DELAY ステートメントを回避する場合に使用します。

このチュートリアルでは、次の手順を使用して、Transact-SQL の静的コード分析のカスタム ルールを作成します。

  1. クラス ライブラリ プロジェクトを作成し、そのプロジェクトの署名を有効にして、必要な参照を追加します。
  2. 2 つのヘルパー C# クラスを作成します。
  3. C# のカスタム ルール クラスを作成します。
  4. クラス ライブラリ プロジェクトをビルドします。
  5. 新しいコード分析ルールをインストールしてテストします。

Visual Studio (SQL Server Data Tools) の手順を除き、このガイドでは SDK スタイルの SQL プロジェクトに焦点を当てています。

前提条件

このチュートリアルを実行するには、次のコンポーネントが必要です。

  • C# .NET Framework の開発をサポートする、SQL Server Data Tools を含む、インストールされている Visual Studio のバージョン。
  • SQL Server オブジェクトを含む SQL Server プロジェクト。
  • データベース プロジェクトを配置できる SQL Server のインスタンス。

このチュートリアルは、既に SQL Server Data Tools の SQL Server 機能を使い慣れているユーザーを対象としています。 クラス ライブラリを作成して NuGet パッケージを追加する方法、コード エディターを使用してクラスにコードを追加する方法など、Visual Studio の概念を理解している必要があります。

Note

SDK スタイルの SQL Server Data Tools のプレビュー制限により、このチュートリアルを完了するには複数の Visual Studio インストールが必要です。 クラス ライブラリ プロジェクトを作成するには最初のインストールが必要です。SDK スタイルの SQL データベース プロジェクトを作成するには 2 番目のインストールが必要です。

このチュートリアルは、既に SQL Server Data Tools の SQL Server 機能を使い慣れているユーザーを対象としています。 クラス ライブラリを作成して NuGet パッケージを追加する方法、コード エディターを使用してクラスにコードを追加する方法など、Visual Studio の概念を理解している必要があります。

  • インストールされている Visual Studio Code のバージョン。SQL Database プロジェクト拡張機能が含まれます。
  • SQL オブジェクトを含む SQL Database プロジェクト。
  • .NET 8 SDK
  • 推奨: VS Code の C# 開発キット拡張機能

このチュートリアルは、Visual Studio Code の SQL Database プロジェクト拡張機能に既に慣れているユーザーを対象としています。 クラス ライブラリを作成してパッケージを追加する方法、コード エディターを使用してコードを編集する方法など、開発の概念を理解している必要があります。

  • テキスト エディター (Visual Studio Code のファイル エディターなど)。
  • SQL オブジェクトを含む SQL Database プロジェクト。
  • .NET 8 SDK

このチュートリアルは、既に SQL プロジェクトを使い慣れているユーザーを対象としています。 クラス ライブラリを作成してパッケージを追加する方法、コード エディターを使用してコードを編集する方法など、開発の概念を理解している必要があります。

手順 1 - クラス ライブラリ プロジェクトを作成する

まずクラス ライブラリを作成します。 クラス ライブラリ プロジェクトを作成するには、次の操作を行います。

  1. SampleRules という名前の C# (.NET Framework) クラス ライブラリ プロジェクトを作成します。

  2. ファイルの名前を Class1.cs から AvoidWaitForDelayRule.cs に変更します。

  3. ソリューション エクスプローラーで、プロジェクト ノードを右クリックし、[追加][参照] の順に選択します。

  4. [アセンブリ\フレームワーク] タブで System.ComponentModel.Composition を選択します。

  5. ソリューション エクスプローラーで、プロジェクト ノードを右クリックし、[NuGet パッケージの管理] を選択します。 Microsoft.SqlServer.DacFx NuGet パッケージを見つけてインストールします。 選択したバージョンは、Visual Studio 2022 の場合 (たとえば 162.2.111) 162.x.x である必要があります。

次に、ルールに使用するサポート クラスを追加します。

まずクラス ライブラリを作成します。 クラス ライブラリ プロジェクトを作成するには、次の操作を行います。

  1. SampleRules という名前の C# (.NET Framework) クラス ライブラリ プロジェクトを作成します。

  2. ファイルの名前を Class1.cs から AvoidWaitForDelayRule.cs に変更します。

  3. ソリューション エクスプローラーで、プロジェクト ノードを右クリックし、[追加][参照] の順に選択します。

  4. [アセンブリ\フレームワーク] タブで System.ComponentModel.Composition を選択します。

  5. ソリューション エクスプローラーで、プロジェクト ノードを右クリックし、[NuGet パッケージの管理] を選択します。 Microsoft.SqlServer.DacFx NuGet パッケージを見つけてインストールします。 選択したバージョンは、Visual Studio 2022 の場合 (たとえば 162.2.111) 162.x.x である必要があります。

次に、ルールに使用するサポート クラスを追加します。

  1. Visual Studio Code を起動し、プロジェクトを作成するフォルダーを開きます。

  2. Visual Studio Code 内で、[表示] メニュー、次に [ターミナル] を選択してターミナル ウィンドウを開きます。

  3. [ターミナル] で、次のコマンドを入力して、新しいソリューションとプロジェクトを作成します。

    dotnet new sln
    dotnet new classlib -n SampleRules -o SampleRules
    dotnet sln add SampleRules/SampleRules.csproj
    
  4. SampleRules ディレクトリに変更します。

    cd SampleRules
    
  5. 必要な NuGet パッケージを追加します。

    dotnet add package Microsoft.SqlServer.DacFx
    

次に、ルールに使用するサポート クラスを追加します。

  1. コマンド プロンプトまたはターミナル ウィンドウを開き、プロジェクトを作成するフォルダーに移動します。

  2. [ターミナル] で、次のコマンドを入力して、新しいソリューションとプロジェクトを作成します。

    dotnet new sln
    dotnet new classlib -n SampleRules -o SampleRules
    dotnet sln add SampleRules/SampleRules.csproj
    
  3. SampleRules ディレクトリに変更します。

    cd SampleRules
    
  4. 必要な NuGet パッケージを追加します。

    dotnet add package Microsoft.SqlServer.DacFx
    

手順 2: カスタム ルール ヘルパー クラスを作成する

ルール用のクラスを作成する前に、ビジター クラスと属性クラスをプロジェクトに追加します。 これらのクラスは、追加のカスタム ルールを作成するときに便利なことがあります。

手順 2.1: WaitForDelayVisitor クラスを定義する

最初に定義する必要があるクラスは、WaitForDelayVisitor クラスです。このクラスは、TSqlConcreteFragmentVisitor から派生します。 このクラスを使用すると、モデル内の WAITFOR DELAY ステートメントにアクセスできます。 ビジター クラスは、SQL Server で提供される ScriptDom API を利用します。 この API の Transact-SQL コードは、抽象構文ツリー (AST) として表されます。ビジター クラスは、WAITFOR DELAY ステートメントなど、特定の構文オブジェクトを探すときに便利です。 これらの構文オブジェクトは、特定のオブジェクト プロパティや関係に関連付けられていないので、オブジェクト モデルを使用して見つけるのは難しいことがありますが、ビジター パターンと ScriptDom API を使用して見つけることができます。

  1. ソリューション エクスプローラーSampleRules プロジェクトを選択します。

  2. [プロジェクト] メニューで、[クラスの追加] を選択します。 [新しい項目の追加] ダイアログ ボックスが表示されます。 [名前] テキスト ボックスに「WaitForDelayVisitor.cs」と入力し、[追加] を選択します。 WaitForDelayVisitor.cs ファイルは、[ソリューション エクスプローラー] のプロジェクトに追加されます。

  1. ソリューション エクスプローラーSampleRules プロジェクトを選択します。

  2. [プロジェクト] メニューで、[クラスの追加] を選択します。 [新しい項目の追加] ダイアログ ボックスが表示されます。 [名前] テキスト ボックスに「WaitForDelayVisitor.cs」と入力し、[追加] を選択します。 WaitForDelayVisitor.cs ファイルは、[ソリューション エクスプローラー] のプロジェクトに追加されます。

  1. Visual Studio Code で[エクスプローラー] ビューを開きます。

  2. WaitForDelayVisitor.cs フォルダーに、SampleRules という名前の新しいファイルを作成します。

  1. SampleRules ディレクトリに移動します。
  2. WaitForDelayVisitor.cs という名前で新しいファイルを作成します。
  1. WaitForDelayVisitor.cs ファイルを開き、次のコードに合わせて内容を更新します。

    using System.Collections.Generic;
    using Microsoft.SqlServer.TransactSql.ScriptDom;
    namespace SampleRules {
        class WaitForDelayVistor {}
    }
    
  2. クラス宣言で、アクセス修飾子を internal に変更し、TSqlConcreteFragmentVisitor からクラスを派生させます。

    internal class WaitForDelayVisitor : TSqlConcreteFragmentVisitor {}
    
  3. 次のコードを追加して、List メンバー変数を定義します。

    public IList<WaitForStatement> WaitForDelayStatements { get; private set; }
    
  4. 次のコードを追加して、クラス コンストラクターを定義します。

    public WaitForDelayVisitor() {
       WaitForDelayStatements = new List<WaitForStatement>();
    }
    
  5. 次のコードを追加して、ExplicitVisit メソッドをオーバーライドします。

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

    このメソッドは、モデル内の WAITFOR ステートメントにアクセスし、DELAY オプションを指定したステートメントを WAITFOR DELAY ステートメントの一覧に追加します。 参照するキー クラスは、WaitForStatement です。

  6. [ファイル] メニューの [保存] をクリックします。

手順 2.2: LocalizedExportCodeAnalysisRuleAttribute クラスを定義する

2 つ目のクラスは、LocalizedExportCodeAnalysisRuleAttribute.cs です。 これはフレームワークによって提供される組み込みの Microsoft.SqlServer.Dac.CodeAnalysis.ExportCodeAnalysisRuleAttribute の拡張機能であり、リソース ファイルからのルールによって使用される DisplayNameDescription の読み取りをサポートします。 このクラスは、複数の言語でルールを使用する予定がある場合に便利です。

  1. ソリューション エクスプローラーSampleRules プロジェクトを選択します。

  2. [プロジェクト] メニューで、[クラスの追加] を選択します。 [新しい項目の追加] ダイアログ ボックスが表示されます。 [名前] テキスト ボックスに「LocalizedExportCodeAnalysisRuleAttribute.cs」と入力し、[追加] を選択します。 ファイルは、ソリューション エクスプローラーのプロジェクトに追加されます。

  1. ソリューション エクスプローラーSampleRules プロジェクトを選択します。

  2. [プロジェクト] メニューで、[クラスの追加] を選択します。 [新しい項目の追加] ダイアログ ボックスが表示されます。 [名前] テキスト ボックスに「LocalizedExportCodeAnalysisRuleAttribute.cs」と入力し、[追加] を選択します。 ファイルは、ソリューション エクスプローラーのプロジェクトに追加されます。

  1. Visual Studio Code の [エクスプローラー] ビューで SampleRules ディレクトリに移動します。
  2. LocalizedExportCodeAnalysisRuleAttribute.cs という名前で新しいファイルを作成します。
  1. SampleRules ディレクトリに移動します。
  2. LocalizedExportCodeAnalysisRuleAttribute.cs という名前で新しいファイルを作成します。
  1. ファイルを開き、次のコードに合わせて内容を更新します。

    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;
                }
            }
        }
    }
    

手順 2.3: リソース ファイルと 3 つのリソース文字列を追加する

次に、ルール名、ルールの説明、およびカテゴリを定義したリソース ファイルを追加します。このルールは、ルール構成インターフェイスに表示されます。

  1. ソリューション エクスプローラーSampleRules プロジェクトを選択します。 [プロジェクト] メニューで、[追加][新しい項目] の順に選択します。 [新しい項目の追加] ダイアログ ボックスが表示されます。

  2. [インストールされたテンプレート] の一覧で、[全般] を選択します。 詳細ウィンドウの [リソース ファイル] を選択します。

  3. [名前]に「 RuleResources.resx」と入力します。 リソースが定義されていないリソース エディターが表示されます。

  4. 次のように、4 つのリソース文字列を定義します。

    名前
    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. [ファイル] メニューで、[RuleResources.resx の保存] を選択します。

  1. ソリューション エクスプローラーSampleRules プロジェクトを選択します。 [プロジェクト] メニューで、[追加][新しい項目] の順に選択します。 [新しい項目の追加] ダイアログ ボックスが表示されます。

  2. [インストールされたテンプレート] の一覧で、[全般] を選択します。 詳細ウィンドウの [リソース ファイル] を選択します。

  3. [名前]に「 RuleResources.resx」と入力します。 リソースが定義されていないリソース エディターが表示されます。

  4. 次のように、4 つのリソース文字列を定義します。

    名前
    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. [ファイル] メニューで、[RuleResources.resx の保存] を選択します。

  1. SampleRules ディレクトリに RuleResources.resx という名前の新しいファイルを作成します。

  2. RuleResources.resx ファイルを開き、次のコードを追加します。

    <?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. RuleResources.resx ファイルを保存します。

  4. SampleRules.csproj ファイルを開き、次のコードを追加して更新し、リソースの内容をプロジェクトに含めます。

    <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. SampleRules.csproj ファイルを保存します。

  1. SampleRules ディレクトリに RuleResources.resx という名前の新しいファイルを作成します。

  2. RuleResources.resx ファイルを開き、次のコードを追加します。

    <?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. RuleResources.resx ファイルを保存します。

  4. SampleRules.csproj ファイルを開き、次のコードを追加して更新し、リソースの内容をプロジェクトに含めます。

    <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. SampleRules.csproj ファイルを保存します。

手順 2.4: SampleConstants クラスを定義する

次に、ユーザー インターフェイスにルールに関する情報を表示するときに Visual Studio で使用されるリソース ファイル内のリソースを参照するクラスを定義します。

  1. ソリューション エクスプローラーSampleRules プロジェクトを選択します。

  2. [プロジェクト] メニューで、[追加][クラス] の順に選択します。 [新しい項目の追加] ダイアログ ボックスが表示されます。 [名前] テキスト ボックスに「SampleRuleConstants.cs」と入力し、[追加] ボタンを選択します。 SampleRuleConstants.cs ファイルは、[ソリューション エクスプローラー] のプロジェクトに追加されます。

  1. ソリューション エクスプローラーSampleRules プロジェクトを選択します。

  2. [プロジェクト] メニューで、[追加][クラス] の順に選択します。 [新しい項目の追加] ダイアログ ボックスが表示されます。 [名前] テキスト ボックスに「SampleRuleConstants.cs」と入力し、[追加] ボタンを選択します。 SampleRuleConstants.cs ファイルは、[ソリューション エクスプローラー] のプロジェクトに追加されます。

  1. Visual Studio Code の [エクスプローラー] ビューで SampleRules ディレクトリに移動します。
  2. SampleRuleConstants.cs という名前で新しいファイルを作成します。
  1. SampleRules ディレクトリに移動します。
  2. SampleRuleConstants.cs という名前で新しいファイルを作成します。
  1. SampleRuleConstants.cs ファイルを開き、次の using ステートメントをファイルに追加します。

    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 = "Public.Dac.Samples.Rules.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. [ファイル] メニューの [保存] をクリックします。

手順 3: カスタム ルール クラスを作成する

カスタム コード分析ルールで使用するヘルパー クラスを追加したら、カスタム ルール クラスを作成し、AvoidWaitForDelayRule という名前を付けます。 データベースの開発時に、AvoidWaitForDelayRule カスタム ルールを使用すると、ストアド プロシージャ、トリガー、および関数で WAITFOR DELAY ステートメントを回避できます。

手順 3.1: AvoidWaitForDelayRule クラスを作成する

  1. ソリューション エクスプローラーSampleRules プロジェクトを選択します。

  2. [プロジェクト] メニューで、[追加][クラス] の順に選択します。 [新しい項目の追加] ダイアログ ボックスが表示されます。 [名前] テキスト ボックスに「AvoidWaitForDelayRule.cs」と入力し、[追加] を選択します。 AvoidWaitForDelayRule.cs ファイルは、[ソリューション エクスプローラー] のプロジェクトに追加されます。

  1. ソリューション エクスプローラーSampleRules プロジェクトを選択します。

  2. [プロジェクト] メニューで、[追加][クラス] の順に選択します。 [新しい項目の追加] ダイアログ ボックスが表示されます。 [名前] テキスト ボックスに「AvoidWaitForDelayRule.cs」と入力し、[追加] を選択します。 AvoidWaitForDelayRule.cs ファイルは、[ソリューション エクスプローラー] のプロジェクトに追加されます。

  1. Visual Studio Code の [エクスプローラー] ビューで SampleRules ディレクトリに移動します。
  2. AvoidWaitForDelayRule.cs という名前で新しいファイルを作成します。
  1. SampleRules ディレクトリに移動します。
  2. AvoidWaitForDelayRule.cs という名前で新しいファイルを作成します。
  1. AvoidWaitForDelayRule.cs ファイルを開き、次の using ステートメントをファイルに追加します。

    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. AvoidWaitForDelayRule クラス宣言で、アクセス修飾子を 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. AvoidWaitForDelayRule クラスは、Microsoft.SqlServer.Dac.CodeAnalysis.SqlCodeAnalysisRule 基本クラスから派生します。

    public sealed class AvoidWaitForDelayRule : SqlCodeAnalysisRule
    
  4. クラスに LocalizedExportCodeAnalysisRuleAttribute を追加します。

    LocalizedExportCodeAnalysisRuleAttribute を使用すると、コード分析サービスでカスタム コード分析ルールを検出できます。 コード分析では、ExportCodeAnalysisRuleAttribute (またはこの属性から継承している属性) でマークしたクラスのみを使用できます。

    LocalizedExportCodeAnalysisRuleAttribute は、サービスで使用される必要なメタデータを提供します。 たとえば、このルールの一意の ID、Visual Studio ユーザー インターフェイスに表示される表示名、問題を特定するときにルールに使用できる Description などです。

    [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";
    }
    

    RuleScope プロパティは、このルールが特定の要素を分析するため、Microsoft.SqlServer.Dac.CodeAnalysis.SqlRuleScope.Element である必要があります。 このルールは、モデル内の一致する要素ごとに 1 回呼び出されます。 モデル全体を分析する場合は、代わりに Microsoft.SqlServer.Dac.CodeAnalysis.SqlRuleScope.Model を使用できます。

  5. Microsoft.SqlServer.Dac.CodeAnalysis.SqlAnalysisRule.SupportedElementTypes を設定するコンストラクターを追加します。 要素がスコープのルールの場合は必須です。 このルールが適用される要素の種類を定義します。 この場合、ルールはストアド プロシージャ、トリガー、および関数に適用されます。 Microsoft.SqlServer.Dac.Model.ModelSchema クラスには、分析可能なすべての要素の種類が一覧表示されます。

    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. 入力パラメーターとして Microsoft.SqlServer.Dac.CodeAnalysis.SqlRuleExecutionContext を使用する Microsoft.SqlServer.Dac.CodeAnalysis.SqlAnalysisRule.Analyze (Microsoft.SqlServer.Dac.CodeAnalysis.SqlRuleExecutionContext) メソッドのオーバーライドを追加します。 このメソッドは、可能性のある問題の一覧を返します。

    メソッドは、コンテキスト パラメーターから 、Microsoft.SqlServer.Dac.Model.TSqlModelMicrosoft.SqlServer.Dac.Model.TSqlObject、および TSqlFragment を取得します。 次に、モデル内のすべての WaitForDelayVisitor ステートメントの一覧を取得するために、WAITFOR DELAY クラスが使用されます。

    そのリスト内の WaitForStatement ごとに、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. [ファイル] メニューの [保存]をクリックします。

手順 4: クラス ライブラリをビルドする

  1. [プロジェクト] メニューの [SampleRules のプロパティ] を選択します。
  2. [署名] タブを選択します
  3. [アセンブリの署名] を選択します。
  4. [厳密な名前のキー ファイルを選択してください]<[新規作成]> を選択します。
  5. [厳密な名前キーの作成] ダイアログ ボックスで、[キー ファイル] に「MyRefKey」と入力します。
  6. (省略可能) 厳密な名前のキー ファイルにパスワードを指定できます。
  7. [OK] を選択します。
  8. [ファイル] メニューの [すべてを保存] をクリックします。
  9. [ビルド] メニューの [ソリューションのビルド] を選択します。
  1. [プロジェクト] メニューの [SampleRules のプロパティ] を選択します。
  2. [署名] タブを選択します
  3. [アセンブリの署名] を選択します。
  4. [厳密な名前のキー ファイルを選択してください]<[新規作成]> を選択します。
  5. [厳密な名前キーの作成] ダイアログ ボックスで、[キー ファイル] に「MyRefKey」と入力します。
  6. (省略可能) 厳密な名前のキー ファイルにパスワードを指定できます。
  7. [OK] を選択します。
  8. [ファイル] メニューの [すべてを保存] をクリックします。
  9. [ビルド] メニューの [ソリューションのビルド] を選択します。
  1. Visual Studio Code 内で、[表示] メニュー、次に [ターミナル] を選択してターミナル ウィンドウを開きます。

  2. [ターミナル] で次のコマンドを入力して、プロジェクトをビルドします。

    dotnet build /p:Configuration=Release
    
  1. SampleRules ディレクトリに移動します。

  2. 次のコマンドを実行してプロジェクトをビルドします。

    dotnet build /p:Configuration=Release
    

手順 5: 新しいコード分析ルールをインストールしてテストする

次に、アセンブリをインストールし、SQL Database プロジェクトをビルドするときに読み込まれるようにします。

Visual Studio で元の SQL プロジェクトをビルドするときに実行されるルールをインストールするには、アセンブリと関連付けられている .pdb ファイルを拡張機能フォルダーにコピーする必要があります。

手順 5.1: SampleRules アセンブリをインストールする

次に、アセンブリ情報を Extensions ディレクトリにコピーします。 Visual Studio が起動すると、<Visual Studio Install Dir>\Common7\IDE\Extensions\Microsoft\SQLDB\DAC\Extensions ディレクトリとサブディレクトリの機能拡張が特定され、使用できるようになります。

Visual Studio 2022 の場合、<Visual Studio Install Dir> は通常 C:\Program Files\Microsoft Visual Studio\2022\Enterprise です。 インストールされている Visual Studio エディションに応じて、EnterpriseProfessional または Community と置き換えます。

SampleRules.dll アセンブリ ファイルを出力ディレクトリから <Visual Studio Install Dir>\Common7\IDE\Extensions\Microsoft\SQLDB\DAC\Extensions ディレクトリにコピーします。 既定では、コンパイル済みの .dll ファイルのパスは YourSolutionPath\YourProjectPath\bin\Debug または YourSolutionPath\YourProjectPath\bin\Release です。

Note

Extensions ディレクトリを作成する必要がある場合があります。

以上でルールのインストールは完了です。Visual Studio を再起動すると、ルールが表示されます。 次に、Visual Studio の新しいセッションを開始して、データベース プロジェクトを作成します。

手順 5.2: 新しい Visual Studio セッションを開始して、データベース プロジェクトを作成する

  1. Visual Studio の 2 つ目のセッションを開始します。
  2. [File]>[New]>[Project] の順に選択します。
  3. [新しいプロジェクト] ダイアログ ボックスで、[SQL Server データベース プロジェクト] を見つけて選択します。
  4. [名前] テキスト ボックスに「SampleRulesDB」と入力し、[OK] を選択します。

手順 5.3: AvoidWaitForRule Code Analysis ルールを有効にする

  1. ソリューション エクスプローラーSampleRulesDB プロジェクトを選択します。
  2. [プロジェクト] メニューの [プロパティ] を選択します。 SampleRulesDB プロパティ ページが表示されます。
  3. [コード分析] を選択します。 RuleSamples.CategorySamples という名前の新しいカテゴリが表示されます。
  4. RuleSamples.CategorySamples を展開します。 SR1004: Avoid WAITFOR DELAY statement in stored procedures, triggers, and functions という結果が表示されます。
  5. このルールを有効にするには、ルール名の横にあるチェックボックスと、[ビルド時にコード分析を有効にする] のチェックボックスをオンにします。 コード分析を有効にする方法の詳細については、「コード分析の概要」を確認してください。
  6. プロジェクト [ビルド] アクションが使用されると、ルールが実行され、検出されたすべての WAITFOR DELAY ステートメントが警告として報告されます。

Visual Studio で元の SQL プロジェクトをビルドするときに実行されるルールをインストールするには、アセンブリと関連付けられている .pdb ファイルを拡張機能フォルダーにコピーする必要があります。

手順 5.1: SampleRules アセンブリをインストールする

次に、アセンブリ情報を Extensions ディレクトリにコピーします。 Visual Studio が起動すると、<Visual Studio Install Dir>\Common7\IDE\Extensions\Microsoft\SQLDB\DAC\Extensions ディレクトリとサブディレクトリの機能拡張が特定され、使用できるようになります。

Visual Studio 2022 の場合、<Visual Studio Install Dir> は通常 C:\Program Files\Microsoft Visual Studio\2022\Enterprise です。 インストールされている Visual Studio エディションに応じて、EnterpriseProfessional または Community と置き換えます。

SampleRules.dll アセンブリ ファイルを出力ディレクトリから <Visual Studio Install Dir>\Common7\IDE\Extensions\Microsoft\SQLDB\DAC\Extensions ディレクトリにコピーします。 既定では、コンパイル済みの .dll ファイルのパスは YourSolutionPath\YourProjectPath\bin\Debug または YourSolutionPath\YourProjectPath\bin\Release です。

Note

Extensions ディレクトリを作成する必要がある場合があります。

以上でルールのインストールは完了です。Visual Studio を再起動すると、ルールが表示されます。 次に、Visual Studio の新しいセッションを開始して、データベース プロジェクトを作成します。

手順 5.2: 新しい Visual Studio セッションを開始して、データベース プロジェクトを作成する

  1. Visual Studio の 2 つ目のセッションを開始します。
  2. [File]>[New]>[Project] の順に選択します。
  3. [新しいプロジェクト] ダイアログ ボックスで、[SQL Server データベース プロジェクト、SDK スタイル (プレビュー)] を見つけて選択します。
  4. [名前] テキスト ボックスに「SampleRulesDB」と入力し、[OK] を選択します。

手順 5.3: AvoidWaitForRule Code Analysis ルールを有効にする

  1. ソリューション エクスプローラーSampleRulesDB プロジェクトを選択します。
  2. プロジェクト ノードをダブルクリックしてプロジェクト ファイルを開きます。 SampleRulesDB プロジェクト ファイルがテキスト エディターに表示されます。
  3. RunSqlCodeAnalysis プロパティを true に設定して、SQL プロジェクト ファイルで[ビルド時のコード分析] を有効にします。
  4. プロジェクト [ビルド] アクションが使用されると、ルールが実行され、検出されたすべての WAITFOR DELAY ステートメントが警告として報告されます。

SDK スタイルのプロジェクトでは、パッケージ参照がサポートされるまでカスタム ルールをインストールするための回避策を使用できます。

  1. dotnet restore を実行して SQL プロジェクトに対するプロジェクトの依存関係を復元し、ローカルの NuGet パッケージ キャッシュに Microsoft.Build.Sql が含まれていることを確認します。
  2. 0.1.19-preview など、SQL プロジェクト ファイルで使用される Microsoft.Build.Sql のバージョンに注意してください。
  3. SampleRules.dll アセンブリ ファイルを出力ディレクトリから ~/.nuget/packages/microsoft.build.sql/0.1.19-preview/tools/netstandard2.1 ディレクトリにコピーします。 正確なディレクトリ パスは、SQL プロジェクト ファイルで使用される Microsoft.Build.Sql のバージョンによって異なる場合があります。
  4. RunSqlCodeAnalysis プロパティを true に設定して、SQL プロジェクト ファイルで[ビルド時のコード分析] を有効にします。
  5. dotnet build を実行して SQL プロジェクトをビルドし、カスタム ルールを実行します。

SDK スタイルのプロジェクトでは、パッケージ参照がサポートされるまでカスタム ルールをインストールするための回避策を使用できます。

  1. dotnet restore を実行して SQL プロジェクトに対するプロジェクトの依存関係を復元し、ローカルの NuGet パッケージ キャッシュに Microsoft.Build.Sql が含まれていることを確認します。
  2. 0.1.19-preview など、SQL プロジェクト ファイルで使用される Microsoft.Build.Sql のバージョンに注意してください。
  3. SampleRules.dll アセンブリ ファイルを出力ディレクトリから ~/.nuget/packages/microsoft.build.sql/0.1.19-preview/tools/netstandard2.1 ディレクトリにコピーします。 正確なディレクトリ パスは、SQL プロジェクト ファイルで使用される Microsoft.Build.Sql のバージョンによって異なる場合があります。
  4. RunSqlCodeAnalysis プロパティを true に設定して、SQL プロジェクト ファイルで[ビルド時のコード分析] を有効にします。
  5. dotnet build を実行して SQL プロジェクトをビルドし、カスタム ルールを実行します。