25 KiB
Strata.SqlTools.LinqToSql
LINQ to SQL Query Analysis and Visualization
Overview
The Strata.SqlTools.LinqToSql package provides comprehensive support for analyzing LINQ to SQL queries by examining their expression trees. It extracts query components (SELECT, WHERE, ORDER BY, etc.) and provides visualization tools for understanding query structure and execution flow.
This package is particularly useful for:
- Query Analysis: Understanding how LINQ queries translate to SQL
- Performance Optimization: Identifying inefficient query patterns
- Documentation: Generating visual diagrams of query structure
- Debugging: Tracing LINQ method chains and their SQL equivalents
Key Features
- ✅ Expression Tree Analysis - Parse IQueryable expression trees to extract SQL components
- ✅ LINQ Method Chain Tracking - Track Where, Select, OrderBy, GroupBy method calls
- ✅ Statement Type Analysis - Analyze INSERT, UPDATE, DELETE, PROCEDURE, and TRACE operations
- ✅ Mermaid Diagram Generation - Visualize queries with flowcharts and sequence diagrams
- ✅ SQL Component Extraction - Extract SELECT, WHERE, ORDER BY, GROUP BY clauses
- ✅ Integration with SqlServer - Built on top of SqlServer.QueryBreakdown
- ✅ Type-Safe Analysis - Strongly-typed entity detection
Installation
dotnet add package Strata.SqlTools.LinqToSql
Dependencies:
Strata.SqlTools(core functionality)Strata.SqlTools.SqlServer(base query breakdown)- .NET 8.0+
Quick Start
Basic Query Analysis
using Strata.SqlTools.Breakdowns.LinqToSql;
// Define your data context with IQueryable properties
public class DataContext
{
public IQueryable<User> Users => new List<User>().AsQueryable();
}
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
public bool IsActive { get; set; }
}
// Analyze a LINQ query
var context = new DataContext();
var query = context.Users.Where(u => u.Age > 21).OrderBy(u => u.Name);
var breakdown = LinqQueryBreakdown.Analyze(query);
// Access extracted components
Console.WriteLine($"Entity Type: {breakdown.EntityType}");
Console.WriteLine($"SELECT: {breakdown.SelectClause}");
Console.WriteLine($"FROM: {breakdown.FromClause}");
Console.WriteLine($"WHERE: {breakdown.WhereClause}");
Console.WriteLine($"ORDER BY: {breakdown.OrderByClause}");
// Get method chain
var methodChain = breakdown.GetMethodChain();
Console.WriteLine($"Method Chain: {string.Join(" -> ", methodChain)}");
Output:
Entity Type: User
SELECT: *
FROM: Users
WHERE: (Age > 21)
ORDER BY: Name ASC
Method Chain: Where -> OrderBy
Core Components
LinqQueryBreakdown Class
The main class for analyzing LINQ queries.
Static Analysis Methods
// SELECT Query Analysis
public static LinqQueryBreakdown Analyze<T>(IQueryable<T> query)
public static bool TryAnalyze<T>(IQueryable<T> query, out LinqQueryBreakdown? breakdown)
// INSERT Operations
public static InsertBreakdown AnalyzeInsert<T>(T entity) where T : class
public static InsertBreakdown AnalyzeInsertRange<T>(IEnumerable<T> entities) where T : class
// DELETE Operations
public static DeleteBreakdown AnalyzeDelete<T>(Expression<Func<T, bool>> filterExpression) where T : class
// UPDATE Operations
public static UpdateBreakdown AnalyzeUpdate<T>(
Expression<Func<T, bool>> filterExpression,
Expression<Func<T, T>> updateExpression) where T : class
// PROCEDURE Operations
public static ProcedureBreakdown AnalyzeProcedure(string procedureName, params object[] parameters)
// TRACE Operations
public static string AnalyzeTrace<T>(IQueryable<T> query, string? executionContext = null) where T : class
Properties
public Expression? OriginalExpression { get; } // Original LINQ expression tree
public string EntityType { get; } // Entity type name (e.g., "User")
public List<string> MethodCallChain { get; } // List of LINQ method calls
Methods
public string GetQuerySummary() // Human-readable query summary
public List<string> GetMethodChain() // LINQ method call sequence
LinqExpressionVisitor
The expression visitor that traverses LINQ expression trees to extract SQL components.
Supported LINQ Methods
| LINQ Method | SQL Clause | Example |
|---|---|---|
Where() |
WHERE | users.Where(u => u.Age > 21) |
Select() |
SELECT | users.Select(u => new { u.Id, u.Name }) |
OrderBy() |
ORDER BY | users.OrderBy(u => u.Name) |
OrderByDescending() |
ORDER BY DESC | users.OrderByDescending(u => u.Age) |
GroupBy() |
GROUP BY | users.GroupBy(u => u.Department) |
ThenBy() |
ORDER BY (multiple) | users.OrderBy(u => u.Name).ThenBy(u => u.Age) |
Expression Types Handled
- Binary Expressions:
>,<,>=,<=,==,!=,&&,|| - Member Access: Property/field access (e.g.,
u.Age) - Constants: Literal values
- Method Calls: LINQ extension methods
Markdown Visualization
The Strata.SqlTools.Markdown package includes specialized generators for LinqToSql.
Installation
dotnet add package Strata.SqlTools.Markdown
QueryBreakdownGenerator
Generates Mermaid diagrams showing query structure and LINQ method chains.
using Strata.SqlTools.Markdown.LinqToSql;
var generator = new QueryBreakdownGenerator();
var query = context.Users
.Where(u => u.Age > 21)
.OrderBy(u => u.Name)
.Select(u => new { u.Id, u.Name });
var breakdown = LinqQueryBreakdown.Analyze(query);
// Generate SQL structure diagram
string sqlDiagram = generator.GenerateMermaidDiagram(breakdown, "User Query");
// Generate LINQ method chain diagram
string methodDiagram = generator.GenerateMethodChainDiagram(breakdown, "Method Flow");
// Generate combined diagram (both SQL structure and method chain)
string combined = generator.GenerateCombinedDiagram(breakdown, "Complete Analysis");
Example Method Chain Diagram:
flowchart LR
Start[IQueryable] --> Where[Where]
Where --> OrderBy[OrderBy]
OrderBy --> Select[Select]
Select --> Result[Result]
SqlStatementGenerator
Generates sequence diagrams showing LINQ execution pipeline.
var stmtGenerator = new SqlStatementGenerator();
// Generate LINQ execution pipeline diagram
string pipeline = stmtGenerator.GenerateLinqPipelineDiagram(breakdown, "Query Execution");
// Generate sequence diagram
string sequence = stmtGenerator.GenerateSequenceDiagram(breakdown, "Execution Flow");
// Generate ER diagram
string erDiagram = stmtGenerator.GenerateEntityRelationshipDiagram(breakdown, "Entity Model");
Example LINQ Pipeline Diagram:
sequenceDiagram
participant Client as Client Application
participant LINQ as LINQ Provider
participant ET as Expression Tree
participant SQL as SQL Generator
participant DB as Database
Client->>LINQ: LINQ Query
activate LINQ
LINQ->>ET: Where Predicate
activate ET
LINQ->>ET: Select Projection
ET->>SQL: Expression Tree
deactivate ET
SQL->>DB: Generate SQL
activate DB
DB-->>SQL: Result Set
deactivate DB
SQL-->>LINQ: Mapped Objects
LINQ-->>Client: IEnumerable Result
deactivate LINQ
Advanced Usage
Complex Query Analysis
// Multi-clause query
var complexQuery = context.Orders
.Where(o => o.Amount > 1000)
.Where(o => o.Status == "Pending")
.OrderBy(o => o.OrderDate)
.ThenByDescending(o => o.Amount)
.Select(o => new
{
o.Id,
o.CustomerName,
o.Amount
});
var breakdown = LinqQueryBreakdown.Analyze(complexQuery);
Console.WriteLine(breakdown.GetQuerySummary());
// Output: "SELECT projection FROM Orders WHERE (Amount > 1000) AND (Status = 'Pending') ORDER BY OrderDate ASC, Amount DESC"
var methods = breakdown.GetMethodChain();
// Output: ["Where", "Where", "OrderBy", "ThenByDescending", "Select"]
Safe Analysis with TryAnalyze
if (LinqQueryBreakdown.TryAnalyze(query, out var breakdown))
{
Console.WriteLine($"Successfully analyzed: {breakdown.GetQuerySummary()}");
// Access components safely
if (!string.IsNullOrEmpty(breakdown.WhereClause))
{
Console.WriteLine($"WHERE clause: {breakdown.WhereClause}");
}
}
else
{
Console.WriteLine("Unable to analyze query");
}
Accessing Inherited SqlServer Properties
LinqQueryBreakdown inherits from SqlServer.QueryBreakdown, providing access to all standard query breakdown features:
var breakdown = LinqQueryBreakdown.Analyze(query);
// Standard QueryBreakdown properties
Console.WriteLine($"SELECT: {breakdown.SelectClause}");
Console.WriteLine($"FROM: {breakdown.FromClause}");
Console.WriteLine($"WHERE: {breakdown.WhereClause}");
Console.WriteLine($"GROUP BY: {breakdown.GroupByClause}");
Console.WriteLine($"HAVING: {breakdown.HavingClause}");
Console.WriteLine($"ORDER BY: {breakdown.OrderByClause}");
// Generate SQL
string sql = breakdown.GetSql();
// Clone breakdown
var clone = (LinqQueryBreakdown)breakdown.Clone();
Statement Type Analysis
Beyond SELECT queries, LinqQueryBreakdown provides comprehensive analysis for other statement types.
INSERT Analysis
// Single entity insert
var user = new User { Id = 1, Name = "John Doe", Email = "john@example.com", Age = 30 };
var insertBreakdown = LinqQueryBreakdown.AnalyzeInsert(user);
Console.WriteLine($"Table: {insertBreakdown.TableName}"); // User
Console.WriteLine($"Columns: {insertBreakdown.InsertIntoClause}");
Console.WriteLine($"Values: {insertBreakdown.ValuesClause}");
// Bulk insert
var users = new List<User>
{
new User { Id = 1, Name = "John Doe", Email = "john@example.com", Age = 30 },
new User { Id = 2, Name = "Jane Smith", Email = "jane@example.com", Age = 28 }
};
var bulkInsertBreakdown = LinqQueryBreakdown.AnalyzeInsertRange(users);
Console.WriteLine($"Inserting {bulkInsertBreakdown.ValuesClause.Count(c => c == '(')} rows");
DELETE Analysis
// Analyze deletion with filter expression
var deleteBreakdown = LinqQueryBreakdown.AnalyzeDelete<User>(u => u.Age < 18);
Console.WriteLine($"Table: {deleteBreakdown.FromClause}"); // User
Console.WriteLine($"WHERE: {deleteBreakdown.WhereClause}"); // (Age < 18)
// Complex filter
var complexDelete = LinqQueryBreakdown.AnalyzeDelete<Order>(o => o.Status == "Cancelled" && o.OrderDate < DateTime.Now.AddYears(-1));
Console.WriteLine($"Deleting old cancelled orders: {complexDelete.WhereClause}");
UPDATE Analysis
// Analyze update with filter and SET expressions
var updateBreakdown = LinqQueryBreakdown.AnalyzeUpdate<User>(
u => u.Department == "Sales",
u => new User { IsActive = false, UpdatedDate = DateTime.Now }
);
Console.WriteLine($"Table: {updateBreakdown.TableName}"); // User
Console.WriteLine($"WHERE: {updateBreakdown.WhereClause}"); // (Department = 'Sales')
Console.WriteLine($"SET: {updateBreakdown.SetClause}"); // Column assignments
// Practical example: Deactivate inactive users
var deactivateBreakdown = LinqQueryBreakdown.AnalyzeUpdate<User>(
u => u.LastLoginDate < DateTime.Now.AddDays(-90),
u => new User { IsActive = false }
);
PROCEDURE Analysis
// Simple procedure call
var procBreakdown = LinqQueryBreakdown.AnalyzeProcedure("sp_GetUsers");
Console.WriteLine($"Procedure: {procBreakdown.ProcedureName}");
Console.WriteLine($"Parameters: {procBreakdown.Parameters.Count}");
// Procedure with parameters
var procWithParamsBreakdown = LinqQueryBreakdown.AnalyzeProcedure(
"sp_GetUsersByAgeRange",
18, 65
);
Console.WriteLine($"Procedure: {procWithParamsBreakdown.ProcedureName}");
Console.WriteLine($"Parameter count: {procWithParamsBreakdown.Parameters.Count}");
foreach (var param in procWithParamsBreakdown.Parameters)
{
Console.WriteLine($" {param.Key}: {param.Value}");
}
TRACE Analysis
// Analyze query execution context
var query = _context.Users.Where(u => u.IsActive);
var traceInfo = LinqQueryBreakdown.AnalyzeTrace(query, "Initial User Load");
Console.WriteLine(traceInfo);
// Output:
// Trace Context for User
// Entity Type: Namespace.User
// Query Provider: EntityQueryProvider
// Expression: Where(Where(...))
// Execution Context: Initial User Load
// Timestamp: 2026-02-24T10:30:45.1234567Z
// Use in logging
_logger.LogInformation("Query trace:\n{Trace}", traceInfo);
Use Cases
1. Query Performance Analysis
var query = context.Products
.Where(p => p.Price > 100)
.Where(p => p.InStock)
.OrderBy(p => p.Name);
var breakdown = LinqQueryBreakdown.Analyze(query);
// Check for multiple WHERE clauses (could be combined)
var whereCount = breakdown.MethodCallChain.Count(m => m == "Where");
if (whereCount > 1)
{
Console.WriteLine($"Warning: {whereCount} separate WHERE clauses detected. Consider combining.");
}
2. Documentation Generation
var queries = new Dictionary<string, IQueryable>
{
["ActiveUsers"] = context.Users.Where(u => u.IsActive),
["RecentOrders"] = context.Orders.Where(o => o.OrderDate > DateTime.Now.AddDays(-30)),
["TopProducts"] = context.Products.OrderByDescending(p => p.SalesCount).Take(10)
};
var generator = new QueryBreakdownGenerator();
var documentation = new StringBuilder();
foreach (var (name, query) in queries)
{
var breakdown = LinqQueryBreakdown.Analyze(query);
var diagram = generator.GenerateCombinedDiagram(breakdown, name);
documentation.AppendLine($"## {name}");
documentation.AppendLine(breakdown.GetQuerySummary());
documentation.AppendLine(diagram);
documentation.AppendLine();
}
File.WriteAllText("queries.md", documentation.ToString());
4. Data Modification Auditing
public class AuditLogger
{
public void LogInsert<T>(T entity) where T : class
{
var breakdown = LinqQueryBreakdown.AnalyzeInsert(entity);
var audit = new AuditEntry
{
Operation = "INSERT",
Table = breakdown.TableName.Clause,
Columns = breakdown.InsertIntoClause.Clause,
Timestamp = DateTime.UtcNow
};
_auditContext.SaveAudit(audit);
}
public void LogDelete<T>(Expression<Func<T, bool>> filter) where T : class
{
var breakdown = LinqQueryBreakdown.AnalyzeDelete(filter);
var audit = new AuditEntry
{
Operation = "DELETE",
Table = breakdown.FromClause.Clause,
Condition = breakdown.WhereClause?.Clause,
Timestamp = DateTime.UtcNow
};
_auditContext.SaveAudit(audit);
}
public void LogUpdate<T>(Expression<Func<T, bool>> filter, Expression<Func<T, T>> updates) where T : class
{
var breakdown = LinqQueryBreakdown.AnalyzeUpdate(filter, updates);
var audit = new AuditEntry
{
Operation = "UPDATE",
Table = breakdown.TableName.Clause,
Updates = breakdown.SetClause?.Clause,
Condition = breakdown.WhereClause?.Clause,
Timestamp = DateTime.UtcNow
};
_auditContext.SaveAudit(audit);
}
}
5. Dynamic Query Logging
public class QueryLogger
{
public void TraceExecution<T>(IQueryable<T> query, string context) where T : class
{
var traceInfo = LinqQueryBreakdown.AnalyzeTrace(query, context);
_logger.LogInformation("Query Execution Trace:\n{TraceInfo}", traceInfo);
if (LinqQueryBreakdown.TryAnalyze(query, out var breakdown))
{
_logger.LogDebug("Query Summary: {Summary}", breakdown.GetQuerySummary());
_logger.LogDebug("Methods: {Methods}", string.Join(" -> ", breakdown.GetMethodChain()));
}
}
}
// Usage
var query = _context.Users.Where(u => u.IsActive).OrderBy(u => u.Name);
_queryLogger.TraceExecution(query, "Active Users Report");
Architecture
Class Hierarchy
IQueryBreakdown (Interface)
↑
QueryBreakdown (Strata.SqlTools)
↑
SqlServer.QueryBreakdown
↑
LinqQueryBreakdown
Component Interaction
flowchart TD
A[IQueryable<T>] --> B[LinqQueryBreakdown.Analyze]
B --> C[LinqExpressionVisitor]
C --> D{Expression Type}
D -->|MethodCall| E[VisitMethodCall]
D -->|Binary| F[VisitBinary]
D -->|Member| G[VisitMember]
D -->|Constant| H[VisitConstant]
E --> I[Extract WHERE/SELECT/ORDER BY]
F --> I
G --> I
H --> I
I --> J[LinqQueryBreakdown Instance]
J --> K[QueryBreakdownGenerator]
K --> L[Mermaid Diagrams]
Limitations
Current Limitations
-
Limited LINQ Method Support: Currently supports Where, Select, OrderBy, OrderByDescending, ThenBy, GroupBy
- Not yet supported: Join, GroupJoin, Skip, Take, First, Last, etc.
-
Simple Expressions Only: Complex lambda expressions may not be fully parsed
- Example: Nested method calls in predicates
-
No Subquery Analysis: Subqueries in LINQ are not yet analyzed
-
Entity Framework Specific: Optimized for LINQ to SQL/Entity Framework patterns
- May not work with all IQueryable providers
-
No Query Reconstruction: The
GetQuery<T>()method returns null because breakdowns are analyzed one-way- Breakdown analysis cannot reconstruct the original LINQ query without the data provider
What's Now Supported
✅ INSERT Analysis - Extract column names and values from entity instances ✅ DELETE Analysis - Extract filter conditions for deletion ✅ UPDATE Analysis - Extract filter conditions and SET clauses ✅ PROCEDURE Analysis - Parse procedure names and parameters ✅ TRACE Analysis - Capture query execution context with timestamps
Workarounds
For unsupported methods, you can still access the base QueryBreakdown properties:
var query = context.Users.Take(10); // Take() not explicitly tracked
var breakdown = LinqQueryBreakdown.Analyze(query);
// SelectClause, FromClause still available
// MethodCallChain may be incomplete
For query reconstruction, use the original IQueryable directly rather than attempting to reconstruct from the breakdown.
Best Practices
1. Use TryAnalyze for Dynamic Queries
// Good: Handle analysis failures gracefully
if (LinqQueryBreakdown.TryAnalyze(userProvidedQuery, out var breakdown))
{
ProcessBreakdown(breakdown);
}
else
{
LogError("Unable to analyze query");
}
// Avoid: Analyze() throws on failure
var breakdown = LinqQueryBreakdown.Analyze(userProvidedQuery); // May throw
2. Check for Null Components
var breakdown = LinqQueryBreakdown.Analyze(query);
// Good: Check before using
if (!string.IsNullOrEmpty(breakdown.WhereClause))
{
Console.WriteLine($"Filter: {breakdown.WhereClause}");
}
// Avoid: Direct access without checking
Console.WriteLine(breakdown.WhereClause.Length); // NullReferenceException if no WHERE
3. Combine with Logging
public IQueryable<User> GetFilteredUsers(int minAge)
{
var query = _context.Users.Where(u => u.Age >= minAge);
// Log query structure for debugging
if (LinqQueryBreakdown.TryAnalyze(query, out var breakdown))
{
_logger.LogDebug("Query: {Summary}", breakdown.GetQuerySummary());
_logger.LogDebug("Methods: {Methods}", string.Join(", ", breakdown.GetMethodChain()));
}
return query;
}
Testing
The LinqToSql package includes comprehensive unit tests for all analysis types:
SELECT Query Tests
[Test]
public void Analyze_SimpleSelectQuery_ExtractsTableName()
{
var query = _context.Users;
var breakdown = LinqQueryBreakdown.Analyze(query);
Assert.That(breakdown.EntityType, Is.EqualTo("User"));
Assert.That(breakdown.FromClause, Is.EqualTo("Users"));
}
[Test]
public void Analyze_WhereClause_ExtractsCondition()
{
var query = _context.Users.Where(u => u.Age > 21);
var breakdown = LinqQueryBreakdown.Analyze(query);
Assert.That(breakdown.WhereClause, Does.Contain("Age"));
Assert.That(breakdown.WhereClause, Does.Contain(">"));
Assert.That(breakdown.WhereClause, Does.Contain("21"));
}
[Test]
public void GetMethodChain_MultipleOperations_ReturnsCorrectSequence()
{
var query = _context.Users
.Where(u => u.IsActive)
.OrderBy(u => u.Name)
.Select(u => new { u.Id, u.Name });
var breakdown = LinqQueryBreakdown.Analyze(query);
var chain = breakdown.GetMethodChain();
Assert.That(chain, Is.EqualTo(new[] { "Where", "OrderBy", "Select" }));
}
Statement Type Tests
[Test]
public void AnalyzeInsert_SingleEntity_CreatesInsertBreakdown()
{
var entity = new User { Id = 1, Name = "John Doe", Email = "john@example.com", Age = 30 };
var breakdown = LinqQueryBreakdown.AnalyzeInsert(entity);
Assert.That(breakdown.TableName.Clause, Is.EqualTo("User"));
Assert.That(breakdown.InsertIntoClause.Clause, Does.Contain("Id"));
Assert.That(breakdown.InsertIntoClause.Clause, Does.Contain("Name"));
}
[Test]
public void AnalyzeInsertRange_MultipleEntities_CreatesInsertBreakdown()
{
var entities = new List<User>
{
new User { Id = 1, Name = "John Doe", Email = "john@example.com", Age = 30 },
new User { Id = 2, Name = "Jane Smith", Email = "jane@example.com", Age = 28 }
};
var breakdown = LinqQueryBreakdown.AnalyzeInsertRange(entities);
Assert.That(breakdown.TableName.Clause, Is.EqualTo("User"));
Assert.That(breakdown.ValuesClause.Clause, Does.Contain("("));
}
[Test]
public void AnalyzeDelete_WithFilterExpression_CreatesDeleteBreakdown()
{
var breakdown = LinqQueryBreakdown.AnalyzeDelete<User>(u => u.Age < 18);
Assert.That(breakdown.FromClause.Clause, Is.EqualTo("User"));
Assert.That(breakdown.WhereClause.Clause, Is.Not.Empty);
}
[Test]
public void AnalyzeUpdate_WithFilterAndUpdateExpressions_CreatesUpdateBreakdown()
{
var breakdown = LinqQueryBreakdown.AnalyzeUpdate<User>(
u => u.Department == "Sales",
u => new User { IsActive = false }
);
Assert.That(breakdown.TableName.Clause, Is.EqualTo("User"));
Assert.That(breakdown.WhereClause.Clause, Is.Not.Empty);
}
[Test]
public void AnalyzeProcedure_WithName_CreatesProcedureBreakdown()
{
var breakdown = LinqQueryBreakdown.AnalyzeProcedure("sp_GetUsers");
Assert.That(breakdown.ProcedureName.Clause, Is.EqualTo("sp_GetUsers"));
}
[Test]
public void AnalyzeProcedure_WithParameters_CreatesProcedureBreakdownWithParams()
{
var breakdown = LinqQueryBreakdown.AnalyzeProcedure("sp_GetUsersByAge", 18, 65);
Assert.That(breakdown.ProcedureName.Clause, Is.EqualTo("sp_GetUsersByAge"));
Assert.That(breakdown.Parameters.Count, Is.EqualTo(2));
}
[Test]
public void AnalyzeTrace_WithValidQuery_ReturnsTraceString()
{
var query = _context.Users.Where(u => u.Age > 18);
var trace = LinqQueryBreakdown.AnalyzeTrace(query, "Test Context");
Assert.That(trace, Does.Contain("User"));
Assert.That(trace, Does.Contain("Query Provider"));
Assert.That(trace, Does.Contain("Test Context"));
}
Troubleshooting
Query Analysis Returns Empty Results
Problem: LinqQueryBreakdown.Analyze() returns a breakdown with null/empty clauses.
Solution: Ensure your query is an IQueryable<T>. LINQ to Objects (IEnumerable<T>) won't work:
// Wrong: IEnumerable (LINQ to Objects)
var list = new List<User>();
var query = list.Where(u => u.Age > 21); // IEnumerable<User>
// Right: IQueryable (LINQ to SQL)
var query = _context.Users.Where(u => u.Age > 21); // IQueryable<User>
Method Chain Missing Methods
Problem: GetMethodChain() doesn't show all LINQ methods used.
Solution: Only supported methods are tracked. Check the Supported LINQ Methods table.
Expression Too Complex
Problem: Complex lambda expressions aren't fully parsed.
Solution: Simplify expressions or break into multiple LINQ calls:
// Complex (may not parse fully)
var query = users.Where(u => CalculateScore(u.Age, u.Experience) > threshold);
// Simpler (parses better)
var query = users.Where(u => u.Age > minAge).Where(u => u.Experience > minExp);
API Reference
Namespaces
Strata.SqlTools.Breakdowns.LinqToSql- Core breakdown classesStrata.SqlTools.Visitors.LinqToSql- Expression tree visitorsStrata.SqlTools.Markdown.LinqToSql- Markdown/Mermaid generators
Key Classes
| Class | Purpose |
|---|---|
LinqQueryBreakdown |
Main analysis class, analyzes IQueryable expressions |
LinqExpressionVisitor |
Expression tree visitor for extracting SQL components |
QueryBreakdownGenerator |
Generates Mermaid diagrams from breakdowns |
SqlStatementGenerator |
Generates sequence/pipeline diagrams |
Related Documentation
- SqlUtilities.Core.md - Core library documentation
- SqlUtilities.SqlServer.md - SQL Server base classes
- EFCore_Integration_Guide.md - Entity Framework Core integration
Version: 1.1.0 Last Updated: February 2026 Package: Strata.SqlTools.LinqToSql
Changelog:
- v1.1.0: Added statement type analysis methods (INSERT, UPDATE, DELETE, PROCEDURE, TRACE)
- v1.0.0: Initial release with SELECT query analysis