# 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 ```bash 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 ```csharp using Strata.SqlTools.Breakdowns.LinqToSql; // Define your data context with IQueryable properties public class DataContext { public IQueryable Users => new List().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 ```csharp // SELECT Query Analysis public static LinqQueryBreakdown Analyze(IQueryable query) public static bool TryAnalyze(IQueryable query, out LinqQueryBreakdown? breakdown) // INSERT Operations public static InsertBreakdown AnalyzeInsert(T entity) where T : class public static InsertBreakdown AnalyzeInsertRange(IEnumerable entities) where T : class // DELETE Operations public static DeleteBreakdown AnalyzeDelete(Expression> filterExpression) where T : class // UPDATE Operations public static UpdateBreakdown AnalyzeUpdate( Expression> filterExpression, Expression> updateExpression) where T : class // PROCEDURE Operations public static ProcedureBreakdown AnalyzeProcedure(string procedureName, params object[] parameters) // TRACE Operations public static string AnalyzeTrace(IQueryable query, string? executionContext = null) where T : class ``` #### Properties ```csharp public Expression? OriginalExpression { get; } // Original LINQ expression tree public string EntityType { get; } // Entity type name (e.g., "User") public List MethodCallChain { get; } // List of LINQ method calls ``` #### Methods ```csharp public string GetQuerySummary() // Human-readable query summary public List 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 ```bash dotnet add package Strata.SqlTools.Markdown ``` ### QueryBreakdownGenerator Generates Mermaid diagrams showing query structure and LINQ method chains. ```csharp 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:** ```mermaid flowchart LR Start[IQueryable] --> Where[Where] Where --> OrderBy[OrderBy] OrderBy --> Select[Select] Select --> Result[Result] ``` ### SqlStatementGenerator Generates sequence diagrams showing LINQ execution pipeline. ```csharp 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:** ```mermaid 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 ```csharp // 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 ```csharp 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: ```csharp 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 ```csharp // 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 { 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 ```csharp // Analyze deletion with filter expression var deleteBreakdown = LinqQueryBreakdown.AnalyzeDelete(u => u.Age < 18); Console.WriteLine($"Table: {deleteBreakdown.FromClause}"); // User Console.WriteLine($"WHERE: {deleteBreakdown.WhereClause}"); // (Age < 18) // Complex filter var complexDelete = LinqQueryBreakdown.AnalyzeDelete(o => o.Status == "Cancelled" && o.OrderDate < DateTime.Now.AddYears(-1)); Console.WriteLine($"Deleting old cancelled orders: {complexDelete.WhereClause}"); ``` ### UPDATE Analysis ```csharp // Analyze update with filter and SET expressions var updateBreakdown = LinqQueryBreakdown.AnalyzeUpdate( 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( u => u.LastLoginDate < DateTime.Now.AddDays(-90), u => new User { IsActive = false } ); ``` ### PROCEDURE Analysis ```csharp // 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 ```csharp // 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 ```csharp 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 ```csharp var queries = new Dictionary { ["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 ```csharp public class AuditLogger { public void LogInsert(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(Expression> 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(Expression> filter, Expression> 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 ```csharp public class QueryLogger { public void TraceExecution(IQueryable 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 ```mermaid flowchart TD A[IQueryable] --> 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 1. **Limited LINQ Method Support**: Currently supports Where, Select, OrderBy, OrderByDescending, ThenBy, GroupBy - Not yet supported: Join, GroupJoin, Skip, Take, First, Last, etc. 2. **Simple Expressions Only**: Complex lambda expressions may not be fully parsed - Example: Nested method calls in predicates 3. **No Subquery Analysis**: Subqueries in LINQ are not yet analyzed 4. **Entity Framework Specific**: Optimized for LINQ to SQL/Entity Framework patterns - May not work with all IQueryable providers 5. **No Query Reconstruction**: The `GetQuery()` 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: ```csharp 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 ```csharp // 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 ```csharp 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 ```csharp public IQueryable 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 ```csharp [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 ```csharp [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 { 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(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( 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`. LINQ to Objects (`IEnumerable`) won't work: ```csharp // Wrong: IEnumerable (LINQ to Objects) var list = new List(); var query = list.Where(u => u.Age > 21); // IEnumerable // Right: IQueryable (LINQ to SQL) var query = _context.Users.Where(u => u.Age > 21); // IQueryable ``` ### Method Chain Missing Methods **Problem**: `GetMethodChain()` doesn't show all LINQ methods used. **Solution**: Only supported methods are tracked. Check the [Supported LINQ Methods](#supported-linq-methods) table. ### Expression Too Complex **Problem**: Complex lambda expressions aren't fully parsed. **Solution**: Simplify expressions or break into multiple LINQ calls: ```csharp // 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 classes - `Strata.SqlTools.Visitors.LinqToSql` - Expression tree visitors - `Strata.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](SqlUtilities.Core.md) - Core library documentation - [SqlUtilities.SqlServer.md](SqlUtilities.SqlServer.md) - SQL Server base classes - [EFCore_Integration_Guide.md](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