Merge pull request #844 from aguzev/master

Add SQL Assessment API sample .NET application
This commit is contained in:
Ebru Ersan
2020-11-11 12:13:52 +03:00
committed by GitHub
7 changed files with 757 additions and 0 deletions
@@ -0,0 +1,350 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
# User-specific files
*.rsuser
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Mono auto generated files
mono_crash.*
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
[Ll]ogs/
# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# Visual Studio 2017 auto generated files
Generated\ Files/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUnit
*.VisualState.xml
TestResult.xml
nunit-*.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# Benchmark Results
BenchmarkDotNet.Artifacts/
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
# StyleCop
StyleCopReport.xml
# Files built by Visual Studio
*_i.c
*_p.c
*_h.h
*.ilk
*.meta
*.obj
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*_wpftmp.csproj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# Visual Studio Trace Files
*.e2e
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# NuGet Symbol Packages
*.snupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx
*.appxbundle
*.appxupload
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!?*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
ServiceFabricBackup/
*.rptproj.bak
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
*- [Bb]ackup.rdl
*- [Bb]ackup ([0-9]).rdl
*- [Bb]ackup ([0-9][0-9]).rdl
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# CodeRush personal settings
.cr/personal
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Tabs Studio
*.tss
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# OpenCover UI analysis results
OpenCover/
# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
*.binlog
# NVidia Nsight GPU debugger configuration file
*.nvuser
# MFractors (Xamarin productivity tool) working folder
.mfractor/
# Local History for Visual Studio
.localhistory/
# BeatPulse healthcheck temp database
healthchecksdb
# Backup folder for Package Reference Convert tool in Visual Studio 2017
MigrationBackup/
# Ionide (cross platform F# VS Code tools) working folder
.ionide/
@@ -0,0 +1,27 @@
{
// Use IntelliSense to find out which attributes exist for C# debugging
// Use hover for the description of the existing attributes
// For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md
"version": "0.2.0",
"configurations": [
{
"name": ".NET Core Launch (console)",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
// If you have changed target frameworks, make sure to update the program path.
"program": "${workspaceFolder}/AssessmentClient.SMO/bin/Debug/netcoreapp3.1/AssessmentClient.SMO.dll",
"args": [],
"cwd": "${workspaceFolder}/AssessmentClient.SMO",
// For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console
"console": "externalTerminal",
"stopAtEntry": false
},
{
"name": ".NET Core Attach",
"type": "coreclr",
"request": "attach",
"processId": "${command:pickProcess}"
}
]
}
@@ -0,0 +1,42 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "build",
"command": "dotnet",
"type": "process",
"args": [
"build",
"${workspaceFolder}/AssessmentClient.SMO/AssessmentClient.SMO.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
},
{
"label": "publish",
"command": "dotnet",
"type": "process",
"args": [
"publish",
"${workspaceFolder}/AssessmentClient.SMO/AssessmentClient.SMO.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
},
{
"label": "watch",
"command": "dotnet",
"type": "process",
"args": [
"watch",
"run",
"${workspaceFolder}/AssessmentClient.SMO/AssessmentClient.SMO.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
}
]
}
@@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.SqlServer.SqlManagementObjects" Version="161.44091.28" />
<PackageReference Include="Microsoft.SqlServer.Assessment" Version="1.0.280" />
</ItemGroup>
</Project>
@@ -0,0 +1,75 @@
namespace AssessmentClient.SMO
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.SqlServer.Management.Assessment;
using Microsoft.SqlServer.Management.Assessment.Checks;
using Microsoft.SqlServer.Management.Smo;
public static class Program
{
private static async Task Main(string[] args)
{
// Connect to a server or a database with SMO
// https://docs.microsoft.com/en-us/sql/relational-databases/server-management-objects-smo/create-program/connecting-to-an-instance-of-sql-server
var target = new Server();
// Use GetAssessmentItem method to obtain
// a list of available SQL Assessment checks
IEnumerable<ICheck> checklist = target.GetAssessmentItems();
// Checks are tagged with strings corresponding to
// categories like "Performance", "Storage", or "Security"
var allTags = new SortedSet<string>(checklist.SelectMany(c => c.Tags));
DisplayCategories(target.Name, allTags);
while (Prompt(out string? line))
{
// Use GetAssessmentResultsList to run assessment
List<IAssessmentResult> assessmentResults = string.IsNullOrWhiteSpace(line)
? await target.GetAssessmentResultsList().ConfigureAwait(false) // all checks
: await target.GetAssessmentResultsList(line.Split()).ConfigureAwait(false); // selected checks
DisplayAssessmentResults(assessmentResults);
}
}
private static void DisplayAssessmentResults(List<IAssessmentResult> assessmentResults)
{
// Properties of IAssessmentResult provide
// recommendation text, help link, etc
foreach (var result in assessmentResults)
{
Console.WriteLine("-------");
Console.Write(" ");
Console.WriteLine(result.Message);
Console.Write(" ");
Console.WriteLine(result.Check.HelpLink);
}
}
private static bool Prompt(out string? line)
{
Console.Write("Enter category (ENTER for all categories, 'exit' to leave) > ");
line = Console.ReadLine();
return string.Compare(line, "exit", StringComparison.OrdinalIgnoreCase) != 0;
}
private static void DisplayCategories(string targetName, IEnumerable<string> allTags)
{
Console.WriteLine($"All categories available for {targetName}:\n");
foreach (var tag in allTags)
{
Console.WriteLine($" {tag}");
}
}
}
}
@@ -0,0 +1,25 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.30621.155
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AssessmentClient.SMO", "AssessmentClient.SMO\AssessmentClient.SMO.csproj", "{C9745F7C-40B1-43FA-AA5D-C2BF0588038D}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{C9745F7C-40B1-43FA-AA5D-C2BF0588038D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C9745F7C-40B1-43FA-AA5D-C2BF0588038D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C9745F7C-40B1-43FA-AA5D-C2BF0588038D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C9745F7C-40B1-43FA-AA5D-C2BF0588038D}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {113F74E6-A46A-4090-A4F5-7280F2623F23}
EndGlobalSection
EndGlobal
@@ -0,0 +1,224 @@
# SQL Assessment API client sample
## Overview
This repository contains an example C# application using SQL Assessment API. The application uses popular [SQL Management Objects (SMO)](https://docs.microsoft.com/en-us/sql/relational-databases/server-management-objects-smo/overview-smo?view=sql-server-ver15) library. WHile it was not required to invoke assessment, SMO gives a convenient object-oriented API for managing SQL Server objects. See [Service based SQL Assessment](#Using-SQL-Tools-Service) for an example of non-SMO application.
To use SQL Assessment API this sample project references [Microsoft.SqlServer.SqlManagementObjects](https://www.nuget.org/packages/Microsoft.SqlServer.SqlManagementObjects) and [Microsoft.SqlServer.Assessment](https://www.nuget.org/packages/Microsoft.SqlServer.Assessment/) Nuget packages.
## SQL Assessment workflow
A typical SQL Assessment workflow consists of three stages:
1. Establish a connection to a SQL Server and select target instance or database (Microsoft.Data.SqlClient, SQL Management Objects, SQL Tools Service, PowerShell).
2. (Optional) Create SQL Assessment check list. A default check list is used if this step was skipped.
3. Invoke assessment on the target object selected at step 1.
## Using SQL Assessment with SQL Management Objects
### Establish a connection and select a SQL Server object with SMO
SQL Management Objects is a handy tool for accessing and managing SQL SErver objects. This sample application connects to a local SQL Server instance with a short statement:
```CSharp
var target = new Server();
```
To assess a database replace this line with the following snippet:
```CSharp
var target = new Server().Databases["MyDatabase"];
```
See [SMO documentation](https://docs.microsoft.com/en-us/sql/relational-databases/server-management-objects-smo/overview-smo) for more details on connecting to a server or a database.
### Invoke assessment with default check list
SQL Assessment asynchronous code returns a list of SQL Assessment results. Each result can be an assessment note, warning, or error. Every assessment result contains a message to the user with a recommendation. Each assessment note is associated to the corresponding check from the list.
```CSharp
List<IAssessmentResult> results = await target.GetAssessmentResultsList();
```
### Invoke assessment with selected checks
SQL Assessment API gives a collection of checks currently available for given target. Selected checks may be passed to `GetAssessmentResultsList`.
```CSharp
IEnumerable<ICheck> availableChecks = target.GetAssessmentItems();
```
Select checks to be run.
```CSharp
var checklist = availableChecks.First(5);
var results = await target.GetAssessmentResultsList(checklist);
```
### Select checks by category
SQL Assessment check may be tagged with one or more category names. Checks from default check list always have "DefaultRuleset" tag and at least one area tag. "DefaultRuleset" denotes a check produced by default ruleset supplied with SQL Assessment API. Area tags may be "Performance", "Security", "Memory", "Deprecated", "Storage", etc.
```CSharp
var checklist = target.GetAssessmentItems("Performance", "Security");
var results = await target.GetAssessmentResultsList(checklist);
```
This is equivalent to the following code:
```CSharp
var results = await target.GetAssessmentResultsList("Performance", "Security");
```
### Custom rule sets
Checks are generated by a SQL Assessment engine for every target. The engine uses rules from one or more rule sets. Rule sets are collected on a stack. A rule set may override rules from an underlying rule set. A ruleset may be constructed with a C# code, but we recommend using declarative JSON format. For more details on rule set files see [SQL Assessment API GitHub page](https://github.com/microsoft/sql-server-samples/tree/master/samples/manage/sql-assessment-api).
```CSharp
using (var reader = File.OpenText("my ruleset.json"))
{
SqlAssessmentExtensions.Engine.PushRuleFactoryJson(reader);
}
```
## Using SQL Tools Service
[SQL Tools Service](https://github.com/microsoft/sqltoolsservice) is a JSON-RPC service over stdio. SQL assessment takes the same three steps: connect, select checks, run.
_In the following examples adjust `Content-Length` value according to actual JSON length including CR or CRLF line endings._
### Connect with SQL Tools Service
```json
Content-Length:267
{
"jsonrpc": "2.0",
"id": "12",
"method": "connection/connect",
"params": {
"ownerUri": "my connection",
"connection": {
"serverName": "(local)",
"authenticationType": "Integrated"
}
}
}
```
See [SQL Tools Service documentation](https://microsoft.github.io/sqltoolssdk/) for more details on connecting to a server or a database.
### Get available checks
Use `targetType` to select target type:
1. SQL Server instance.
2. SQL Server database. In this case provide database name while connecting to the target.
```json
Content-Length:181
{
"jsonrpc": "2.0",
"id": "12",
"method": "assessment/getAssessmentItems",
"params": {
"targetType": 1,
"ownerUri": "my connection"
}
}
```
Sample output:
```json
{
"jsonrpc": "2.0",
"id": "12",
"result": {
"success": true,
"errorMessage": null,
"items": [
{
"rulesetVersion": "1.0.280",
"rulesetName": "Microsoft ruleset",
"targetType": 1,
"targetName": "MYSERVER",
"checkId": "TF174",
"tags": [
"DefaultRuleset",
"TraceFlag",
"Memory",
"Performance"
],
"displayName": "TF 174 increases the plan cache bucket count",
"description": "Trace Flag 174 increases the SQL Server ...",
"helpLink": "https://docs.microsoft.com/sql/t-sql/ ...",
"level": "Information"
},
...
]
}
}
```
### Invoke SQL Assessment with SQL Tools Service
Use the same `targetType`.
```json
Content-Length:169
{
"jsonrpc": "2.0",
"id": "12",
"method": "assessment/invoke",
"params": {
"targetType": 1,
"ownerUri": "my connection"
}
}
```
Sample output:
```json
{
"jsonrpc": "2.0",
"id": "12",
"result": {
"success": true,
"errorMessage": null,
"items": [
{
"message": "Enable trace flag 834 to use large-page allocations to improve analytical and data warehousing workloads",
"kind": 0,
"timestamp": "2020-11-09T22:46:36.5529014+03:00",
"rulesetVersion": "1.0.280",
"rulesetName": "Microsoft ruleset",
"targetType": 1,
"targetName": "MYSERVER",
"checkId": "TF834",
"tags": [
"DefaultRuleset",
"TraceFlag",
"Performance",
"Memory",
"ColumnStore"
],
"displayName": "TF 834 enables large-page allocations",
"description": "Trace Flag 834 causes the server ...",
"helpLink": "https://support.microsoft.com/kb/3210239",
"level": "Information"
},
...
]
}
}
```