diff --git a/src/PlanViewer.App/Controls/PlanViewerControl.axaml.cs b/src/PlanViewer.App/Controls/PlanViewerControl.axaml.cs
index 1b3dd71..fa0bf7f 100644
--- a/src/PlanViewer.App/Controls/PlanViewerControl.axaml.cs
+++ b/src/PlanViewer.App/Controls/PlanViewerControl.axaml.cs
@@ -1,7 +1,5 @@
using System;
using System.Collections.Generic;
-using System.Collections.ObjectModel;
-using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
diff --git a/src/PlanViewer.App/Dialogs/QueryStoreHistoryWindow.axaml.cs b/src/PlanViewer.App/Dialogs/QueryStoreHistoryWindow.axaml.cs
index b39825a..66f6706 100644
--- a/src/PlanViewer.App/Dialogs/QueryStoreHistoryWindow.axaml.cs
+++ b/src/PlanViewer.App/Dialogs/QueryStoreHistoryWindow.axaml.cs
@@ -7,7 +7,6 @@
using Avalonia.Controls.Primitives;
using Avalonia.Input;
using Avalonia.Interactivity;
-using Avalonia.Layout;
using Avalonia.Media;
using Avalonia.VisualTree;
using PlanViewer.Core.Models;
@@ -111,9 +110,9 @@ public QueryStoreHistoryWindow(string connectionString, string queryHash,
// Select initial metric in the combo box
var metricTag = initialMetricTag;
- foreach (ComboBoxItem item in MetricSelector.Items)
+ foreach (var entry in MetricSelector.Items)
{
- if (item.Tag?.ToString() == metricTag)
+ if (entry is ComboBoxItem item && item.Tag?.ToString() == metricTag)
{
MetricSelector.SelectedItem = item;
break;
@@ -759,7 +758,7 @@ private void HighlightGridRows()
private void OnHighlightLoadingRow(object? sender, DataGridRowEventArgs e)
{
- var idx = e.Row.GetIndex();
+ var idx = e.Row.Index;
if (_selectedRowIndices.Contains(idx))
{
e.Row.Background = new SolidColorBrush(Avalonia.Media.Color.FromArgb(60, 79, 195, 247));
diff --git a/src/PlanViewer.App/PlanViewer.App.csproj b/src/PlanViewer.App/PlanViewer.App.csproj
index f6091dd..a677f74 100644
--- a/src/PlanViewer.App/PlanViewer.App.csproj
+++ b/src/PlanViewer.App/PlanViewer.App.csproj
@@ -14,23 +14,23 @@
-
+
-
-
-
-
-
+
+
+
+
+
-
+
None
All
-
-
-
+
+
+
@@ -40,7 +40,7 @@
but the managed SkiaSharp 3.x requires native libs in the [119.0, 120.0) range.
Without these pins, the old 2.88.x .so overwrites the correct one at publish time,
causing a TypeInitializationException on Linux (GitHub issue #139). -->
-
+
diff --git a/src/PlanViewer.Cli/Commands/CredentialCommand.cs b/src/PlanViewer.Cli/Commands/CredentialCommand.cs
index e94e1f1..e49b9f1 100644
--- a/src/PlanViewer.Cli/Commands/CredentialCommand.cs
+++ b/src/PlanViewer.Cli/Commands/CredentialCommand.cs
@@ -77,12 +77,15 @@ private static Command CreateListCommand(ICredentialService credentialService)
cmd.SetHandler(() =>
{
- IReadOnlyList<(string ServerName, string Username)>? creds = credentialService switch
- {
- WindowsCredentialService win => win.ListAll(),
- KeychainCredentialService mac => mac.ListAll(),
- _ => null
- };
+ IReadOnlyList<(string ServerName, string Username)>? creds = null;
+ // CA1416: WindowsCredentialService is gated on OperatingSystem.IsWindows().
+ // .NET 8 won't run below Windows 10, so the underlying "windows5.1.2600" requirement is always met.
+#pragma warning disable CA1416
+ if (OperatingSystem.IsWindows() && credentialService is WindowsCredentialService win)
+ creds = win.ListAll();
+#pragma warning restore CA1416
+ if (OperatingSystem.IsMacOS() && credentialService is KeychainCredentialService mac)
+ creds = mac.ListAll();
if (creds == null)
{
diff --git a/src/PlanViewer.Core/PlanViewer.Core.csproj b/src/PlanViewer.Core/PlanViewer.Core.csproj
index b91d4dc..c9f1aeb 100644
--- a/src/PlanViewer.Core/PlanViewer.Core.csproj
+++ b/src/PlanViewer.Core/PlanViewer.Core.csproj
@@ -14,7 +14,7 @@
-
+
diff --git a/src/PlanViewer.Core/Services/CredentialServiceFactory.cs b/src/PlanViewer.Core/Services/CredentialServiceFactory.cs
index 648ff53..cc60138 100644
--- a/src/PlanViewer.Core/Services/CredentialServiceFactory.cs
+++ b/src/PlanViewer.Core/Services/CredentialServiceFactory.cs
@@ -1,4 +1,3 @@
-using System.Runtime.InteropServices;
using PlanViewer.Core.Interfaces;
namespace PlanViewer.Core.Services;
@@ -7,10 +6,14 @@ public static class CredentialServiceFactory
{
public static ICredentialService Create()
{
- if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ // CA1416: the underlying CredentialManager API declares "windows5.1.2600" (XP+);
+ // .NET 8 won't run on anything below Windows 10, so OperatingSystem.IsWindows() is sufficient.
+#pragma warning disable CA1416
+ if (OperatingSystem.IsWindows())
return new WindowsCredentialService();
+#pragma warning restore CA1416
- if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
+ if (OperatingSystem.IsMacOS())
return new KeychainCredentialService();
// Linux and other platforms: use in-memory storage (credentials not persisted across sessions)
diff --git a/src/PlanViewer.Core/Services/PlanAnalyzer.cs b/src/PlanViewer.Core/Services/PlanAnalyzer.cs
index aae2d0e..5aa8f2e 100644
--- a/src/PlanViewer.Core/Services/PlanAnalyzer.cs
+++ b/src/PlanViewer.Core/Services/PlanAnalyzer.cs
@@ -403,7 +403,7 @@ private static void AnalyzeStatement(PlanStatement stmt, AnalyzerConfig cfg)
if (unsnifffedParams.Count > 0)
{
- var hasRecompile = stmt.StatementText.Contains("RECOMPILE", StringComparison.OrdinalIgnoreCase);
+ var hasRecompile = stmt.StatementText?.Contains("RECOMPILE", StringComparison.OrdinalIgnoreCase) == true;
if (!hasRecompile)
{
var names = string.Join(", ", unsnifffedParams.Select(p => p.Name));
@@ -1321,7 +1321,7 @@ _ when nonSargableReason.StartsWith("Function call") =>
// Rule 28: Row Count Spool — NOT IN with nullable column
// Pattern: Row Count Spool with high rewinds, child scan has IS NULL predicate,
// and statement text contains NOT IN
- if (!cfg.IsRuleDisabled(28) && node.PhysicalOp.Contains("Row Count Spool"))
+ if (!cfg.IsRuleDisabled(28) && node.PhysicalOp?.Contains("Row Count Spool") == true)
{
var rewinds = node.HasActualStats ? (double)node.ActualRewinds : node.EstimateRewinds;
if (rewinds > 10000 && HasNotInPattern(node, stmt))
diff --git a/src/PlanViewer.Core/Services/WindowsCredentialService.cs b/src/PlanViewer.Core/Services/WindowsCredentialService.cs
index 99c96aa..645e567 100644
--- a/src/PlanViewer.Core/Services/WindowsCredentialService.cs
+++ b/src/PlanViewer.Core/Services/WindowsCredentialService.cs
@@ -1,8 +1,10 @@
+using System.Runtime.Versioning;
using Meziantou.Framework.Win32;
using PlanViewer.Core.Interfaces;
namespace PlanViewer.Core.Services;
+[SupportedOSPlatform("windows5.1.2600")]
public class WindowsCredentialService : ICredentialService
{
private const string Prefix = "planview:";
diff --git a/tests/PlanViewer.Core.Tests/PlanViewer.Core.Tests.csproj b/tests/PlanViewer.Core.Tests/PlanViewer.Core.Tests.csproj
index 3e468c0..5fd8768 100644
--- a/tests/PlanViewer.Core.Tests/PlanViewer.Core.Tests.csproj
+++ b/tests/PlanViewer.Core.Tests/PlanViewer.Core.Tests.csproj
@@ -10,10 +10,10 @@
-
-
-
-
+
+
+
+