共用方式為


撰寫自訂程式碼分析規則

適用於:Microsoft Fabric 中的 SQL ServerAzure SQL 資料庫 Azure SQL 受控執行個體 SQL 資料庫

此逐步解說示範用來建立 SQL Server 程式碼分析規則的步驟。 在此逐步解說中建立的規則旨在避開預存程序、觸發程序和函式中的 WAITFOR DELAY 陳述式。

在此逐步解說中,您將使用下列步驟,建立自訂規則進行 Transact-SQL 靜態 Code Analysis:

  1. 建立類別庫專案、啟用該專案的簽署,以及新增必要參考。
  2. 建立兩個協助程式 Visual C# 類別。
  3. 建立 Visual C# 自訂規則類別。
  4. 建立類別庫專案。
  5. 安裝並測試新的程式碼分析規則。

除了 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 資料庫專案則需要進行第二次安裝。

這個逐步解說適用於已經熟悉 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:建立類別庫專案

首先建立類別庫。 若要建立類別庫專案:

  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.x.x (例如 162.2.111)。

接下來,您需要新增規則將使用的支援類別。

首先建立類別庫。 若要建立類別庫專案:

  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.x.x (例如 162.2.111)。

接下來,您需要新增規則將使用的支援類別。

  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. SampleRules 資料夾中,建立名為 WaitForDelayVisitor.cs 的新檔案。

  1. 瀏覽至 SampleRules 目錄。
  2. 建立名為 WaitForDelayVisitor.cs 的新檔案。
  1. 開啟 WaitForDelayVisitor.cs 檔案並更新內容,以符合下列程式碼:

    using System.Collections.Generic;
    using Microsoft.SqlServer.TransactSql.ScriptDom;
    namespace SampleRules {
        class WaitForDelayVisitor {}
    }
    
  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:新增資源檔與三個資源字串

接下來,新增定義規則名稱、規則說明以及規則將出現在規則組態介面的哪個類別中的資源檔。

  1. 在 [方案總管] 中,選取 SampleRules 專案。 在 [專案] 功能表上,依序選取 [新增]、[新增項目]。 [加入新項目] 對話方塊隨即出現。

  2. 在 [已安裝的範本] 清單中,選取 [一般]。 在詳細資料窗格中,選取 [資源檔]。

  3. 在 [名稱] 中,輸入 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}.
  5. 按一下 [檔案] 功能表上的 [儲存 RuleResources.resx]。

  1. 在 [方案總管] 中,選取 SampleRules 專案。 在 [專案] 功能表上,依序選取 [新增]、[新增項目]。 [加入新項目] 對話方塊隨即出現。

  2. 在 [已安裝的範本] 清單中,選取 [一般]。 在詳細資料窗格中,選取 [資源檔]。

  3. 在 [名稱] 中,輸入 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}.
  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.3:定義 LocalizedExportCodeAnalysisRuleAttribute 類別

第二個類別為 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.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 = "SampleRules.RuleResources";
    
            /// <summary>
            /// Lookup name inside the resources file for the select asterisk rule name
            /// </summary>
            public const string AvoidWaitForDelay_RuleName = "AvoidWaitForDelay_RuleName";
            /// <summary>
            /// Lookup ID inside the resources file for the select asterisk description
            /// </summary>
            public const string AvoidWaitForDelay_ProblemDescription = "AvoidWaitForDelay_ProblemDescription";
    
            /// <summary>
            /// The design category (should not be localized)
            /// </summary>
            public const string CategoryDesign = "Design";
    
            /// <summary>
            /// The performance category (should not be localized)
            /// </summary>
            public const string CategoryPerformance = "Design";
        }
    }
    
  2. 在 [檔案] 功能表上,選取 [儲存]

步驟 3:建立自訂規則類別

