创作自定义代码分析规则
适用于:SQL Server
Azure SQL 数据库
Azure SQL 托管实例
Microsoft Fabric SQL 数据库
此演练演示创建 SQL Server 代码分析规则的步骤。 在此演练中创建的规则用于避免存储过程、触发器和函数中的 WAITFOR DELAY
语句。
此演练使用以下步骤创建 Transact-SQL 静态代码分析的自定义规则:
- 创建类库项目、启用该项目的签名并添加必要的引用。
- 创建两个帮助程序 C# 类。
- 创建 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 包以及如何使用代码编辑器向类添加代码。
- 已安装包括 SQL 数据库 Projects 扩展的 Visual Studio Code 版本。
- 包含 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 类
第一种必须定义的类是从 TSqlConcreteFragmentVisitor 派生的 WaitForDelayVisitor
类。 此类提供了对模型中 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 WaitForDelayVisitor {} }
在类声明中,将访问修饰符更改为内部并从
TSqlConcreteFragmentVisitor
派生类:internal class WaitForDelayVisitor : TSqlConcreteFragmentVisitor {}
添加以下代码以定义列表成员变量:
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
。 资源编辑器随即出现,其中未定义任何资源。定义 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.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:创建自定义规则类
添加自定义代码分析规则将使用的帮助程序类后,创建一个自定义规则类并将其命名为 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
类声明中,将访问修饰符更改为公共:/// <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
。 该规则为模型中的每个匹配元素调用一次。 如果想要分析整个模型,则可以改用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
”。 - (可选)可以为强名称密钥文件指定密码。
- 选择“确定”。
- 在“文件”菜单上,单击“全部保存”。
- 在“生成”菜单上,选择“生成解决方案” 。
- 在“项目”菜单上,选择“SampleRules 属性”。
- 选择“签名”选项卡。
- 选择“为程序集签名”。
- 在“选择强名称密钥文件”中,选择“新建”<>。
- 在“创建强名称密钥”对话框的“密钥文件名称”中,键入“
MyRefKey
”。 - (可选)可以为强名称密钥文件指定密码。
- 选择“确定”。
- 在“文件”菜单上,单击“全部保存”。
- 在“生成”菜单上,选择“生成解决方案” 。
通过依次选择“查看”菜单、“终端”,在 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 项目并执行自定义规则。