EFCore.QueryAnalyzer
1.0.2
See the version list below for details.
dotnet add package EFCore.QueryAnalyzer --version 1.0.2
NuGet\Install-Package EFCore.QueryAnalyzer -Version 1.0.2
<PackageReference Include="EFCore.QueryAnalyzer" Version="1.0.2" />
<PackageVersion Include="EFCore.QueryAnalyzer" Version="1.0.2" />
<PackageReference Include="EFCore.QueryAnalyzer" />
paket add EFCore.QueryAnalyzer --version 1.0.2
#r "nuget: EFCore.QueryAnalyzer, 1.0.2"
#:package EFCore.QueryAnalyzer@1.0.2
#addin nuget:?package=EFCore.QueryAnalyzer&version=1.0.2
#tool nuget:?package=EFCore.QueryAnalyzer&version=1.0.2
EF Core Query Analyzer
A high-performance, production-ready NuGet package for monitoring Entity Framework Core query performance and automatically reporting slow queries to your analytics platform.
Features
- ๐ Zero-configuration startup with sensible defaults
- ๐ Real-time monitoring of all EF Core queries
- ๐ฏ Configurable thresholds for slow query detection
- ๐ Stack trace capture to identify problematic code
- ๐ HTTP API integration for centralized monitoring
- ๐๏ธ Multiple reporting strategies (HTTP, In-Memory, Custom)
- ๐ง Environment-aware configuration (Dev/Prod)
- โก Minimal performance overhead
- ๐งช Built-in testing support
Quick Start
1. Install the Package
dotnet add package EFCore.QueryAnalyzer
2. Configure in Program.cs
using EFCore.QueryAnalyzer.Extensions;
var builder = WebApplication.CreateBuilder(args);
// Add the query analyzer
builder.Services.AddEFCoreQueryAnalyzer(builder.Configuration);
// Configure your DbContext with the analyzer
builder.Services.AddDbContext<MyDbContext>((serviceProvider, options) =>
{
options.UseSqlServer(connectionString)
.AddQueryAnalyzer(serviceProvider);
});
var app = builder.Build();
3. Configure in appsettings.json
{
"QueryAnalyzer": {
"ThresholdMilliseconds": 1000,
"ApiEndpoint": "https://your-monitoring-platform.com/api/slow-queries",
"ApiKey": "your-secret-api-key",
"CaptureStackTrace": true,
"EnableInDevelopment": true,
"EnableInProduction": false
}
}
That's it! The analyzer will now automatically monitor your queries and report slow ones to your API.
Configuration Options
| Option | Type | Default | Description |
|---|---|---|---|
ThresholdMilliseconds |
double |
1000 |
Threshold in ms for considering a query as slow |
IsEnabled |
bool |
true |
Whether the analyzer is enabled |
CaptureStackTrace |
bool |
true |
Whether to capture stack traces for slow queries |
MaxStackTraceLines |
int |
10 |
Maximum lines in captured stack traces |
MaxQueryLength |
int |
10000 |
Maximum length of query text to store |
ApiEndpoint |
string? |
null |
HTTP endpoint for reporting slow queries |
ApiKey |
string? |
null |
API key for authentication |
ApiTimeoutMs |
int |
5000 |
Timeout for API calls in milliseconds |
EnableInDevelopment |
bool |
true |
Enable reporting in development environment |
EnableInProduction |
bool |
false |
Enable reporting in production environment |
Usage Scenarios
1. Basic Configuration
builder.Services.AddEFCoreQueryAnalyzer(options =>
{
options.ThresholdMilliseconds = 500;
options.ApiEndpoint = "https://your-api.com/slow-queries";
options.ApiKey = "your-api-key";
});
2. HTTP Reporting with Custom Client
builder.Services.AddEFCoreQueryAnalyzerWithHttp(
options =>
{
options.ThresholdMilliseconds = 750;
options.ApiEndpoint = "https://monitoring.company.com/api/queries";
options.ApiKey = builder.Configuration["MonitoringApiKey"];
},
httpClient =>
{
httpClient.Timeout = TimeSpan.FromSeconds(10);
httpClient.DefaultRequestHeaders.Add("X-App-Version", "1.0.0");
});
3. In-Memory Reporting (Testing)
builder.Services.AddEFCoreQueryAnalyzerWithInMemory(options =>
{
options.ThresholdMilliseconds = 100;
options.CaptureStackTrace = true;
});
4. Custom Reporting Service
public class MyCustomReportingService : IQueryReportingService
{
public async Task ReportSlowQueryAsync(QueryTrackingContext context, CancellationToken cancellationToken)
{
// Send to your preferred destination:
// - Database, File System, Message Queue, etc.
await SendToMyDestination(context);
}
}
// Register it
builder.Services.AddEFCoreQueryAnalyzerWithCustomReporting<MyCustomReportingService>(options =>
{
options.ThresholdMilliseconds = 500;
});
5. Inline Configuration (No DI)
var options = new DbContextOptionsBuilder<MyDbContext>()
.UseSqlServer(connectionString)
.AddQueryAnalyzer(analyzerOptions =>
{
analyzerOptions.ThresholdMilliseconds = 500;
analyzerOptions.ApiEndpoint = "https://your-api.com/slow-queries";
})
.Options;
using var context = new MyDbContext(options);
Report Format
When a slow query is detected, the following JSON is sent to your API endpoint:
{
"queryId": "a1b2c3d4",
"rawQuery": "SELECT u.Id, u.Email FROM Users u WHERE u.Email LIKE @p0",
"parameters": {
"@p0": "%test%"
},
"executionTimeMs": 1250.5,
"stackTrace": "at MyApp.Controllers.UserController.GetUsers() in UserController.cs:line 45\nat MyApp.Services.UserService.FindByEmail() in UserService.cs:line 23",
"timestamp": "2025-09-02T10:30:00Z",
"contextType": "MyApp.Data.ApplicationDbContext",
"environment": "Development",
"applicationName": "MyApplication",
"version": "1.0.0"
}
Environment Configuration
The package automatically detects the environment and applies the appropriate settings:
- Development: Reporting enabled by default, detailed stack traces
- Production: Reporting disabled by default for safety
- Testing: Use in-memory reporting for unit tests
Best Practices
1. Production Safety
builder.Services.AddEFCoreQueryAnalyzer(options =>
{
options.EnableInProduction = builder.Environment.IsProduction() &&
builder.Configuration.GetValue<bool>("EnableQueryAnalyzer");
options.ThresholdMilliseconds = builder.Environment.IsProduction() ? 2000 : 500;
});
2. Conditional Registration
if (builder.Configuration.GetValue<bool>("Features:QueryAnalyzer"))
{
builder.Services.AddEFCoreQueryAnalyzer(builder.Configuration);
}
3. Testing Setup
// In your test setup
services.AddEFCoreQueryAnalyzerWithInMemory();
// In your tests
var reportingService = serviceProvider.GetService<IQueryReportingService>() as InMemoryQueryReportingService;
var reports = reportingService?.GetReports();
Assert.True(reports.Any(r => r.ExecutionTimeMs > expectedThreshold));
Advanced Scenarios
Multiple Reporting Destinations
public class MultiDestinationReportingService : IQueryReportingService
{
private readonly ILogger _logger;
private readonly HttpClient _httpClient;
private readonly IMessageQueue _messageQueue;
public async Task ReportSlowQueryAsync(QueryTrackingContext context, CancellationToken cancellationToken)
{
// Send to multiple destinations in parallel
await Task.WhenAll(
SendToApi(context),
SendToQueue(context),
LogToFile(context)
);
}
}
Conditional Reporting
public class SmartReportingService : IQueryReportingService
{
public async Task ReportSlowQueryAsync(QueryTrackingContext context, CancellationToken cancellationToken)
{
// Only report queries from specific contexts
if (context.ContextType.Contains("CriticalDbContext"))
{
await _urgentReporter.ReportAsync(context);
}
// Different thresholds for different operations
if (context.CommandText.Contains("SELECT") && context.ExecutionTime.TotalSeconds > 5)
{
await _selectQueryReporter.ReportAsync(context);
}
}
}
Troubleshooting
Common Issues
- No reports being sent: Check
EnableInDevelopment/EnableInProductionsettings - API authentication errors: Verify
ApiKeyconfiguration - Missing stack traces: Ensure
CaptureStackTraceis enabled - Performance impact: Adjust
ThresholdMillisecondsor disable in high-load scenarios
Logging
Enable detailed logging to troubleshoot issues:
{
"Logging": {
"LogLevel": {
"EFCore.QueryAnalyzer": "Debug"
}
}
}
Requirements
- .NET 6.0 or higher
- Entity Framework Core 6.0 or higher
- ASP.NET Core (for dependency injection scenarios)
License
MIT License - see LICENSE file for details.
Contributing
Contributions are welcome! Please see CONTRIBUTING.md for guidelines.
Sample appsettings.json
{
"ConnectionStrings": {
"DefaultConnection": "Server=localhost;Database=MyApp;Trusted_Connection=true;"
},
"QueryAnalyzer": {
"ThresholdMilliseconds": 1000,
"ApiEndpoint": "https://monitoring.mycompany.com/api/slow-queries",
"ApiKey": "sk-1234567890abcdef",
"CaptureStackTrace": true,
"MaxStackTraceLines": 15,
"MaxQueryLength": 5000,
"ApiTimeoutMs": 10000,
"EnableInDevelopment": true,
"EnableInProduction": false
},
"Logging": {
"LogLevel": {
"Default": "Information",
"EFCore.QueryAnalyzer": "Information"
}
}
}
Sample appsettings.Production.json
{
"QueryAnalyzer": {
"ThresholdMilliseconds": 2000,
"CaptureStackTrace": false,
"EnableInProduction": true,
"ApiTimeoutMs": 5000
}
}
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net6.0 is compatible. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 is compatible. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net6.0
- Microsoft.EntityFrameworkCore (>= 6.0.0)
- Microsoft.EntityFrameworkCore.Relational (>= 6.0.0)
- Microsoft.Extensions.Configuration.Abstractions (>= 6.0.0)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 6.0.0)
- Microsoft.Extensions.Hosting (>= 6.0.0)
- Microsoft.Extensions.Http (>= 6.0.0)
- Microsoft.Extensions.Logging.Abstractions (>= 6.0.0)
- Microsoft.Extensions.Options (>= 6.0.0)
- System.Text.Json (>= 6.0.0)
-
net7.0
- Microsoft.EntityFrameworkCore (>= 6.0.0)
- Microsoft.EntityFrameworkCore.Relational (>= 6.0.0)
- Microsoft.Extensions.Configuration.Abstractions (>= 6.0.0)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 6.0.0)
- Microsoft.Extensions.Hosting (>= 6.0.0)
- Microsoft.Extensions.Http (>= 6.0.0)
- Microsoft.Extensions.Logging.Abstractions (>= 6.0.0)
- Microsoft.Extensions.Options (>= 6.0.0)
- System.Text.Json (>= 6.0.0)
-
net8.0
- Microsoft.EntityFrameworkCore (>= 6.0.0)
- Microsoft.EntityFrameworkCore.Relational (>= 6.0.0)
- Microsoft.Extensions.Configuration.Abstractions (>= 6.0.0)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 6.0.0)
- Microsoft.Extensions.Hosting (>= 6.0.0)
- Microsoft.Extensions.Http (>= 6.0.0)
- Microsoft.Extensions.Logging.Abstractions (>= 6.0.0)
- Microsoft.Extensions.Options (>= 6.0.0)
- System.Text.Json (>= 6.0.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.0.3-beta-04 | 285 | 12/12/2025 |
| 1.0.3-beta-03 | 491 | 10/21/2025 |
| 1.0.3-beta-02 | 179 | 10/17/2025 |
| 1.0.3-beta-01 | 5,099 | 9/29/2025 |
| 1.0.3-beta | 603 | 9/10/2025 |
| 1.0.2 | 219 | 9/2/2025 |