Skip to content

Commit 65eaadd

Browse files
committed
Code cleanup
1 parent c3d095a commit 65eaadd

File tree

145 files changed

+1734
-1796
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

145 files changed

+1734
-1796
lines changed

.editorconfig

Lines changed: 43 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,20 @@
1-
# Top level EditorConfig file
1+
# Top-level EditorConfig
22
# For more information, see https://aka.ms/editorconfigdocs
33

44
root = true
55

6+
# =====================================================================================
7+
# Global language settings
8+
# =====================================================================================
9+
610
[*.cs]
11+
# Encoding & formatting
712
charset = utf-8
813
indent_style = space
914
indent_size = 4
1015
insert_final_newline = true
1116

17+
# Code style
1218
# Organize usings
1319
dotnet_sort_system_directives_first = true
1420

@@ -21,56 +27,64 @@ dotnet_style_qualification_for_event = false:suggestion
2127
# New line options
2228
csharp_new_line_before_open_brace = all:suggestion
2329

30+
# Analyzer: ignore IDE0130 (Namespace does not match folder structure)
31+
dotnet_diagnostic.IDE0130.severity = none
32+
33+
[*.{cs,vb}]
34+
dotnet_code_quality.CA1826.exclude_ordefault_methods = true
35+
2436
[*.{csproj,props,targets}]
2537
indent_style = space
2638
indent_size = 2
2739

28-
# ---------------------------
29-
# Analyzer severities
40+
# (we continue to rely on: EnableNETAnalyzers=true, AnalysisLevel=8.0-recommended)
41+
42+
# =====================================================================================
43+
# Analyzer severities (global)
44+
# =====================================================================================
45+
3046
# Public API analyzers
3147
dotnet_diagnostic.RS0016.severity = error # Add new APIs to Unshipped
3248
dotnet_diagnostic.RS0017.severity = error # Mark APIs as obsolete/removed
3349
dotnet_diagnostic.RS0025.severity = error # Duplicate member in the public API file
3450
dotnet_diagnostic.RS0037.severity = error # The generated public API file is not up to date
3551

36-
[*.{cs,vb}]
37-
dotnet_code_quality.CA1826.exclude_ordefault_methods = true
52+
# =====================================================================================
53+
# Per-project severity settings
54+
# =====================================================================================
3855

