カスタム コード分析ルールを作成
適用対象: SQL Server Azure SQL Database Azure SQL Managed Instance
このチュートリアルでは、SQL Server のコード分析ルールを作成する手順について説明します。 このチュートリアルで作成するルールは、ストアド プロシージャ、トリガー、および関数で WAITFOR DELAY
ステートメントを回避する場合に使用します。
このチュートリアルでは、次の手順を使用して、Transact-SQL の静的コード分析のカスタム ルールを作成します。
- クラス ライブラリ プロジェクトを作成し、そのプロジェクトの署名を有効にして、必要な参照を追加します。
- 2 つのヘルパー C# クラスを作成します。
- C# のカスタム ルール クラスを作成します。
- クラス ライブラリ プロジェクトをビルドします。
- 新しいコード分析ルールをインストールしてテストします。
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 番目のインストールが必要です。
- .NET 8 SDK
- Visual Studio 2022 Community、Professional、または Enterprise
- SQL Server Data Tools、Visual Studio 2022 にインストールされた SDK スタイル (プレビュー)
- C# .NET 開発をサポートする、インストールされている Visual Studio のバージョン。
- SQL Server オブジェクトを含む SQL Server プロジェクト。
このチュートリアルは、既に 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 - クラス ライブラリ プロジェクトを作成する
まずクラス ライブラリを作成します。 クラス ライブラリ プロジェクトを作成するには、次の操作を行います。
SampleRules
という名前の C# (.NET Framework) クラス ライブラリ プロジェクトを作成します。ファイルの名前を
Class1.cs
からAvoidWaitForDelayRule.cs
に変更します。ソリューション エクスプローラーで、プロジェクト ノードを右クリックし、[追加]、[参照] の順に選択します。
[アセンブリ\フレームワーク] タブで
System.ComponentModel.Composition
を選択します。ソリューション エクスプローラーで、プロジェクト ノードを右クリックし、[NuGet パッケージの管理] を選択します。
Microsoft.SqlServer.DacFx
NuGet パッケージを見つけてインストールします。 選択したバージョンは、Visual Studio 2022 の場合 (たとえば162.2.111
)162.x.x
である必要があります。
次に、ルールに使用するサポート クラスを追加します。
まずクラス ライブラリを作成します。 クラス ライブラリ プロジェクトを作成するには、次の操作を行います。
SampleRules
という名前の C# (.NET Framework) クラス ライブラリ プロジェクトを作成します。ファイルの名前を
Class1.cs
からAvoidWaitForDelayRule.cs
に変更します。ソリューション エクスプローラーで、プロジェクト ノードを右クリックし、[追加]、[参照] の順に選択します。
[アセンブリ\フレームワーク] タブで
System.ComponentModel.Composition
を選択します。ソリューション エクスプローラーで、プロジェクト ノードを右クリックし、[NuGet パッケージの管理] を選択します。
Microsoft.SqlServer.DacFx
NuGet パッケージを見つけてインストールします。 選択したバージョンは、Visual Studio 2022 の場合 (たとえば162.2.111
)162.x.x
である必要があります。
次に、ルールに使用するサポート クラスを追加します。
Visual Studio Code を起動し、プロジェクトを作成するフォルダーを開きます。
Visual Studio Code 内で、[表示] メニュー、次に [ターミナル] を選択してターミナル ウィンドウを開きます。
[ターミナル] で、次のコマンドを入力して、新しいソリューションとプロジェクトを作成します。
dotnet new sln dotnet new classlib -n SampleRules -o SampleRules dotnet sln add SampleRules/SampleRules.csproj
SampleRules
ディレクトリに変更します。cd SampleRules
必要な NuGet パッケージを追加します。
dotnet add package Microsoft.SqlServer.DacFx
次に、ルールに使用するサポート クラスを追加します。
コマンド プロンプトまたはターミナル ウィンドウを開き、プロジェクトを作成するフォルダーに移動します。
[ターミナル] で、次のコマンドを入力して、新しいソリューションとプロジェクトを作成します。
dotnet new sln dotnet new classlib -n SampleRules -o SampleRules dotnet sln add SampleRules/SampleRules.csproj
SampleRules
ディレクトリに変更します。cd SampleRules
必要な 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 を使用して見つけることができます。
ソリューション エクスプローラーで
SampleRules
プロジェクトを選択します。[プロジェクト] メニューで、[クラスの追加] を選択します。 [新しい項目の追加] ダイアログ ボックスが表示されます。 [名前] テキスト ボックスに「
WaitForDelayVisitor.cs
」と入力し、[追加] を選択します。WaitForDelayVisitor.cs
ファイルは、[ソリューション エクスプローラー] のプロジェクトに追加されます。
ソリューション エクスプローラーで
SampleRules
プロジェクトを選択します。[プロジェクト] メニューで、[クラスの追加] を選択します。 [新しい項目の追加] ダイアログ ボックスが表示されます。 [名前] テキスト ボックスに「
WaitForDelayVisitor.cs
」と入力し、[追加] を選択します。WaitForDelayVisitor.cs
ファイルは、[ソリューション エクスプローラー] のプロジェクトに追加されます。
Visual Studio Code で[エクスプローラー] ビューを開きます。
WaitForDelayVisitor.cs
フォルダーに、SampleRules
という名前の新しいファイルを作成します。
SampleRules
ディレクトリに移動します。WaitForDelayVisitor.cs
という名前で新しいファイルを作成します。
WaitForDelayVisitor.cs
ファイルを開き、次のコードに合わせて内容を更新します。using System.Collections.Generic; using Microsoft.SqlServer.TransactSql.ScriptDom; namespace SampleRules { class WaitForDelayVistor {} }
クラス宣言で、アクセス修飾子を internal に変更し、
TSqlConcreteFragmentVisitor
からクラスを派生させます。internal class WaitForDelayVisitor : TSqlConcreteFragmentVisitor {}
次のコードを追加して、List メンバー変数を定義します。
public IList<WaitForStatement> WaitForDelayStatements { get; private set; }
次のコードを追加して、クラス コンストラクターを定義します。
public WaitForDelayVisitor() { WaitForDelayStatements = new List<WaitForStatement>(); }
次のコードを追加して、
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 です。[ファイル] メニューの [保存] をクリックします。
手順 2.2: LocalizedExportCodeAnalysisRuleAttribute クラスを定義する
2 つ目のクラスは、LocalizedExportCodeAnalysisRuleAttribute.cs
です。 これはフレームワークによって提供される組み込みの Microsoft.SqlServer.Dac.CodeAnalysis.ExportCodeAnalysisRuleAttribute
の拡張機能であり、リソース ファイルからのルールによって使用される DisplayName
と Description
の読み取りをサポートします。 このクラスは、複数の言語でルールを使用する予定がある場合に便利です。
ソリューション エクスプローラーで
SampleRules
プロジェクトを選択します。[プロジェクト] メニューで、[クラスの追加] を選択します。 [新しい項目の追加] ダイアログ ボックスが表示されます。 [名前] テキスト ボックスに「
LocalizedExportCodeAnalysisRuleAttribute.cs
」と入力し、[追加] を選択します。 ファイルは、ソリューション エクスプローラーのプロジェクトに追加されます。
ソリューション エクスプローラーで
SampleRules
プロジェクトを選択します。[プロジェクト] メニューで、[クラスの追加] を選択します。 [新しい項目の追加] ダイアログ ボックスが表示されます。 [名前] テキスト ボックスに「
LocalizedExportCodeAnalysisRuleAttribute.cs
」と入力し、[追加] を選択します。 ファイルは、ソリューション エクスプローラーのプロジェクトに追加されます。
- Visual Studio Code の [エクスプローラー] ビューで
SampleRules
ディレクトリに移動します。 LocalizedExportCodeAnalysisRuleAttribute.cs
という名前で新しいファイルを作成します。
SampleRules
ディレクトリに移動します。LocalizedExportCodeAnalysisRuleAttribute.cs
という名前で新しいファイルを作成します。
ファイルを開き、次のコードに合わせて内容を更新します。
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 つのリソース文字列を追加する
次に、ルール名、ルールの説明、およびカテゴリを定義したリソース ファイルを追加します。このルールは、ルール構成インターフェイスに表示されます。
ソリューション エクスプローラーで
SampleRules
プロジェクトを選択します。 [プロジェクト] メニューで、[追加]、[新しい項目] の順に選択します。 [新しい項目の追加] ダイアログ ボックスが表示されます。[インストールされたテンプレート] の一覧で、[全般] を選択します。 詳細ウィンドウの [リソース ファイル] を選択します。
[名前]に「
RuleResources.resx
」と入力します。 リソースが定義されていないリソース エディターが表示されます。次のように、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}.
[ファイル] メニューで、[RuleResources.resx の保存] を選択します。
ソリューション エクスプローラーで
SampleRules
プロジェクトを選択します。 [プロジェクト] メニューで、[追加]、[新しい項目] の順に選択します。 [新しい項目の追加] ダイアログ ボックスが表示されます。[インストールされたテンプレート] の一覧で、[全般] を選択します。 詳細ウィンドウの [リソース ファイル] を選択します。
[名前]に「
RuleResources.resx
」と入力します。 リソースが定義されていないリソース エディターが表示されます。次のように、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}.
[ファイル] メニューで、[RuleResources.resx の保存] を選択します。
SampleRules
ディレクトリにRuleResources.resx
という名前の新しいファイルを作成します。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>
RuleResources.resx
ファイルを保存します。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>
SampleRules.csproj
ファイルを保存します。
SampleRules
ディレクトリにRuleResources.resx
という名前の新しいファイルを作成します。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>
RuleResources.resx
ファイルを保存します。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>
SampleRules.csproj
ファイルを保存します。
手順 2.4: SampleConstants クラスを定義する
次に、ユーザー インターフェイスにルールに関する情報を表示するときに Visual Studio で使用されるリソース ファイル内のリソースを参照するクラスを定義します。
ソリューション エクスプローラーで
SampleRules
プロジェクトを選択します。[プロジェクト] メニューで、[追加]、[クラス] の順に選択します。 [新しい項目の追加] ダイアログ ボックスが表示されます。 [名前] テキスト ボックスに「
SampleRuleConstants.cs
」と入力し、[追加] ボタンを選択します。SampleRuleConstants.cs
ファイルは、[ソリューション エクスプローラー] のプロジェクトに追加されます。
ソリューション エクスプローラーで
SampleRules
プロジェクトを選択します。[プロジェクト] メニューで、[追加]、[クラス] の順に選択します。 [新しい項目の追加] ダイアログ ボックスが表示されます。 [名前] テキスト ボックスに「
SampleRuleConstants.cs
」と入力し、[追加] ボタンを選択します。SampleRuleConstants.cs
ファイルは、[ソリューション エクスプローラー] のプロジェクトに追加されます。
- Visual Studio Code の [エクスプローラー] ビューで
SampleRules
ディレクトリに移動します。 SampleRuleConstants.cs
という名前で新しいファイルを作成します。
SampleRules
ディレクトリに移動します。SampleRuleConstants.cs
という名前で新しいファイルを作成します。
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"; } }
[ファイル] メニューの [保存] をクリックします。
手順 3: カスタム ルール クラスを作成する
カスタム コード分析ルールで使用するヘルパー クラスを追加したら、カスタム ルール クラスを作成し、AvoidWaitForDelayRule
という名前を付けます。 データベースの開発時に、AvoidWaitForDelayRule
カスタム ルールを使用すると、ストアド プロシージャ、トリガー、および関数で WAITFOR DELAY
ステートメントを回避できます。
手順 3.1: AvoidWaitForDelayRule クラスを作成する
ソリューション エクスプローラーで
SampleRules
プロジェクトを選択します。[プロジェクト] メニューで、[追加]、[クラス] の順に選択します。 [新しい項目の追加] ダイアログ ボックスが表示されます。 [名前] テキスト ボックスに「
AvoidWaitForDelayRule.cs
」と入力し、[追加] を選択します。AvoidWaitForDelayRule.cs
ファイルは、[ソリューション エクスプローラー] のプロジェクトに追加されます。
ソリューション エクスプローラーで
SampleRules
プロジェクトを選択します。[プロジェクト] メニューで、[追加]、[クラス] の順に選択します。 [新しい項目の追加] ダイアログ ボックスが表示されます。 [名前] テキスト ボックスに「
AvoidWaitForDelayRule.cs
」と入力し、[追加] を選択します。AvoidWaitForDelayRule.cs
ファイルは、[ソリューション エクスプローラー] のプロジェクトに追加されます。
- Visual Studio Code の [エクスプローラー] ビューで
SampleRules
ディレクトリに移動します。 AvoidWaitForDelayRule.cs
という名前で新しいファイルを作成します。
SampleRules
ディレクトリに移動します。AvoidWaitForDelayRule.cs
という名前で新しいファイルを作成します。
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 {} }
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
AvoidWaitForDelayRule
クラスは、Microsoft.SqlServer.Dac.CodeAnalysis.SqlCodeAnalysisRule
基本クラスから派生します。public sealed class AvoidWaitForDelayRule : SqlCodeAnalysisRule
クラスに
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
を使用できます。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 }; }
入力パラメーターとして
Microsoft.SqlServer.Dac.CodeAnalysis.SqlRuleExecutionContext
を使用するMicrosoft.SqlServer.Dac.CodeAnalysis.SqlAnalysisRule.Analyze
(Microsoft.SqlServer.Dac.CodeAnalysis.SqlRuleExecutionContext)
メソッドのオーバーライドを追加します。 このメソッドは、可能性のある問題の一覧を返します。メソッドは、コンテキスト パラメーターから 、
Microsoft.SqlServer.Dac.Model.TSqlModel
、Microsoft.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); }
[ファイル] メニューの [保存]をクリックします。
手順 4: クラス ライブラリをビルドする
- [プロジェクト] メニューの [SampleRules のプロパティ] を選択します。
- [署名] タブを選択します
- [アセンブリの署名] を選択します。
- [厳密な名前のキー ファイルを選択してください] で <[新規作成]> を選択します。
- [厳密な名前キーの作成] ダイアログ ボックスで、[キー ファイル] に「
MyRefKey
」と入力します。 - (省略可能) 厳密な名前のキー ファイルにパスワードを指定できます。
- [OK] を選択します。
- [ファイル] メニューの [すべてを保存] をクリックします。
- [ビルド] メニューの [ソリューションのビルド] を選択します。
- [プロジェクト] メニューの [SampleRules のプロパティ] を選択します。
- [署名] タブを選択します
- [アセンブリの署名] を選択します。
- [厳密な名前のキー ファイルを選択してください] で <[新規作成]> を選択します。
- [厳密な名前キーの作成] ダイアログ ボックスで、[キー ファイル] に「
MyRefKey
」と入力します。 - (省略可能) 厳密な名前のキー ファイルにパスワードを指定できます。
- [OK] を選択します。
- [ファイル] メニューの [すべてを保存] をクリックします。
- [ビルド] メニューの [ソリューションのビルド] を選択します。
Visual Studio Code 内で、[表示] メニュー、次に [ターミナル] を選択してターミナル ウィンドウを開きます。
[ターミナル] で次のコマンドを入力して、プロジェクトをビルドします。
dotnet build /p:Configuration=Release
SampleRules
ディレクトリに移動します。次のコマンドを実行してプロジェクトをビルドします。
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 エディションに応じて、Enterprise
を Professional
または 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 セッションを開始して、データベース プロジェクトを作成する
- Visual Studio の 2 つ目のセッションを開始します。
- [File]>[New]>[Project] の順に選択します。
- [新しいプロジェクト] ダイアログ ボックスで、[SQL Server データベース プロジェクト] を見つけて選択します。
- [名前] テキスト ボックスに「
SampleRulesDB
」と入力し、[OK] を選択します。
手順 5.3: AvoidWaitForRule Code Analysis ルールを有効にする
- ソリューション エクスプローラーで
SampleRulesDB
プロジェクトを選択します。 - [プロジェクト] メニューの [プロパティ] を選択します。
SampleRulesDB
プロパティ ページが表示されます。 - [コード分析] を選択します。
RuleSamples.CategorySamples
という名前の新しいカテゴリが表示されます。 RuleSamples.CategorySamples
を展開します。SR1004: Avoid WAITFOR DELAY statement in stored procedures, triggers, and functions
という結果が表示されます。- このルールを有効にするには、ルール名の横にあるチェックボックスと、[ビルド時にコード分析を有効にする] のチェックボックスをオンにします。 コード分析を有効にする方法の詳細については、「コード分析の概要」を確認してください。
- プロジェクト [ビルド] アクションが使用されると、ルールが実行され、検出されたすべての
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 エディションに応じて、Enterprise
を Professional
または 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 セッションを開始して、データベース プロジェクトを作成する
- Visual Studio の 2 つ目のセッションを開始します。
- [File]>[New]>[Project] の順に選択します。
- [新しいプロジェクト] ダイアログ ボックスで、[SQL Server データベース プロジェクト、SDK スタイル (プレビュー)] を見つけて選択します。
- [名前] テキスト ボックスに「
SampleRulesDB
」と入力し、[OK] を選択します。
手順 5.3: AvoidWaitForRule Code Analysis ルールを有効にする
- ソリューション エクスプローラーで
SampleRulesDB
プロジェクトを選択します。 - プロジェクト ノードをダブルクリックしてプロジェクト ファイルを開きます。
SampleRulesDB
プロジェクト ファイルがテキスト エディターに表示されます。 RunSqlCodeAnalysis
プロパティをtrue
に設定して、SQL プロジェクト ファイルで[ビルド時のコード分析] を有効にします。- プロジェクト [ビルド] アクションが使用されると、ルールが実行され、検出されたすべての
WAITFOR DELAY
ステートメントが警告として報告されます。
SDK スタイルのプロジェクトでは、パッケージ参照がサポートされるまでカスタム ルールをインストールするための回避策を使用できます。
dotnet restore
を実行して SQL プロジェクトに対するプロジェクトの依存関係を復元し、ローカルの NuGet パッケージ キャッシュに Microsoft.Build.Sql が含まれていることを確認します。0.1.19-preview
など、SQL プロジェクト ファイルで使用される Microsoft.Build.Sql のバージョンに注意してください。SampleRules.dll
アセンブリ ファイルを出力ディレクトリから~/.nuget/packages/microsoft.build.sql/0.1.19-preview/tools/netstandard2.1
ディレクトリにコピーします。 正確なディレクトリ パスは、SQL プロジェクト ファイルで使用される Microsoft.Build.Sql のバージョンによって異なる場合があります。RunSqlCodeAnalysis
プロパティをtrue
に設定して、SQL プロジェクト ファイルで[ビルド時のコード分析] を有効にします。dotnet build
を実行して SQL プロジェクトをビルドし、カスタム ルールを実行します。
SDK スタイルのプロジェクトでは、パッケージ参照がサポートされるまでカスタム ルールをインストールするための回避策を使用できます。
dotnet restore
を実行して SQL プロジェクトに対するプロジェクトの依存関係を復元し、ローカルの NuGet パッケージ キャッシュに Microsoft.Build.Sql が含まれていることを確認します。0.1.19-preview
など、SQL プロジェクト ファイルで使用される Microsoft.Build.Sql のバージョンに注意してください。SampleRules.dll
アセンブリ ファイルを出力ディレクトリから~/.nuget/packages/microsoft.build.sql/0.1.19-preview/tools/netstandard2.1
ディレクトリにコピーします。 正確なディレクトリ パスは、SQL プロジェクト ファイルで使用される Microsoft.Build.Sql のバージョンによって異なる場合があります。RunSqlCodeAnalysis
プロパティをtrue
に設定して、SQL プロジェクト ファイルで[ビルド時のコード分析] を有効にします。dotnet build
を実行して SQL プロジェクトをビルドし、カスタム ルールを実行します。