{"id":57786,"date":"2025-08-27T10:05:00","date_gmt":"2025-08-27T17:05:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/dotnet\/?p=57786"},"modified":"2025-08-27T12:08:31","modified_gmt":"2025-08-27T19:08:31","slug":"ef-core-visualizer-view-entity-framework-core-query-plan-inside-visual-studio","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/dotnet\/ef-core-visualizer-view-entity-framework-core-query-plan-inside-visual-studio\/","title":{"rendered":"EFCore.Visualizer &#8211; View Entity Framework Core query plan inside Visual Studio"},"content":{"rendered":"<blockquote><p>This is a guest blog from <a href=\"https:\/\/github.com\/Giorgi\">Giorgi Dalakishvili<\/a>, the developer of <a href=\"https:\/\/marketplace.visualstudio.com\/items?itemName=GiorgiDalakishvili.EFCoreVisualizer\">EFCore.Visualizer<\/a>.<\/p><\/blockquote>\n<p>Entity Framework Core is a powerful, feature-rich ORM powering many of today&#8217;s applications. With EF Core, developers write strongly-typed LINQ queries that the framework translates into SQL queries for the target database. With advanced features such as including nested collections and lazy loading, Entity Framework Core frees developers from writing boilerplate data access code.<\/p>\n<h2>The Problem<\/h2>\n<p>While LINQ queries are typically translated to well-performing SQL queries, as schemas become larger and queries more complex, the generated SQL can become suboptimal. Missing database indexes can also cause queries to execute slowly, leading to degraded application performance.<\/p>\n<p>EF Core provides an easy way to <a href=\"https:\/\/learn.microsoft.com\/ef\/core\/logging-events-diagnostics\/#simple-logging\">log generated queries<\/a> and <a href=\"https:\/\/learn.microsoft.com\/ef\/core\/performance\/performance-diagnosis?tabs=simple-logging%2Cload-entities#identifying-slow-database-commands-via-logging\">identify slow queries<\/a>. This might be enough sometimes but to really get to the root of the problem and see how the database engine executes queries, it&#8217;s necessary to explore the <a href=\"https:\/\/learn.microsoft.com\/sql\/relational-databases\/performance\/execution-plans?view=sql-server-ver17\">query execution plan<\/a>.<\/p>\n<h2>The Solution<\/h2>\n<p><a href=\"https:\/\/marketplace.visualstudio.com\/items?itemName=GiorgiDalakishvili.EFCoreVisualizer\">EFCore.Visualizer<\/a> is a Visual Studio extension for viewing and analyzing query plans directly inside Visual Studio. The extension adds a debugger visualizer for <code>IQueryable&lt;&gt;<\/code> variables that shows both the generated query and its execution plan.<\/p>\n<p>When you hit a breakpoint and hover over any <code>IQueryable<\/code> variable, EFCore.Visualizer captures the query, requests the execution plan from your database, and shows a visual representation of the query plan. The visualizer works with any EF Core query, whether it&#8217;s a simple <code>Where<\/code> clause or a complex query with joins, includes, and aggregations. The extension supports every major RDBMS: SQL Server, PostgreSQL, MySQL, SQLite, and Oracle, automatically detecting your database provider.<\/p>\n<p>By bringing the query plan directly inside Visual Studio, it removes the need to switch between Visual Studio and the database management tool for viewing query plans and shortens the developer inner loop. Instead of copying the query from Visual Studio to database management tool, analyzing the execution plan, switching back, tweaking the query and repeating the above steps again, developers can view the query plan in Visual Studio right where they write and debug their code.<\/p>\n<h2>Installation<\/h2>\n<p>Getting started with EFCore.Visualizer is straightforward. You can install the extension directly from within Visual Studio by searching for &#8220;EFCore.Visualizer&#8221; in the Extension Manager. Alternatively, you can download it from the <a href=\"https:\/\/marketplace.visualstudio.com\/items?itemName=GiorgiDalakishvili.EFCoreVisualizer\">Visual Studio Marketplace<\/a>.<\/p>\n<h2>Usage<\/h2>\n<p>To demonstrate the extension usage, I will use the following model:<\/p>\n<pre><code class=\"language-cs\">public class BloggingContext : DbContext\r\n{\r\n    public DbSet&lt;Blog&gt; Blogs { get; set; }\r\n    public DbSet&lt;Post&gt; Posts { get; set; }\r\n\r\n    protected override void OnModelCreating(ModelBuilder modelBuilder)\r\n    {\r\n        base.OnModelCreating(modelBuilder);\r\n        modelBuilder.Entity&lt;Post&gt;().HasIndex(p =&gt; p.PublishedAt);\r\n    }\r\n}\r\n\r\npublic class Blog\r\n{\r\n    public int BlogId { get; set; }\r\n    public string Url { get; set; }\r\n\r\n    public List&lt;Post&gt; Posts { get; } = new();\r\n}\r\n\r\npublic class Post\r\n{\r\n    public int PostId { get; set; }\r\n    public string Title { get; set; }\r\n    public string Content { get; set; }\r\n\r\n    public DateTimeOffset PublishedAt { get; set; }\r\n\r\n    public int BlogId { get; set; }\r\n    public Blog Blog { get; set; }\r\n}<\/code><\/pre>\n<p>The sample database has a couple of thousand rows in the <code>Posts<\/code> table.<\/p>\n<p>Once installed, start debugging and hover over any <code>IQueryable<\/code> instance. In the standard debugger tooltip click <strong>Query Plan Visualizer<\/strong> and view the generated SQL and the execution plan.<\/p>\n<p>Let&#8217;s start by writing a query to get all posts written in 2010 and examining its execution plan:<\/p>\n<pre><code class=\"language-cs\">var postsQuery = bloggingContext.Posts.Where(post =&gt; post.PublishedAt.Year == 2010);<\/code><\/pre>\n<p><img decoding=\"async\" title=\"Debugger Tooltip\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2025\/08\/debugger-visualizer.png\" alt=\"Screenshot showing the Query Plan Visualizer option in Visual Studio debugger tooltip\" \/>\n<img decoding=\"async\" title=\"SQL Server Plan\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2025\/08\/sql-server-plan.png\" alt=\"SQL Server execution plan showing table scan without index usage\" \/><\/p>\n<p>You can see that even though there is an index on the <code>PublishedAt<\/code> column, SQL Server doesn&#8217;t use it. If you look at the generated query, you will notice that the query is extracting the year from <code>PublishedAt<\/code> the column, making the query <a href=\"https:\/\/en.wikipedia.org\/wiki\/Sargable\">non-sargable<\/a>.<\/p>\n<p>Let&#8217;s rewrite the query without changing the semantics and see what the execution plan looks like:<\/p>\n<pre><code class=\"language-cs\">var fromDate = new DateTime(2010, 1, 1);\r\nvar toDate = new DateTime(2011, 1, 1);\r\n\r\npostsQuery = bloggingContext.Posts.Where(post =&gt; post.PublishedAt &gt;= fromDate &amp;&amp; post.PublishedAt &lt; toDate);<\/code><\/pre>\n<p><img decoding=\"async\" title=\"SQL Server Plan with Sargable Query\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2025\/08\/sql-server-plan-fixed.png\" alt=\"SQL Server execution plan showing index seek used by sargable date filter\" \/><\/p>\n<p>As you can see, with a simple change to the query, the database is now utilizing the index on the <code>PublishedAt<\/code>.<\/p>\n<h2>How it works<\/h2>\n<p>The visualizer works by turning the LINQ queries into ADO.NET commands and fetching its plan from the database engine. The plan is then rendered using the <a href=\"https:\/\/github.com\/JustinPealing\/html-query-plan\">html-query-plan<\/a> library for SQL Server, <a href=\"https:\/\/github.com\/dalibo\/pev2\/\">pev2<\/a> for Postgres, or <a href=\"https:\/\/github.com\/dumptyd\/treeflex\">treeflex<\/a> for other databases.<\/p>\n<h2>Limitations<\/h2>\n<p>The visualizer doesn&#8217;t support queries when a reducing terminating operator is used (<code>Count()<\/code>, <code>Min()<\/code>, <code>First()<\/code> etc). Also, if the query is very complex or the network connection to the database engine is slow, fetching the query plan can exceed the 5-second limit for custom visualizers. Unfortunately, there is no way to extend the timeout, but you can vote for the <a href=\"https:\/\/github.com\/microsoft\/VSExtensibility\/issues\/325\">issue<\/a>.<\/p>\n<h2>Conclusion<\/h2>\n<p>As developers, we&#8217;ve all been there &#8211; staring at a slow query, wondering what the database is actually doing. If you&#8217;re working with Entity Framework Core and want to better understand your query performance, give <a href=\"https:\/\/marketplace.visualstudio.com\/items?itemName=GiorgiDalakishvili.EFCoreVisualizer\">EFCore.Visualizer<\/a> a try. The source code is available on <a href=\"https:\/\/github.com\/Giorgi\/EFCore.Visualizer\">GitHub<\/a> if you&#8217;d like to contribute or explore how it works.<\/p>\n<p>For a deeper look at EFCore.Visualizer, check out its feature episode on <em data-start=\"275\" data-end=\"298\" data-is-only-node=\"\">Visual Studio Toolbox<\/em>:<\/p>\n<p><a href=\"https:\/\/www.youtube.com\/watch?v=0z5320LK9LI\"><img decoding=\"async\" src=\"https:\/\/img.youtube.com\/vi\/0z5320LK9LI\/0.jpg\" alt=\"HTML tutorial\" \/><\/a><\/p>\n<div id=\"ir-ext-ui\" style=\"color: #000000; font-size: 16px; line-height: 16px; background-color: #f7f7f7; border: 1px solid #999999; border-radius: 1px; top: 1px; left: 1px; display: none;\">\n<div class=\"ir-ext-dimensions\"><span class=\"ir-ext-rendered\" title=\"Rendered image dimensions (after any scaling\/resizing has been applied)\"> <span data-ir-ext-width=\"\">960<\/span>x<span data-ir-ext-height=\"\">587<\/span> <\/span> <span class=\"ir-ext-natural\" style=\"display: none;\" title=\"Natural image dimensions (without applying any scaling\/resizing)\"> (<span data-ir-ext-width=\"\">0<\/span>x<span data-ir-ext-height=\"\">0<\/span>) <\/span><\/div>\n<div class=\"ir-ext-filesize\"><span title=\"76531 bytes\" data-ir-ext-value=\"\">74.74<\/span> <span title=\"Kilobyte: 1 KB = 2^10 (1024) bytes\" data-ir-ext-unit=\"\">KB<\/span><\/div>\n<style>    #ir-ext-ui {      position: fixed;      padding: 1px;      z-index: 9999;      display: none;      font-family: Consolas, \"Lucida Console\", \"Courier New\", Courier, monospace;      text-align: right;    }  <\/style>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>A Visual Studio extension that helps developers visualize and analyze Entity Framework Core query execution plans directly within their development environment.<\/p>\n","protected":false},"author":92199,"featured_media":57787,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[685,859,646],"tags":[8062,7250,8061],"class_list":["post-57786","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-dotnet","category-entity-framework","category-visual-studio","tag-net-data","tag-ef-core","tag-visual-studio-extension"],"acf":[],"blog_post_summary":"<p>A Visual Studio extension that helps developers visualize and analyze Entity Framework Core query execution plans directly within their development environment.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/57786","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/users\/92199"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/comments?post=57786"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/57786\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media\/57787"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media?parent=57786"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/categories?post=57786"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/tags?post=57786"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}