39-
# (we continue to rely on: EnableNETAnalyzers=true, AnalysisLevel=8.0-recommended)
40-
41-
# ---------------------------
42-
# Per-project severity settings:
43-
44-
# test projects under /tests
56+
# tests/*
4557
[tests/**.cs]
46-
dotnet_diagnostic.CA1707.severity = none # disable “no underscores in identifiers for test names
47-
dotnet_diagnostic.CS1591.severity = none # disable missing XML documentation” for test code
58+
dotnet_diagnostic.CA1707.severity = none # allow underscores in identifiers (test names)
59+
dotnet_diagnostic.CS1591.severity = none # missing XML docs off in tests
4860
dotnet_diagnostic.IDE0290.severity = none # disable “Use primary constructor”
4961

50-
# sample apps under /samples
62+
# samples/*
5163
[samples/**.cs]
52-
dotnet_diagnostic.CS1591.severity = none # disable missing XML documentation” for sample code
64+
dotnet_diagnostic.CS1591.severity = none # missing XML docs off in samples
5365

54-
# tools/KeelMatrix.QueryWatch.Cli
66+
# tools/KeelMatrix.QueryWatch.Cli/*
5567
[tools/KeelMatrix.QueryWatch.Cli/**.cs]
56-
dotnet_diagnostic.RCS1194.severity = none # disable Roslynator rule RCS1194 (style/constructor guideline)
57-
dotnet_diagnostic.S3871.severity = none # disable Sonar rule S3871 (exception/immutability guideline)
58-
dotnet_diagnostic.CS1591.severity = none # disable missing XML documentation” for CLI code
68+
dotnet_diagnostic.RCS1194.severity = none # Roslynator: constructor guideline
69+
dotnet_diagnostic.S3871.severity = none # Sonar: exception/immutability guideline
70+
dotnet_diagnostic.CS1591.severity = none # missing XML docs off in CLI
5971

60-
# Core library — src/KeelMatrix.QueryWatch
72+
# src/KeelMatrix.QueryWatch/*
6173
[src/KeelMatrix.QueryWatch/**.cs]
62-
dotnet_diagnostic.CA1510.severity = none # disable prefer ArgumentNullException.ThrowIfNull requirement
63-
dotnet_diagnostic.CA1513.severity = none # disable prefer nameof(...) in exceptions requirement
64-
dotnet_diagnostic.CA2249.severity = none # disable “Use 'string.Contains' instead of 'string.IndexOf' to improve readability”
65-
dotnet_diagnostic.IDE0290.severity = none # disable “Use primary constructor”
66-
dotnet_diagnostic.RCS1001.severity = none # disable “Add braces to if statement”
74+
dotnet_diagnostic.CA1510.severity = none # prefer ThrowIfNull
75+
dotnet_diagnostic.CA1513.severity = none # prefer nameof(...) in exceptions
76+
dotnet_diagnostic.CA2249.severity = none # prefer string.Contains over IndexOf
77+
dotnet_diagnostic.IDE0290.severity = none # “Use primary constructor”
78+
dotnet_diagnostic.RCS1001.severity = none # “Add braces to if statement”
6779

68-
# Project: Contracts — src/KeelMatrix.QueryWatch.Contracts
80+
# src/KeelMatrix.QueryWatch.Contracts/*
6981
[src/KeelMatrix.QueryWatch.Contracts/**.cs]
70-
dotnet_diagnostic.CS1591.severity = none # disable missing XML documentation” for public contracts
82+
dotnet_diagnostic.CS1591.severity = none # missing XML docs off for public contracts
7183

72-
# ---------------------------
73-
# Debug-Release specific severity settings:
84+
# =====================================================================================
85+
# Configuration-specific severity settings
86+
# (Order matters: these come last to override above where applicable)
87+
# =====================================================================================
7488

7589
# In Debug: suppress CS1591 across the board (keep the build clean)
7690
[*.cs]
Lines changed: 20 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
// Microbenchmarks for write-path contention in QueryWatchSession.
22
// Compares production "lock + copy-on-stop" vs an alternative "ConcurrentQueue + snapshot".
3-
#nullable enable
43
using System.Collections.Concurrent;
54
using BenchmarkDotNet.Attributes;
65

@@ -19,31 +18,31 @@ public class RecordThroughputBench {
1918
[Params(2_000)]
2019
public int EventsPerThread { get; set; } = 2_000;
2120

22-
private KeelMatrix.QueryWatch.QueryWatchOptions _optsNoText = null!;
21+
private QueryWatchOptions _optsNoText = null!;
2322

2423
[GlobalSetup]
2524
public void Setup() {
26-
_optsNoText = new KeelMatrix.QueryWatch.QueryWatchOptions {
25+
_optsNoText = new QueryWatchOptions {
2726
CaptureSqlText = false // exercise the hot path without redaction overhead
2827
};
2928
}
3029

3130
[Benchmark(Baseline = true, Description = "lock[List] + copy-on-stop (production)")]
32-
public KeelMatrix.QueryWatch.QueryWatchReport LockList() {
33-
var session = new KeelMatrix.QueryWatch.QueryWatchSession(_optsNoText);
31+
public QueryWatchReport LockList() {
32+
QueryWatchSession session = new(_optsNoText);
3433
RunWorkers(Threads, EventsPerThread, () => session.Record("SELECT 1", TimeSpan.FromMilliseconds(1)));
3534
return session.Stop();
3635
}
3736

3837
[Benchmark(Description = "ConcurrentQueue + snapshot (alternative)")]
39-
public KeelMatrix.QueryWatch.QueryWatchReport ConcurrentQueueSnapshot() {
40-
var session = new ConcurrentQueueSession(_optsNoText);
38+
public QueryWatchReport ConcurrentQueueSnapshot() {
39+
ConcurrentQueueSession session = new(_optsNoText);
4140
RunWorkers(Threads, EventsPerThread, () => session.Record("SELECT 1", TimeSpan.FromMilliseconds(1)));
4241
return session.Stop();
4342
}
4443

4544
private static void RunWorkers(int threads, int eventsPerThread, Action action) {
46-
var tasks = new Task[threads];
45+
Task[] tasks = new Task[threads];
4746
for (int t = 0; t < threads; t++) {
4847
tasks[t] = Task.Run(() => {
4948
for (int i = 0; i < eventsPerThread; i++) action();
@@ -54,16 +53,11 @@ private static void RunWorkers(int threads, int eventsPerThread, Action action)
5453
}
5554

5655
// Minimal alternative session used only for benchmarking.
57-
internal sealed class ConcurrentQueueSession {
58-
private readonly ConcurrentQueue<KeelMatrix.QueryWatch.QueryEvent> _q = new();
59-
private readonly KeelMatrix.QueryWatch.QueryWatchOptions _options;
56+
internal sealed class ConcurrentQueueSession(QueryWatchOptions options) {
57+
private readonly ConcurrentQueue<QueryEvent> _q = new();
58+
private readonly QueryWatchOptions _options = options ?? throw new ArgumentNullException(nameof(options));
6059
private int _stopped; // 0=running,1=stopped
6160
private readonly DateTimeOffset _startedAt = DateTimeOffset.UtcNow;
62-
private DateTimeOffset? _stoppedAt;
63-
64-
public ConcurrentQueueSession(KeelMatrix.QueryWatch.QueryWatchOptions options) {
65-
_options = options ?? throw new ArgumentNullException(nameof(options));
66-
}
6761

6862
public void Record(string commandText, TimeSpan duration) {
6963
if (Volatile.Read(ref _stopped) != 0)
@@ -73,30 +67,27 @@ public void Record(string commandText, TimeSpan duration) {
7367
string text = string.Empty;
7468
if (_options.CaptureSqlText) {
7569
text = commandText ?? string.Empty;
76-
foreach (var r in _options.Redactors) text = r.Redact(text);
70+
foreach (IQueryTextRedactor r in _options.Redactors) text = r.Redact(text);
7771
}
7872

7973
// second check to minimize recording after stop (not strictly needed for the benchmark)
8074
if (Volatile.Read(ref _stopped) != 0)
8175
throw new InvalidOperationException("Session has been stopped; cannot record new events.");
8276

8377
// FIX: use the public 3-arg ctor; the 4-arg (with meta) is internal
84-
var ev = new KeelMatrix.QueryWatch.QueryEvent(text, duration, DateTimeOffset.UtcNow);
78+
QueryEvent ev = new(text, duration, DateTimeOffset.UtcNow);
8579
_q.Enqueue(ev);
8680
}
8781

88-
public KeelMatrix.QueryWatch.QueryWatchReport Stop() {
89-
var now = DateTimeOffset.UtcNow;
90-
if (Interlocked.CompareExchange(ref _stopped, 1, 0) == 0) {
91-
_stoppedAt = now;
92-
}
93-
else {
94-
throw new InvalidOperationException("Session has already been stopped.");
95-
}
82+
public QueryWatchReport Stop() {
83+
DateTimeOffset now = DateTimeOffset.UtcNow;
84+
DateTimeOffset? _stoppedAt = Interlocked.CompareExchange(ref _stopped, 1, 0) == 0
85+
? (DateTimeOffset?)now
86+
: throw new InvalidOperationException("Session has already been stopped.");
9687

97-
var arr = _q.ToArray();
98-
var list = new List<KeelMatrix.QueryWatch.QueryEvent>(arr);
99-
return KeelMatrix.QueryWatch.QueryWatchReport.CreateSnapshot(list, _options, _startedAt, _stoppedAt ?? now);
88+
QueryEvent[] arr = [.. _q];
89+
List<QueryEvent> list = [.. arr];
90+
return QueryWatchReport.CreateSnapshot(list, _options, _startedAt, _stoppedAt ?? now);
10091
}
10192
}
10293
}

bench/KeelMatrix.QueryWatch.Redaction.Benchmarks/CiAwareConfig.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,13 @@
44
namespace KeelMatrix.QueryWatch.Redaction.Benchmarks {
55
internal sealed class CiAwareConfig : ManualConfig {
66
public CiAwareConfig() {
7-
AddLogger([.. DefaultConfig.Instance.GetLoggers()]);
8-
AddColumnProvider([.. DefaultConfig.Instance.GetColumnProviders()]);
9-
AddDiagnoser([.. DefaultConfig.Instance.GetDiagnosers()]);
7+
_ = AddLogger([.. DefaultConfig.Instance.GetLoggers()]);
8+
_ = AddColumnProvider([.. DefaultConfig.Instance.GetColumnProviders()]);
9+
_ = AddDiagnoser([.. DefaultConfig.Instance.GetDiagnosers()]);
1010

1111
bool isCi = Environment.GetEnvironmentVariable("CI") is not null;
1212
if (!isCi) {
13-
AddExporter(JsonExporter.FullCompressed);
13+
_ = AddExporter(JsonExporter.FullCompressed);
1414
}
1515
}
1616
}

samples/Ado.Sqlite/Program.cs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,47 +4,47 @@
44
using Microsoft.Data.Sqlite;
55

66
// Plain ADO.NET + SQLite sample
7-
var artifacts = Path.Combine(AppContext.BaseDirectory, "artifacts");
7+
string artifacts = Path.Combine(AppContext.BaseDirectory, "artifacts");
88
Directory.CreateDirectory(artifacts);
9-
var outJson = Path.Combine(artifacts, "qwatch.ado.json");
9+
string outJson = Path.Combine(artifacts, "qwatch.ado.json");
1010

11-
using var q = QueryWatchScope.Start(
11+
using QueryWatchScope q = QueryWatchScope.Start(
1212
maxQueries: 50,
1313
maxAverage: TimeSpan.FromMilliseconds(200),
1414
exportJsonPath: outJson,
1515
sampleTop: 50);
1616

1717
// In-memory SQLite needs the connection to stay open for the DB to persist.
18-
using var raw = new SqliteConnection("Data Source=:memory:");
18+
using SqliteConnection raw = new("Data Source=:memory:");
1919
await raw.OpenAsync();
2020

2121
// Wrap the provider connection so all commands record into the QueryWatch session.
22-
using var conn = new QueryWatchConnection(raw, q.Session);
22+
using QueryWatchConnection conn = new(raw, q.Session);
2323

2424
// Create schema (we include a harmless SQL comment with an email to show masking)
2525
using (var cmd = conn.CreateCommand()) {
2626
cmd.CommandText = Redaction.Apply("/* contact: [email protected] */ CREATE TABLE Users(Id INTEGER PRIMARY KEY, Name TEXT NOT NULL);");
27-
await cmd.ExecuteNonQueryAsync();
27+
_ = await cmd.ExecuteNonQueryAsync();
2828
}
2929