新增自訂 Code Analysis 規則將使用的協助程式類別之後,建立自訂規則類別,並將它命名為 AvoidWaitForDelayRuleAvoidWaitForDelayRule 自訂規則將用來協助資料庫開發人員避免在預存程序、觸發程序和函式中使用 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 允許 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

  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.SqlAnalysisRule.Analyze (Microsoft.SqlServer.Dac.CodeAnalysis.SqlRuleExecutionContext) 方法的覆寫,以將 Microsoft.SqlServer.Dac.CodeAnalysis.SqlRuleExecutionContext 用作輸入參數使用。 此方法會傳回潛在問題的清單。

    該方法從上下文參數取得 Microsoft.SqlServer.Dac.Model.TSqlModelMicrosoft.SqlServer.Dac.Model.TSqlObjectTSqlFragment。 然後,使用 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. 選取 [確定]。
  8. 在 [File] \(檔案\) 功能表上,選取 [Save All] \(全部儲存\)
  9. 在 [建置] 功能表上,選取 [建置方案]
  1. 在 [專案] 功能表上,選取 [SampleRules 屬性]。
  2. 選取 [簽署] 索引標籤。
  3. 選取簽署組件
  4. 在 [選擇強式名稱金鑰檔案] 中,選取 [新增]<>
  5. 在 [建立強式名稱金鑰] 對話方塊的 [金鑰檔案名稱] 中,輸入 MyRefKey
  6. (選擇性) 您可以為強式名稱金鑰檔指定密碼。
  7. 選取 [確定]。
  8. 在 [File] \(檔案\) 功能表上,選取 [Save All] \(全部儲存\)
  9. 在 [建置] 功能表上,選取 [建置方案]
  1. 在 Visual Studio Code 中,選取 [檢視] 功能表,再選取 [終端機],以開啟 [終端機] 視窗。

  2. 在 [終端機] 中輸入下列命令,以建置專案:

    dotnet build /p:Configuration=Release
    
  1. 瀏覽至 SampleRules 目錄。

  2. 執行下列命令來建置專案:

    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 取代為 ProfessionalCommunity,視已安裝的 Visual Studio 版本而定。

將 SampleRules.dll 組件檔從輸出目錄複製到 <Visual Studio Install Dir>\Common7\IDE\Extensions\Microsoft\SQLDB\DAC\Extensions 目錄。 依預設,已編譯 .dll 檔案的路徑是 YourSolutionPath\YourProjectPath\bin\DebugYourSolutionPath\YourProjectPath\bin\Release

注意

您可能必須建立 Extensions 目錄。

您的規則現在應該已完成安裝,一旦重新啟動 Visual Studio,即會出現。 接下來,啟動新的 Visual Studio 工作階段並建立資料庫專案。

步驟 5.2:啟動新的 Visual Studio 工作階段並建立資料庫專案

  1. 啟動第二個 Visual Studio 工作階段。
  2. 選取 [檔案]> [新增]> [專案]
  3. 在 [新增專案] 對話框中,找到並選取 [SQL Server 資料庫專案]
  4. 在 [名稱] 文字輸入框中,輸入 SampleRulesDB,然後選取 [確定]

步驟 5.3:啟用 AvoidWaitForRule Code Analysis 規則

  1. 在 [方案總管] 中,選取 SampleRulesDB 專案。
  2. 選取 [專案] 功能表上的 [屬性]SampleRulesDB 屬性頁面將顯示。
  3. 選取 Code Analysis。 您應該會看到名為 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。 將 Enterprise 取代為 ProfessionalCommunity,視已安裝的 Visual Studio 版本而定。

將 SampleRules.dll 組件檔從輸出目錄複製到 <Visual Studio Install Dir>\Common7\IDE\Extensions\Microsoft\SQLDB\DAC\Extensions 目錄。 依預設,已編譯 .dll 檔案的路徑是 YourSolutionPath\YourProjectPath\bin\DebugYourSolutionPath\YourProjectPath\bin\Release

注意

您可能必須建立 Extensions 目錄。

您的規則現在應該已完成安裝,一旦重新啟動 Visual Studio,即會出現。 接下來,啟動新的 Visual Studio 工作階段並建立資料庫專案。

步驟 5.2:啟動新的 Visual Studio 工作階段並建立資料庫專案

  1. 啟動第二個 Visual Studio 工作階段。
  2. 選取 [檔案]> [新增]> [專案]
  3. 在 [新增專案] 對話框中,找到並選取 [SDK 樣式 SQL Server 資料庫專案 (預覽版)]
  4. 在 [名稱] 文字輸入框中,輸入 SampleRulesDB,然後選取 [確定]

步驟 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. 請注意 SQL 專案檔中使用的 Microsoft.Build.Sql 版本,例如 0.1.19-preview
  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. 請注意 SQL 專案檔中使用的 Microsoft.Build.Sql 版本,例如 0.1.19-preview
  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 專案,並執行自訂規則。