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 @@ - - - - + + + +