3030
// Insert a few rows (we also redact parameter strings defensively)
3131
for (int i = 0; i < 5; i++) {
3232
using var ins = conn.CreateCommand();
33-
var email = $"user{i}@example.com"; // demo PII-like value (will be masked in CommandText)
33+
string email = $"user{i}@example.com"; // demo PII-like value (will be masked in CommandText)
3434
ins.CommandText = Redaction.Apply($"/* email: {email} */ INSERT INTO Users(Name) VALUES ($name);");
3535

3636
var p = ins.CreateParameter();
3737
p.ParameterName = "$name";
3838
p.Value = Redaction.Param("User_" + i); // if your JSON ever includes parameters, this stays safe
39-
ins.Parameters.Add(p);
39+
_ = ins.Parameters.Add(p);
4040

41-
await ins.ExecuteNonQueryAsync();
41+
_ = await ins.ExecuteNonQueryAsync();
4242
}
4343

4444
// Query back
4545
using (var select = conn.CreateCommand()) {
4646
select.CommandText = Redaction.Apply("SELECT COUNT(*) FROM Users WHERE Name LIKE 'User_%';");
47-
var count = Convert.ToInt32(await select.ExecuteScalarAsync(), System.Globalization.CultureInfo.InvariantCulture);
47+
int count = Convert.ToInt32(await select.ExecuteScalarAsync(), System.Globalization.CultureInfo.InvariantCulture);
4848
Console.WriteLine($"Users in DB: {count}");
4949
}
5050

