撰寫自訂程式碼分析規則
適用於:Microsoft Fabric 中的 SQL ServerAzure SQL 資料庫 Azure SQL 受控執行個體 SQL 資料庫
此逐步解說示範用來建立 SQL Server 程式碼分析規則的步驟。 在此逐步解說中建立的規則旨在避開預存程序、觸發程序和函式中的 WAITFOR DELAY
陳述式。
在此逐步解說中,您將使用下列步驟,建立自訂規則進行 Transact-SQL 靜態 Code Analysis:
- 建立類別庫專案、啟用該專案的簽署,以及新增必要參考。
- 建立兩個協助程式 Visual C# 類別。
- 建立 Visual C# 自訂規則類別。
- 建立類別庫專案。
- 安裝並測試新的程式碼分析規則。
除了 Visual Studio (SQL Server Data Tools) 指令之外,本指南著重於 SDK 樣式的 SQL 專案。
必要條件
您需要下列元件才能完成這個逐步解說:
- 已安裝包含 SQL Server Data Tools 且支援 C# .NET Framework 開發的 Visual Studio 版本。
- 包含 SQL Server 物件的 SQL Server 專案。
- 可以部署資料庫專案的 SQL Server 執行個體。
這個逐步解說適用於已經熟悉 SQL Server Data Tools SQL Server 功能的使用者。 您應該很熟悉 Visual Studio 概念,例如如何建立類別庫、新增 NuGet 套件及如何使用程式碼編輯器將程式碼加入至類別。
注意
由於 SDK 樣式 SQL Server Data Tools (預覽版) 的限制,完成此逐步教學需要多次安裝 Visual Studio。 建立類別庫專案需要進行第一次安裝,建立 SDK 樣式 SQL 資料庫專案則需要進行第二次安裝。
- .NET 8 SDK
- Visual Studio 2022 Community、Professional 或 Enterprise
- Visual Studio 2022 中已安裝 SDK 樣式 SQL Server Data Tools (預覽版)
- 已安裝某一版支援 C# .NET 開發的 Visual Studio。
- 包含 SQL Server 物件的 SQL Server 專案。
這個逐步解說適用於已經熟悉 SQL Server Data Tools SQL Server 功能的使用者。 您應該很熟悉 Visual Studio 概念,例如如何建立類別庫、新增 NuGet 套件及如何使用程式碼編輯器將程式碼加入至類別。
- 已安裝的 Visual Studio Code 版本,其中包含 SQL 資料庫專案延伸模組。
- 包含 SQL 物件的 SQL 資料庫專案。
- .NET 8 SDK
- 建議:適用於 VS Code 的 C# 開發工具包延伸模組
本逐步解說適用於已經熟悉 Visual Studio Code 中 SQL 資料庫專案延伸模組的使用者。 您應該很熟悉開發概念,例如如何建立類別庫、新增套件及如何使用程式碼編輯器來編輯程式碼。
- 文字編輯器,例如 Visual Studio Code 中的檔案編輯器。
- 包含 SQL 物件的 SQL 資料庫專案。
- .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.x.x
(例如162.2.111
)。
接下來,您需要新增規則將使用的支援類別。
首先建立類別庫。 若要建立類別庫專案:
建立名為
SampleRules
的 C# (.NET Framework) 類別庫專案。將檔案
Class1.cs
重新命名為AvoidWaitForDelayRule.cs
。在方案總管中,以滑鼠右鍵按一下專案節點,然後依序選取 [新增]、[參考]。
在 [元件\架構] 索引標籤中選取
System.ComponentModel.Composition
。在方案總管中,以滑鼠右鍵按一下專案節點,然後選取 [管理 NuGet 套件]。 找到並安裝
Microsoft.SqlServer.DacFx
NuGet 套件。 對於 Visual Studio 2022,選取的版本必須為162.x.x
(例如162.2.111
)。
接下來,您需要新增規則將使用的支援類別。
啟動 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 中,開啟 [總管] 檢視。
在
SampleRules
資料夾中,建立名為WaitForDelayVisitor.cs
的新檔案。
- 瀏覽至
SampleRules
目錄。 - 建立名為
WaitForDelayVisitor.cs
的新檔案。
開啟
WaitForDelayVisitor.cs
檔案並更新內容,以符合下列程式碼:using System.Collections.Generic; using Microsoft.SqlServer.TransactSql.ScriptDom; namespace SampleRules { class WaitForDelayVisitor {} }
在類別宣告中,將存取修飾詞變更為 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:新增資源檔與三個資源字串
接下來,新增定義規則名稱、規則說明以及規則將出現在規則組態介面的哪個類別中的資源檔。
在 [方案總管] 中,選取
SampleRules
專案。 在 [專案] 功能表上,依序選取 [新增]、[新增項目]。 [加入新項目] 對話方塊隨即出現。在 [已安裝的範本] 清單中,選取 [一般]。 在詳細資料窗格中,選取 [資源檔]。
在 [名稱] 中,輸入
RuleResources.resx
。 資源編輯器隨即出現,沒有已定義的資源。定義四個資源字串,如下所示:
名稱 值 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
。 資源編輯器隨即出現,沒有已定義的資源。定義四個資源字串,如下所示:
名稱 值 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.3:定義 LocalizedExportCodeAnalysisRuleAttribute 類別
第二個類別為 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.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 = "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"; } }
在 [檔案] 功能表上,選取 [儲存]。
步驟 3:建立自訂規則類別
新增自訂 Code Analysis 規則將使用的協助程式類別之後,建立自訂規則類別,並將它命名為 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
允許 Code Analysis 服務探索自訂 Code Analysis 規則。 只有以ExportCodeAnalysisRuleAttribute
(或衍生自此項的屬性) 標示的類別才能在 Code Analysis 中使用。LocalizedExportCodeAnalysisRuleAttribute
提供服務所使用的一些必要中繼資料。 這包括此規則的唯一識別碼、將顯示在 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
,因為此規則會分析特定元素。 對於模型中的每一個相符元素,將呼叫一次規則。 若要分析整個模型,可以改為使用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.SqlAnalysisRule.Analyze
(Microsoft.SqlServer.Dac.CodeAnalysis.SqlRuleExecutionContext)
方法的覆寫,以將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
。 - (選擇性) 您可以為強式名稱金鑰檔指定密碼。
- 選取 [確定]。
- 在 [File] \(檔案\) 功能表上,選取 [Save All] \(全部儲存\)。
- 在 [建置] 功能表上,選取 [建置方案]。
- 在 [專案] 功能表上,選取 [SampleRules 屬性]。
- 選取 [簽署] 索引標籤。
- 選取簽署組件。
- 在 [選擇強式名稱金鑰檔案] 中,選取 [新增]<>。
- 在 [建立強式名稱金鑰] 對話方塊的 [金鑰檔案名稱] 中,輸入
MyRefKey
。 - (選擇性) 您可以為強式名稱金鑰檔指定密碼。
- 選取 [確定]。
- 在 [File] \(檔案\) 功能表上,選取 [Save All] \(全部儲存\)。
- 在 [建置] 功能表上,選取 [建置方案]。
在 Visual Studio Code 中,選取 [檢視] 功能表,再選取 [終端機],以開啟 [終端機] 視窗。
在 [終端機] 中輸入下列命令,以建置專案:
dotnet build /p:Configuration=Release
瀏覽至
SampleRules
目錄。執行下列命令來建置專案:
dotnet build /p:Configuration=Release
步驟 5:安裝並測試新的程式碼分析規則
下一步,您必須安裝組件,以便在建置 SQL 資料庫專案時將其載入。
若要安裝將在您使用 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
。 將 Enterprise
取代為 Professional
或 Community
,視已安裝的 Visual Studio 版本而定。
將 SampleRules.dll 組件檔從輸出目錄複製到 <Visual Studio Install Dir>\Common7\IDE\Extensions\Microsoft\SQLDB\DAC\Extensions
目錄。 依預設,已編譯 .dll
檔案的路徑是 YourSolutionPath\YourProjectPath\bin\Debug
或 YourSolutionPath\YourProjectPath\bin\Release
。
注意
您可能必須建立 Extensions
目錄。
您的規則現在應該已完成安裝,一旦重新啟動 Visual Studio,即會出現。 接下來,啟動新的 Visual Studio 工作階段並建立資料庫專案。
步驟 5.2:啟動新的 Visual Studio 工作階段並建立資料庫專案
- 啟動第二個 Visual Studio 工作階段。
- 選取 [檔案]> [新增]> [專案]。
- 在 [新增專案] 對話框中,找到並選取 [SQL Server 資料庫專案]。
- 在 [名稱] 文字輸入框中,輸入
SampleRulesDB
,然後選取 [確定]。
步驟 5.3:啟用 AvoidWaitForRule Code Analysis 規則
- 在 [方案總管] 中,選取
SampleRulesDB
專案。 - 選取 [專案] 功能表上的 [屬性]。
SampleRulesDB
屬性頁面將顯示。 - 選取 Code Analysis。 您應該會看到名為
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
。 將 Enterprise
取代為 Professional
或 Community
,視已安裝的 Visual Studio 版本而定。
將 SampleRules.dll 組件檔從輸出目錄複製到 <Visual Studio Install Dir>\Common7\IDE\Extensions\Microsoft\SQLDB\DAC\Extensions
目錄。 依預設,已編譯 .dll
檔案的路徑是 YourSolutionPath\YourProjectPath\bin\Debug
或 YourSolutionPath\YourProjectPath\bin\Release
。
注意
您可能必須建立 Extensions
目錄。
您的規則現在應該已完成安裝,一旦重新啟動 Visual Studio,即會出現。 接下來,啟動新的 Visual Studio 工作階段並建立資料庫專案。
步驟 5.2:啟動新的 Visual Studio 工作階段並建立資料庫專案
- 啟動第二個 Visual Studio 工作階段。
- 選取 [檔案]> [新增]> [專案]。
- 在 [新增專案] 對話框中,找到並選取 [SDK 樣式 SQL Server 資料庫專案 (預覽版)]。
- 在 [名稱] 文字輸入框中,輸入
SampleRulesDB
,然後選取 [確定]。
步驟 5.3:啟用 AvoidWaitForRule Code Analysis 規則
- 在 [方案總管] 中,選取
SampleRulesDB
專案。 - 按兩下專案節點即可開啟專案檔。
SampleRulesDB
專案檔會在文字編輯器中顯示。 - 將
RunSqlCodeAnalysis
屬性設定為true
,在 SQL 專案檔中啟用建置的程式碼分析。 - 使用專案建置動作時,將會執行規則,而且找到的任何
WAITFOR DELAY
陳述式都會報告為警告。
SDK 樣式專案可以使用因應措施來安裝自訂規則,直到支援套件參考為止。
- 執行
dotnet restore
以還原 SQL 專案上的專案相依性,以確保本機 NuGet 套件快取包含 Microsoft.Build.Sql。 - 請注意 SQL 專案檔中使用的 Microsoft.Build.Sql 版本,例如
0.1.19-preview
。 - 將
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。 - 請注意 SQL 專案檔中使用的 Microsoft.Build.Sql 版本,例如
0.1.19-preview
。 - 將
SampleRules.dll
組件檔從輸出目錄複製到~/.nuget/packages/microsoft.build.sql/0.1.19-preview/tools/netstandard2.1
目錄。 確切的目錄路徑可能會根據 SQL 專案檔中使用的 Microsoft.Build.Sql 版本而有所不同。 - 將
RunSqlCodeAnalysis
屬性設定為true
,在 SQL 專案檔中啟用建置的程式碼分析。 - 執行
dotnet build
以建置 SQL 專案,並執行自訂規則。