samples/Ado.Sqlite/Redaction.cs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,20 @@ namespace Ado.Sqlite {
44
/// <summary>
55
/// Simple redaction helper for the sample (emails + long hex tokens)
66
/// </summary>
7-
internal static class Redaction {
7+
internal static partial class Redaction {
88
// Very small demo rules; expand as needed
9-
private static readonly Regex Email = new(@"[A-Za-z0-9._%+\-]+@[A-Za-z0-9.\-]+\.[A-Za-z]{2,}", RegexOptions.Compiled);
10-
private static readonly Regex HexToken = new(@"\b[0-9a-f]{32,}\b", RegexOptions.IgnoreCase | RegexOptions.Compiled);
9+
private static readonly Regex Email = EmailRegex();
10+
private static readonly Regex HexToken = HexTokenRegex();
1111

1212
public static string Apply(string input) =>
1313
HexToken.Replace(Email.Replace(input, "***"), "***");
1414

1515
public static object Param(object? v) => v is string s ? Apply(s) : v ?? DBNull.Value;
16+
17+
[GeneratedRegex(@"[A-Za-z0-9._%+\-]+@[A-Za-z0-9.\-]+\.[A-Za-z]{2,}", RegexOptions.Compiled)]
18+
private static partial Regex EmailRegex();
19+
20+
[GeneratedRegex(@"\b[0-9a-f]{32,}\b", RegexOptions.IgnoreCase | RegexOptions.Compiled, "en-US")]
21+
private static partial Regex HexTokenRegex();
1622
}
1723
}

samples/Dapper.Sqlite/Program.cs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,18 @@
55
using Microsoft.Data.Sqlite;
66

77
// Dapper + SQLite sample (async + transactions)
8-
var artifacts = Path.Combine(AppContext.BaseDirectory, "artifacts");
8+
string artifacts = Path.Combine(AppContext.BaseDirectory, "artifacts");
99
Directory.CreateDirectory(artifacts);
10-
var outJson = Path.Combine(artifacts, "qwatch.dapper.json");
10+
string outJson = Path.Combine(artifacts, "qwatch.dapper.json");
1111

12-
using var q = QueryWatchScope.Start(
12+
using QueryWatchScope q = QueryWatchScope.Start(
1313
maxQueries: 50,
1414
maxAverage: TimeSpan.FromMilliseconds(200),
1515
exportJsonPath: outJson,
1616
sampleTop: 50);
1717

1818
// Create an in-memory DB
19-
using var raw = new SqliteConnection("Data Source=:memory:");
19+
using SqliteConnection raw = new("Data Source=:memory:");
2020
await raw.OpenAsync();
2121

2222
// Wrap with QueryWatch (returns QueryWatchConnection under the hood for SQLite)
@@ -28,8 +28,8 @@
2828
// Insert in a transaction (exercise Transaction wrapper + async APIs)
2929
using (var tx = await conn.BeginTransactionAsync()) {
3030
for (int i = 0; i < 3; i++) {
31-
var email = $"user{i}@example.com"; // will be redacted in CommandText
32-
await conn.ExecuteAsync(
31+
string email = $"user{i}@example.com"; // will be redacted in CommandText
32+
_ = await conn.ExecuteAsync(
3333
Redaction.Apply($"/* email: {email} */ INSERT INTO Users(Name) VALUES (@name);"),
3434
new { name = Redaction.Param("User_" + i) },
3535
transaction: tx);
@@ -38,7 +38,7 @@ await conn.ExecuteAsync(
3838
}
3939

4040
// Query back (async)
41-
var total = await conn.ExecuteScalarAsync<int>(Redaction.Apply("SELECT COUNT(*) FROM Users WHERE Name LIKE 'User_%';"));
41+
int total = await conn.ExecuteScalarAsync<int>(Redaction.Apply("SELECT COUNT(*) FROM Users WHERE Name LIKE 'User_%';"));
4242
Console.WriteLine($"Users in DB: {total}");
4343

4444
Console.WriteLine($"QueryWatch JSON written to: {outJson}");

samples/Dapper.Sqlite/Redaction.cs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,20 @@ namespace DapperSample {
44
/// <summary>
55
/// Simple redaction helper for the sample (emails + long hex tokens)
66
/// </summary>
7-
internal static class Redaction {
7+
internal static partial class Redaction {
88
// Very small demo rules; expand as needed
9-
private static readonly Regex Email = new(@"[A-Za-z0-9._%+\-]+@[A-Za-z0-9.\-]+\.[A-Za-z]{2,}", RegexOptions.Compiled);
10-
private static readonly Regex HexToken = new(@"\b[0-9a-f]{32,}\b", RegexOptions.IgnoreCase | RegexOptions.Compiled);
9+
private static readonly Regex Email = EmailRegex();
10+
private static readonly Regex HexToken = HexTokenRegex();
1111

1212
public static string Apply(string input) =>
1313
HexToken.Replace(Email.Replace(input, "***"), "***");
1414

1515
public static object Param(object? v) => v is string s ? Apply(s) : v ?? DBNull.Value;
16+
17+
[GeneratedRegex(@"[A-Za-z0-9._%+\-]+@[A-Za-z0-9.\-]+\.[A-Za-z]{2,}", RegexOptions.Compiled)]
18+
private static partial Regex EmailRegex();
19+
20+
[GeneratedRegex(@"\b[0-9a-f]{32,}\b", RegexOptions.IgnoreCase | RegexOptions.Compiled, "en-US")]
21+
private static partial Regex HexTokenRegex();
1622
}
1723
}

samples/EFCore.Sqlite/AppDbContext.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ public sealed class AppDbContext : DbContext {
55
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }
66
public DbSet<User> Users => Set<User>();
77
protected override void OnModelCreating(ModelBuilder modelBuilder) {
8-
modelBuilder.Entity<User>().HasKey(u => u.Id);
9-
modelBuilder.Entity<User>().Property(u => u.Name).IsRequired();
8+
_ = modelBuilder.Entity<User>().HasKey(u => u.Id);
9+
_ = modelBuilder.Entity<User>().Property(u => u.Name).IsRequired();
1010
base.OnModelCreating(modelBuilder);
1111
}
1212
}

0 commit comments

Comments
 (0)