<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Informatics - Medium]]></title>
        <description><![CDATA[Sharing facts, thoughts, experiences, and ideas related to informatics. I also write at https://emramadhan.com. You can support my work at https://ko-fi.com/emramadhan to fuel my next article. Thank you! - Medium]]></description>
        <link>https://medium.com/informatics?source=rss----191053c6302a---4</link>
        <image>
            <url>https://cdn-images-1.medium.com/proxy/1*TGH72Nnw24QL3iV9IOm4VA.png</url>
            <title>Informatics - Medium</title>
            <link>https://medium.com/informatics?source=rss----191053c6302a---4</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Thu, 05 Nov 2026 14:42:45 GMT</lastBuildDate>
        <atom:link href="https://medium.com/feed/informatics" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[EF Core & Blazor #5: Writing Smarter Queries & Loading Data Efficiently]]></title>
            <link>https://medium.com/informatics/ef-core-blazor-5-writing-smarter-queries-loading-data-efficiently-fafd2c308979?source=rss----191053c6302a---4</link>
            <guid isPermaLink="false">https://medium.com/p/fafd2c308979</guid>
            <category><![CDATA[relational-databases]]></category>
            <category><![CDATA[entity-framework-core]]></category>
            <category><![CDATA[blazor]]></category>
            <category><![CDATA[linq]]></category>
            <category><![CDATA[csharp]]></category>
            <dc:creator><![CDATA[M. Ramadhan]]></dc:creator>
            <pubDate>Wed, 28 Oct 2026 14:55:11 GMT</pubDate>
            <atom:updated>2026-10-30T06:37:07.006Z</atom:updated>
            <content:encoded><![CDATA[<h4><em>Learn how to choose the right data-loading strategy, project only the fields you need, aggregate database records, and improve query efficiency with Entity Framework Core and Blazor.</em></h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*vqk5juBBRBlzJ4_C" /><figcaption>Photo by <a href="https://unsplash.com/@hostreviews?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Stephen Phillips — Hostreviews.co.uk</a> on <a href="https://unsplash.com/photos/monitor-screengrab-shr_Xn8S8QU?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></figcaption></figure><h3>Table of Contents</h3><p><a href="#4e26">Introduction</a><br><a href="#17e2">1. Revisiting Include(): A Quick Refresher</a><br> ▹ <a href="#8d25">Loading a book together with its publisher</a><br> ▹ <a href="#89ca">Does every query need Include()?</a><br><a href="#d7e2">2. Understanding EF Core Data-Loading Strategies</a><br> ▹ <a href="#45a9">Eager loading: Retrieve related data with the main query</a><br> ▹ <a href="#d266">Explicit loading: Retrieve related data when needed</a><br> ▹ <a href="#75f6">Lazy loading: Convenient, but easy to misuse</a><br><a href="#690a">3. Moving Beyond Basic Queries with LINQ</a><br> ▹ <a href="#d62a">Filtering records in the database</a><br> ▹ <a href="#cd38">Combining conditions with related data</a><br> ▹ <a href="#82f3">Counting records without loading them all</a><br> ▹ <a href="#6ab4">Grouping data for summaries</a><br><a href="#8959">4. Building More Efficient Queries in Blazor</a><br> ▹ <a href="#044b">Use Projection to Retrieve Only the Data You Need</a><br> ▹ <a href="#c104">Use AsNoTracking() for Read-Only Queries</a><br> ▹ <a href="#6175">Combine Filtering, Projection, and No-Tracking</a><br> ▹ <a href="#c72d">Apply These Techniques in Blazor</a><br><a href="#9cca">5. Inspecting the SQL Generated by EF Core</a><br> ▹ <a href="#6027">Why You Should Inspect Generated SQL</a><br> ▹ <a href="#54c7">Using ToQueryString()</a><br> ▹ <a href="#b043">Understanding the SQL Output</a><br><a href="#8777">6. Complete Runnable Books.razor</a><br> ▹ <a href="#04d0">A. Understanding @inject ILogger Logger</a><br> ▹ <a href="#f3f3">B. Understanding errorMessage</a><br> ▹ <a href="#9971">C. Understanding isLoading</a><br> ▹ <a href="#4fdd">D. How These Three Pieces Work Together</a><br> ▹ <a href="#376a">E. Test It in Visual Studio</a><br><a href="#40a5">Conclusion</a><br><a href="#5e73">References</a></p><h3>Introduction</h3><p>Imagine that you are building a library application with Blazor and Entity Framework Core. The application can display books, show their publishers, and save changes to SQL Server. Everything works as expected.</p><p>Then the library grows from a few dozen books to several thousand. A page that once loaded almost instantly starts taking longer. Some operations retrieve more data than the screen needs, while others make repeated trips to the database.</p><p>The application may still be correct. The problem is how it retrieves its data.</p><p>This is an important stage in learning Entity Framework Core (EF Core). Creating, reading, updating, and deleting records is only the beginning. A well-designed application must also retrieve the right data, avoid unnecessary database operations, and handle related entities deliberately.</p><p>In Article 4 of this series, we explored the one-to-many relationship between publishers and books, including navigation properties, Fluent API configuration, and retrieving related data with Include().</p><p>Now, we can build on that foundation. This article explores EF Core data-loading strategies, LINQ projections, aggregation, read-only queries, and generated SQL. We will apply these concepts to the same Blazor library application, using SQL Server as the database.</p><p>The goal is not to make every query complicated. It is to make each query fit the job it needs to perform.</p><h3>1. Revisiting Include(): A Quick Refresher</h3><p>In our library application, a book can belong to a publisher. The Book entity contains a foreign key and a navigation property:</p><pre>namespace LibraryEfCore.Data.Models;<br><br>public class Book<br>{<br>   public int Id { get; set; }<br>   public string Title { get; set; } = &quot;&quot;;<br>   public string? ISBN { get; set; }<br>   public short PublishedYear { get; set; }<br>   public string? Description { get; set; }<br><br>   // Foreign Key &amp; Navigation Property<br>   public short? PublisherId { get; set; }<br>   public Publisher? Publisher { get; set; }<br>}</pre><p>The PublisherId property identifies the associated publisher. The Publisher navigation property allows application code to work with the related publisher entity when it has been loaded.</p><p>These two properties serve different purposes. A foreign key identifies a relationship in the database, while a navigation property provides an object-oriented way to access related data.</p><h4>Loading a book together with its publisher</h4><p>Suppose we want to display a list of books with their publisher names. We can use Include():</p><pre>var books = await db.Books<br>    .Include(b =&gt; b.Publisher)<br>    .ToListAsync();</pre><p>This is an example of <strong>eager loading</strong>. We tell EF Core that the related publisher data is needed as part of the query.</p><p>The result can then be used in a Razor component:</p><pre>@foreach (var book in books)<br>{<br>    &lt;tr&gt;<br>        &lt;td&gt;@book.Title&lt;/td&gt;<br>        &lt;td&gt;@(book.Publisher?.Name ?? &quot;Not assigned&quot;)&lt;/td&gt;<br>    &lt;/tr&gt;<br>}</pre><p>The null-conditional operator (?.) is useful here because the publisher relationship is optional. A book may not have a publisher assigned.</p><h4>Does every query need Include()?</h4><p>No. Include() is useful when you need related entity objects, but it is not required merely because a relationship exists.</p><p>For example, if a page needs only the book title and publisher name, you can select those values directly. You do not necessarily need to load complete Book and Publisher entities.</p><p>That distinction leads to a useful rule:</p><ul><li><strong>Use </strong><strong>Include() when you need related entities.</strong></li><li><strong>Consider projection when you need only selected values from those entities.</strong></li></ul><p>The best choice depends on what the application will do with the results.</p><h3>2. Understanding EF Core Data-Loading Strategies</h3><p>EF Core supports several approaches to loading related data. Understanding their differences helps you choose a strategy that fits each operation.</p><h4>Eager loading: Retrieve related data with the main query</h4><p>Eager loading is the approach we have already seen:</p><pre>var books = await db.Books<br>    .Include(b =&gt; b.Publisher)<br>    .ToListAsync();</pre><p>It is appropriate when the page needs the book and publisher information together.</p><p>For example, a library catalog might display a table containing book titles, ISBNs, publication years, and publisher names. Retrieving the required information as part of the initial query makes the data requirements explicit.</p><p>Eager loading does not guarantee that EF Core will always execute exactly one SQL statement. Depending on the query and configuration, EF Core can use a single query or split the work into multiple queries.</p><p>For a simple reference navigation such as Book.Publisher, a join is a common translation. More complicated collection-loading queries can introduce different performance considerations.</p><h4>Explicit loading: Retrieve related data when needed</h4><p>Sometimes, an application does not need related data immediately. Imagine a library administrator opens a book’s detail page. The page initially displays the title and publication year. The administrator can then select a button to display additional publisher information.</p><p>In that situation, explicit loading may be appropriate:</p><pre>var book = await db.Books<br>    .FirstOrDefaultAsync(b =&gt; b.Id == bookId);<br><br>if (book is not null)<br>{<br>    await db.Entry(book)<br>        .Reference(b =&gt; b.Publisher)<br>        .LoadAsync();<br>}</pre><p>This example assumes that bookId contains the requested book&#39;s ID and that db is an active LibraryDbContext.</p><p>The second operation explicitly loads the publisher navigation property. It requires another database operation if the relationship has not already been loaded.</p><p>That extra round trip may be worthwhile when the related information is genuinely optional. If every page request needs the publisher name, eager loading or projection is usually simpler.</p><h4>Lazy loading: Convenient, but easy to misuse</h4><p>Lazy loading retrieves related data when a navigation property is accessed, provided the feature has been configured and the entity supports it.</p><p>For example, code that accesses book.Publisher might trigger a database query automatically. This convenience can hide an important cost: <strong>the application may execute many more queries than the code appears to contain.</strong></p><p>Suppose a page loads 100 books and then accesses each book’s publisher individually. With lazy loading, this can result in one query for the books and additional queries for their publishers.</p><p>This pattern is commonly called the <em>N+1 query problem</em>: one query retrieves the main collection, and additional queries retrieve related data for individual items.</p><p>The exact number of queries depends on factors such as which relationships are accessed and whether related entities have already been loaded or tracked. Even so, the risk is worth understanding.</p><p>For our learning project, explicit query design with eager loading or projection is a good starting point. It makes database access easier to reason about and avoids introducing lazy-loading behavior that can surprise beginners.</p><h3>3. Moving Beyond Basic Queries with LINQ</h3><p>LINQ allows us to describe what data we want using C#. EF Core translates supported expressions into database operations when the query is executed.</p><p>We do not need to retrieve an entire table simply because we want to work with its records.</p><h4>Filtering records in the database</h4><p>Suppose the library catalog should display only books published from 2020 onward. We can write:</p><pre>var books = await db.Books<br>    .Where(b =&gt; b.PublishedYear &gt;= 2020)<br>    .ToListAsync();</pre><p>The Where() condition becomes part of the database query when EF Core can translate it. SQL Server can then filter the records before returning the results to the application.</p><p>This is generally preferable to retrieving every book and filtering the collection afterward:</p><pre>// Avoid this when the filtering can be done in SQL.<br>var allBooks = await db.Books.ToListAsync();<br><br>var recentBooks = allBooks<br>    .Where(b =&gt; b.PublishedYear &gt;= 2020)<br>    .ToList();</pre><p>The second version transfers all books to the application before filtering them in memory. For a small demonstration, the difference might be negligible. For a large database, it can waste memory, network bandwidth, and processing time.</p><p>There is also a practical distinction between IQueryable&lt;T&gt; and IEnumerable&lt;T&gt;. An EF Core query normally remains an IQueryable&lt;T&gt; while you compose database-translatable operations. Calling ToListAsync() executes the query and materializes the results into a list.</p><h4>Combining conditions with related data</h4><p>Now suppose we want to retrieve books published from 2020 onward and display their publishers. We can combine filtering with eager loading:</p><pre>var books = await db.Books<br>    .Where(b =&gt; b.PublishedYear &gt;= 2020)<br>    .Include(b =&gt; b.Publisher)<br>    .ToListAsync();</pre><p>The query describes two requirements:</p><ul><li>Return books whose publication year is at least 2020.</li><li>Load the associated publisher for each returned book.</li></ul><p>EF Core can translate these requirements into SQL. The database performs the filtering, while the related publisher data is retrieved according to the loading strategy.</p><p>Notice that the query remains readable. We do not need to write SQL manually for every common data-access operation.</p><h4>Counting records without loading them all</h4><p>What if the page needs to show the number of books in the catalog? We could retrieve every book and count the resulting list, but that would be unnecessary. Instead, use CountAsync():</p><pre>var bookCount = await db.Books.CountAsync();</pre><p>To count only recently published books:</p><pre>var recentBookCount = await db.Books<br>    .CountAsync(b =&gt; b.PublishedYear &gt;= 2020);</pre><p>EF Core translates these operations into database-side counting when supported. SQL Server can return the count without transferring every matching book row to the application.</p><p>Other useful aggregate operations include AnyAsync(), SumAsync(), AverageAsync(), MinAsync(), and MaxAsync(), where appropriate for the data being queried.</p><p>For example, to determine whether a publisher has any books:</p><pre>var hasBooks = await db.Books<br>    .AnyAsync(b =&gt; b.PublisherId == publisherId);</pre><p>This is clearer than loading the publisher’s entire collection just to determine whether at least one book exists.</p><h4>Grouping data for summaries</h4><p>A library dashboard might need to show how many books each publisher has published. We can use GroupBy() and Select():</p><pre>var publisherSummaries = await db.Books<br>    .GroupBy(b =&gt; b.PublisherId)<br>    .Select(g =&gt; new<br>    {<br>        PublisherId = g.Key,<br>        BookCount = g.Count()<br>    })<br>    .ToListAsync();</pre><p>This groups books by their publisher foreign key and calculates a count for each group. Because PublisherId is nullable, the results may include a group for books that have no publisher assigned.</p><p>This example illustrates the principle of aggregation, but it returns publisher IDs rather than publisher names. If the dashboard needs names too, we can write a projection that joins publishers and groups the results, or query the publishers with a count of their related books.</p><p>For instance, a publisher-oriented summary can be written as:</p><pre>var publisherSummaries = await db.Publishers<br>    .Select(p =&gt; new<br>    {<br>        p.Name,<br>        BookCount = p.Books.Count()<br>    })<br>    .ToListAsync();</pre><p>EF Core can translate supported collection-count expressions into database queries. As always, inspect the generated SQL if performance or translation behavior is uncertain.</p><h3>4. Building More Efficient Queries in Blazor</h3><p>As your library application grows, retrieving data efficiently becomes increasingly important. A query that works well with a few dozen books may become less efficient when the database contains thousands of records. Fortunately, Entity Framework Core provides several techniques to help you retrieve only the data you need and reduce unnecessary processing.</p><p>In this section, we will explore four practical techniques: projection, no-tracking queries, combining query operations, and applying these techniques in a Blazor component.</p><h4>Use Projection to Retrieve Only the Data You Need</h4><p>When you query an entity, EF Core can retrieve all the mapped columns for that entity, even if your page displays only a few of them. Projection allows you to select only the properties required by your application.</p><p>For example, suppose your library catalog displays only a book’s title and publication year. You could retrieve the complete Book entities:</p><pre>var books = await db.Books<br>    .ToListAsync();</pre><p>However, this query retrieves all mapped columns for each book. If the Book entity contains properties such as Description, ISBN, and PublisherId, those columns may be retrieved even when the page does not need them.</p><p>A more focused query uses Select():</p><pre>var books = await db.Books<br>    .Select(b =&gt; new<br>    {<br>        b.Title,<br>        b.PublishedYear<br>    })<br>    .ToListAsync();</pre><p>Here, Select() projects each result into an anonymous object containing only Title and PublishedYear. EF Core can translate this projection into SQL that selects only the corresponding columns.</p><p>For example, the generated SQL may resemble:</p><pre>SELECT [b].[Title], [b].[PublishedYear]<br>FROM [Books] AS [b];</pre><p>The exact SQL formatting and aliases may vary depending on the EF Core version and query configuration.</p><p>Projection is particularly useful when building tables, summary cards, reports, and dashboard components. It can reduce the amount of data transferred from SQL Server and avoid materializing complete entities when only a few values are required.</p><p>For larger applications, you can project into a dedicated class instead of an anonymous type:</p><pre>public class BookListItem<br>{<br>    public string Title { get; set; } = &quot;&quot;;<br>    public int PublishedYear { get; set; }<br>}</pre><p>Then use the class in your query:</p><pre>var books = await db.Books<br>    .Select(b =&gt; new BookListItem<br>    {<br>        Title = b.Title,<br>        PublishedYear = b.PublishedYear<br>    })<br>    .ToListAsync();</pre><p>This approach makes the result structure explicit and reusable. It is especially helpful when passing data between application layers or displaying results in a Blazor component.</p><h4>Use AsNoTracking() for Read-Only Queries</h4><p>By default, EF Core tracks changes to entity instances returned by many queries. This tracking allows EF Core to detect modifications and persist them when you call SaveChangesAsync().</p><p>Change tracking is useful when you intend to update an entity. However, it may introduce unnecessary overhead when a page only displays data and does not modify the retrieved entities.</p><p>Consider a page that lists every book in the library:</p><pre>var books = await db.Books<br>    .OrderBy(b =&gt; b.Title)<br>    .ToListAsync();</pre><p>If the page only displays these books, tracking their changes is generally unnecessary. You can use AsNoTracking() to tell EF Core not to track the returned entities:</p><pre>var books = await db.Books<br>    .AsNoTracking()<br>    .OrderBy(b =&gt; b.Title)<br>    .ToListAsync();</pre><p>This option can reduce memory usage and change-tracking overhead, particularly when retrieving many entities. However, the actual performance benefit depends on the query, the number of results, and the application’s workload.</p><p>There is one important distinction: AsNoTracking() does not prevent you from reading or displaying the results. It simply means EF Core will not track the returned entities in its change tracker. If you later modify one of those entities and call SaveChangesAsync(), EF Core will not automatically persist that modification unless you explicitly attach or otherwise track the entity.</p><p>Use tracking queries when you need to load entities, modify them, and save the changes through the same context. Use no-tracking queries when you only need to retrieve and display entity data.</p><h4>Combine Filtering, Projection, and No-Tracking</h4><p>The techniques above are useful individually, but they can also be combined in a single query. This allows you to filter records, select specific properties, and avoid tracking entity results when appropriate.</p><p>Suppose your library application allows users to select a publisher and view only that publisher’s books. The page needs each book’s title and publication year, but it does not need the complete Book entity.</p><p>You could write:</p><pre>var books = await db.Books<br>    .AsNoTracking()<br>    .Where(b =&gt; b.PublisherId == publisherId)<br>    .OrderBy(b =&gt; b.Title)<br>    .Select(b =&gt; new BookListItem<br>    {<br>        Title = b.Title,<br>        PublishedYear = b.PublishedYear<br>    })<br>    .ToListAsync();</pre><p>This query combines four operations:</p><ul><li>AsNoTracking() specifies that returned entity instances should not be tracked.</li><li>Where() filters the books by publisher.</li><li>OrderBy() sorts the results alphabetically by title.</li><li>Select() projects each result into a BookListItem containing only the required properties.</li></ul><p>EF Core translates the query into SQL and executes it when ToListAsync() is called. Filtering, sorting, and selecting columns can therefore be performed by SQL Server rather than by retrieving all books and processing them in application memory.</p><p>Notice that the final result consists of BookListItem objects, not Book entities. Since this projection does not return Book entity instances, EF Core does not track the Book entities in this example anyway. Consequently, AsNoTracking() is not essential for this particular projection, although including it can make the read-only intention explicit and keep the query style consistent.</p><p>This distinction matters: <strong>projection and no-tracking solve related but different problems.</strong> Projection controls the shape of the returned data, while no-tracking controls whether EF Core tracks returned entity instances.</p><h4>Apply These Techniques in Blazor</h4><p>Let’s apply these ideas to the library application. Imagine a Blazor page that displays a list of books and lets users filter the results by publisher.</p><p>For this page, we want to:</p><ol><li>Retrieve the publisher names for a dropdown list.</li><li>Display only the book properties needed by the table.</li><li>Filter the books when a publisher is selected.</li><li>Sort the results by title.</li><li>Avoid unnecessary entity tracking for read-only operations.</li></ol><p>A query for the publisher dropdown could look like this:</p><pre>var publishers = await db.Publishers<br>    .AsNoTracking()<br>    .OrderBy(p =&gt; p.Name)<br>    .Select(p =&gt; new PublisherOption<br>    {<br>        Id = p.Id,<br>        Name = p.Name<br>    })<br>    .ToListAsync();</pre><p>The PublisherOption class can be defined as follows:</p><pre>public class PublisherOption<br>{<br>    public int Id { get; set; }<br>    public string Name { get; set; } = &quot;&quot;;<br>}</pre><p>This query retrieves only the publisher ID and name, which are sufficient for displaying dropdown options. Because the result is a projection into PublisherOption, the query does not return tracked Publisher entities.</p><p>Next, the page can retrieve books based on the selected publisher:</p><pre>var query = db.Books.AsNoTracking();<br><br>if (selectedPublisherId.HasValue)<br>{<br>    query = query.Where(<br>        b =&gt; b.PublisherId == selectedPublisherId.Value);<br>}<br>var books = await query<br>    .OrderBy(b =&gt; b.Title)<br>    .Select(b =&gt; new BookListItem<br>    {<br>        Title = b.Title,<br>        Description = b.Description,<br>        ISBN = b.ISBN,<br>        PublishedYear = b.PublishedYear,<br>        PublisherName = b.Publisher != null<br>            ? b.Publisher.Name<br>            : &quot;Not assigned&quot;<br>    })<br>    .ToListAsync();</pre><p>Here, selectedPublisherId is a nullable integer. When it has a value, the query filters the books by publisher. When it is null, the query returns books from all publishers.</p><p>The projection also retrieves the publisher’s name through the Publisher navigation property. EF Core can translate this expression into the appropriate SQL, so you do not need to call Include() simply to display the publisher&#39;s name. The result is a list of BookListItem objects containing the values needed by the page.</p><p>The corresponding result class is:</p><pre>public class BookListItem<br>{<br>    public string Title { get; set; } = &quot;&quot;;<br>    public string? Description { get; set; }<br>    public string? ISBN { get; set; }<br>    public int PublishedYear { get; set; }<br>    public string PublisherName { get; set; } = &quot;&quot;;<br>}</pre><p>Because this is a projection into a class rather than a collection of Book entities, EF Core does not track the Book entities for this query. The AsNoTracking() call is therefore optional in this specific example; it is more directly useful for queries that return entity instances.</p><p>In a Blazor Interactive Server application, you should also manage the lifetime of DbContext carefully. One common approach is to use IDbContextFactory&lt;LibraryDbContext&gt; to create a context for each database operation:</p><pre>await using var db =<br>    await DbFactory.CreateDbContextAsync();<br><br>var books = await db.Books<br>    .AsNoTracking()<br>    .OrderBy(b =&gt; b.Title)<br>    .Select(b =&gt; new BookListItem<br>    {<br>        Title = b.Title,<br>        PublishedYear = b.PublishedYear<br>    })<br>    .ToListAsync();</pre><p>This example assumes that DbFactory has been injected into the component and registered in Program.cs. Creating and disposing a context for each operation helps prevent a long-lived context from being shared across unrelated component operations.</p><p>These techniques provide a practical foundation for efficient data retrieval in Blazor. By selecting only the required properties, choosing tracking behavior deliberately, and letting SQL Server handle filtering and sorting, you can keep your queries focused as the library grows.</p><h3>5. Inspecting the SQL Generated by EF Core</h3><p>When you write queries with Entity Framework Core, you work primarily with C# and LINQ rather than SQL statements. EF Core translates supported LINQ expressions into SQL that the database provider can execute. This abstraction makes database programming more convenient, but understanding the SQL generated by EF Core is still an important skill.</p><p>Inspecting the generated SQL helps you understand how your LINQ queries interact with SQL Server. It can also reveal whether a query retrieves unnecessary columns, applies filtering in the database, or produces joins that deserve closer examination. In this section, we will explore why generated SQL matters, how to inspect it using ToQueryString(), and how to interpret the resulting SQL.</p><h4>Why You Should Inspect Generated SQL</h4><p>Two LINQ queries may produce the same results while generating different SQL statements. Depending on the query structure, one approach may retrieve more data than necessary or introduce additional database operations.</p><p>For example, consider this query:</p><pre>var books = await db.Books<br>    .ToListAsync();</pre><p>This query retrieves all mapped columns for all books. If your page displays only the title and publication year, retrieving every mapped column may be unnecessary.</p><p>Compare it with a projected query:</p><pre>var books = await db.Books<br>    .Select(b =&gt; new<br>    {<br>        b.Title,<br>        b.PublishedYear<br>    })<br>    .ToListAsync();</pre><p>The second query requests only the two required columns. Inspecting its generated SQL allows you to verify that EF Core translates the projection as expected.</p><p>Examining SQL can help you investigate several questions:</p><ul><li>Does the query retrieve only the columns the page needs?</li><li>Is filtering performed by SQL Server or unnecessarily in application memory?</li><li>Does the query introduce joins to retrieve related data?</li><li>Are sorting and aggregation operations translated into SQL?</li><li>Could the query retrieve more records than the application actually needs?</li></ul><p>Generated SQL is not a complete measure of query performance. A short SQL statement can still perform poorly because of indexes, data distribution, execution plans, or other database conditions. Nevertheless, inspecting the SQL is a useful first step toward understanding and improving database access.</p><h4>Using ToQueryString()</h4><p>EF Core provides the ToQueryString() extension method to display a debugging representation of the SQL for an IQueryable. You can use it to inspect the SQL generated from a LINQ query without first executing that query.</p><p>Suppose you want to retrieve books published in or after 2020, sorted alphabetically by title:</p><pre>var query = db.Books<br>    .Where(b =&gt; b.PublishedYear &gt;= 2020)<br>    .OrderBy(b =&gt; b.Title)<br>    .Select(b =&gt; new<br>    {<br>        b.Title,<br>        b.PublishedYear<br>    });<br><br>var sql = query.ToQueryString();<br>Console.WriteLine(sql);</pre><p>Make sure your file includes the EF Core namespace:</p><pre>using Microsoft.EntityFrameworkCore;</pre><p>The ToQueryString() method returns a string containing the SQL representation of the query. For SQL Server, the output may resemble the following:</p><pre>SELECT [b].[Title], [b].[PublishedYear]<br>FROM [Books] AS [b]<br>WHERE [b].[PublishedYear] &gt;= 2020<br>ORDER BY [b].[Title]</pre><p>The exact output may differ depending on your EF Core version, provider, and model configuration.</p><p>Notice that the SQL contains only the two requested columns. The publication-year condition appears in the WHERE clause, and the title ordering appears in the ORDER BY clause. This shows how EF Core translates the corresponding LINQ operations into SQL.</p><p>You can also inspect a query that retrieves a book’s publisher name:</p><pre>var query = db.Books<br>    .Where(b =&gt; b.PublisherId != null)<br>    .Select(b =&gt; new<br>    {<br>        b.Title,<br>        PublisherName = b.Publisher!.Name<br>    });<br><br>Console.WriteLine(query.ToQueryString());</pre><p>Because the query accesses the publisher’s name through a navigation property, EF Core can generate a SQL join to retrieve the related data. The exact SQL depends on the relationship configuration and the expression being translated.</p><p><strong>An important distinction:</strong> ToQueryString() is intended for debugging and inspection. It does not execute the query, and its output is not guaranteed to be the exact final command sent to the database in every situation. For example, the actual command may differ when EF Core applies execution-time behavior or when the query is used in a context that changes its translation.</p><p>To see the results, you must still execute the query:</p><pre>var books = await query.ToListAsync();</pre><p>In a Blazor application, you can inspect the returned SQL while debugging, use a breakpoint to examine the sql variable, or write the output to your application&#39;s logging system. Avoid relying on Console.WriteLine() as your primary production logging mechanism.</p><h4>Understanding the SQL Output</h4><p>Being able to display generated SQL is useful, but the next step is learning how to interpret it. You do not need to become a SQL Server expert immediately. Start by connecting familiar LINQ operations with their SQL equivalents.</p><p>Consider this query:</p><pre>var query = db.Books<br>    .Where(b =&gt; b.PublishedYear &gt;= 2020)<br>    .OrderBy(b =&gt; b.Title)<br>    .Select(b =&gt; new<br>    {<br>        b.Title,<br>        b.PublishedYear<br>    });</pre><p>Its generated SQL may resemble:</p><pre>SELECT [b].[Title], [b].[PublishedYear]<br>FROM [Books] AS [b]<br>WHERE [b].[PublishedYear] &gt;= 2020<br>ORDER BY [b].[Title]</pre><p>Each part corresponds to an operation in the LINQ query:</p><p>LINQ operationSQL equivalentPurposeSelect()SELECTChooses the columns returnedWhere()WHEREFilters recordsOrderBy()ORDER BYSorts the resultsToListAsync()Executes the queryRetrieves the results from the database</p><p>The last row requires a small clarification: ToListAsync() is the operation that triggers execution of this query, not a SQL clause in the generated statement.</p><p>When inspecting generated SQL, look for a few practical details.</p><p><strong>1. Check which columns are selected.</strong></p><p>If your page needs only a book’s title and publication year, the SQL should generally select only those columns. If it retrieves a large description field or other unnecessary columns, consider whether projection would help.</p><p><strong>2. Check where filtering happens.</strong></p><p>A condition expressed through a translatable LINQ Where() operation will normally appear in the SQL WHERE clause. This allows SQL Server to filter records before returning the results to your application. Avoid retrieving an entire table and filtering it in C# when the database can perform the filtering.</p><p><strong>3. Examine joins when accessing related data.</strong></p><p>If your query selects b.Publisher.Name, EF Core may generate a join between the Books and Publishers tables. This is often appropriate, but you should understand which related data the query needs. Selecting a related property does not necessarily require loading the entire related entity.</p><p><strong>4. Look for unnecessary result sets or excessive data retrieval.</strong></p><p>If a page displays a small subset of records, retrieving every record may be inefficient. Consider filtering, pagination, or a more targeted projection. The best solution depends on the page’s requirements.</p><p><strong>5. Remember that generated SQL is only part of performance analysis.</strong></p><p>Even SQL that looks reasonable can run slowly if the database has unsuitable indexes, large volumes of data, or an expensive execution plan. For deeper investigation, SQL Server execution plans and database performance tools provide additional information.</p><p>The goal is not to inspect every SQL statement in every application. Rather, use ToQueryString() when learning EF Core, investigating unexpected query behavior, or diagnosing a performance concern. It gives you a practical way to connect your C# code with the SQL that EF Core generates.</p><p>By understanding both LINQ and its SQL translation, you can make more informed decisions about query design and build Blazor applications that access SQL Server more efficiently.</p><h3>6. Complete Runnable Books.razor</h3><p>The example below includes publisher filtering, asynchronous database access, loading and error states, and the Description field from your existing Book model.</p><pre>@page &quot;/books&quot;<br>@using Microsoft.EntityFrameworkCore<br>@using LibraryEfCore.Data<br>@using Microsoft.JSInterop<br>@inject IJSRuntime JS<br>@inject IDbContextFactory&lt;LibraryDbContext&gt; DbFactory<br>@inject ILogger&lt;Books&gt; Logger<br><br>&lt;PageTitle&gt;Books&lt;/PageTitle&gt;<br>&lt;h1&gt;Library Catalog&lt;/h1&gt;<br>&lt;p&gt;<br>   Browse the books in the library and filter them by publisher.<br>&lt;/p&gt;<br>&lt;p&gt;<br>   &lt;a class=&quot;btn btn-primary&quot; href=&quot;/books/add&quot;&gt;<br>        Add Book<br>   &lt;/a&gt;<br>&lt;/p&gt;<br><br>&lt;div class=&quot;mb-3 d-flex align-items-center gap-3&quot;&gt;<br>   &lt;label for=&quot;publisherFilter&quot;<br>      class=&quot;form-label mb-0 text-nowrap&quot;&gt;<br>      Filter by publisher<br>   &lt;/label&gt;<br><br>   &lt;select id=&quot;publisherFilter&quot;<br>           class=&quot;form-select&quot;<br>           value=&quot;@PublisherFilterValue&quot;<br>           @onchange=&quot;OnPublisherChangedAsync&quot;<br>           disabled=&quot;@isLoading&quot;&gt;<br><br>      &lt;option value=&quot;&quot;&gt;All publishers&lt;/option&gt;<br>      @foreach (var publisher in publishers)<br>      {<br>         &lt;option value=&quot;@publisher.Id&quot;&gt;<br>            @publisher.Name<br>         &lt;/option&gt;<br>      }<br>   &lt;/select&gt;<br>&lt;/div&gt;<br><br>@if (isLoading)<br>{<br>   &lt;p&gt;Loading books...&lt;/p&gt;<br>}<br>else if (!string.IsNullOrWhiteSpace(errorMessage))<br>{<br>   &lt;div class=&quot;alert alert-danger&quot; role=&quot;alert&quot;&gt;<br>      @errorMessage<br>   &lt;/div&gt;<br><br>   &lt;button type=&quot;button&quot;<br>           class=&quot;btn btn-primary&quot;<br>           @onclick=&quot;LoadCatalogAsync&quot;&gt;<br>      Try Again<br>   &lt;/button&gt;<br>}<br>else if (books.Count == 0)<br>{<br>   &lt;p&gt;No books have been added yet.&lt;/p&gt;<br>}<br>else<br>{<br>   &lt;p&gt;<br>      Books found: &lt;strong&gt;@books.Count&lt;/strong&gt;<br>   &lt;/p&gt;<br><br>   &lt;table class=&quot;table&quot;&gt;<br>      &lt;thead&gt;<br>         &lt;tr&gt;<br>            &lt;th&gt;Title&lt;/th&gt;<br>            &lt;th&gt;ISBN&lt;/th&gt;<br>            &lt;th&gt;Publisher&lt;/th&gt;<br>            &lt;th&gt;Published Year&lt;/th&gt;<br>            &lt;th&gt;Description&lt;/th&gt;<br>            &lt;th&gt;Actions&lt;/th&gt;<br>         &lt;/tr&gt;<br>      &lt;/thead&gt;<br>      &lt;tbody&gt;<br>         @foreach (var book in books)<br>         {<br>            &lt;tr&gt;<br>               &lt;td&gt;@book.Title&lt;/td&gt;<br>               &lt;td&gt;@(book.ISBN ?? &quot;-&quot;)&lt;/td&gt;<br>               &lt;td&gt;@(book.PublisherName ?? &quot;Not assigned&quot;)&lt;/td&gt;<br>               &lt;td&gt;@book.PublishedYear&lt;/td&gt;<br>               &lt;td&gt;@(book.Description ?? &quot;-&quot;)&lt;/td&gt;<br>               &lt;td&gt;<br>                  &lt;a class=&quot;btn btn-sm btn-primary me-1 mt-1&quot;<br>                     href=&quot;@($&quot;/books/edit/{book.Id}&quot;)&quot;&gt;<br>                     Edit<br>                  &lt;/a&gt;<br>                  &lt;button class=&quot;btn btn-sm btn-danger mt-1&quot;<br>                     @onclick=&quot;() =&gt; DeleteBookAsync(book.Id)&quot;&gt;<br>                     Delete<br>                  &lt;/button&gt;<br>               &lt;/td&gt;<br>            &lt;/tr&gt;<br>         }<br>      &lt;/tbody&gt;<br>   &lt;/table&gt;<br>}<br><br>@code {<br>   private List&lt;PublisherOption&gt; publishers = new();<br>   private List&lt;BookListItem&gt; books = new();<br>   private int? selectedPublisherId;<br>   private bool isLoading;<br>   private string? errorMessage;<br>   private string PublisherFilterValue =&gt;<br>      selectedPublisherId?.ToString() ?? &quot;&quot;;<br><br>   protected override async Task OnInitializedAsync()<br>   {<br>      await LoadCatalogAsync();<br>   }<br><br>   private async Task LoadCatalogAsync()<br>   {<br>      isLoading = true;<br>      errorMessage = null;<br><br>      try<br>      {<br>         await LoadPublishersCoreAsync();<br>         await LoadBooksCoreAsync();<br>      }<br>      catch (Exception ex)<br>      {<br>         Logger.LogError(<br>             ex,<br>             &quot;Failed to load the library catalog.&quot;);<br><br>         errorMessage =<br>             &quot;Unable to load the library catalog. &quot; +<br>             &quot;Check the database connection and try again.&quot;;<br>      }<br>      finally<br>      {<br>         isLoading = false;<br>      }<br>   }<br><br>   private async Task LoadPublishersCoreAsync()<br>   {<br>      await using var db =<br>         await DbFactory.CreateDbContextAsync();<br><br>      publishers = await db.Publishers<br>         .AsNoTracking()<br>         .OrderBy(p =&gt; p.Name)<br>         .Select(p =&gt; new PublisherOption<br>         {<br>            Id = p.Id,<br>            Name = p.Name<br>         })<br>         .ToListAsync();<br>   }<br><br>   private async Task OnPublisherChangedAsync(<br>       ChangeEventArgs args)<br>   {<br>      var value = args.Value?.ToString();<br><br>      selectedPublisherId =<br>         int.TryParse(value, out var publisherId)<br>            ? publisherId<br>            : null;<br><br>      await LoadBooksAsync();<br>   }<br><br>   private async Task LoadBooksAsync()<br>   {<br>      isLoading = true;<br>      errorMessage = null;<br><br>      try<br>      {<br>         await LoadBooksCoreAsync();<br>      }<br>      catch (Exception ex)<br>      {<br>         Logger.LogError(<br>            ex,<br>            &quot;Failed to load books for publisher {PublisherId}.&quot;,<br>            selectedPublisherId);<br><br>         errorMessage =<br>            &quot;Unable to load books. &quot; +<br>            &quot;Check the database connection and try again.&quot;;<br>      }<br>      finally<br>      {<br>         isLoading = false;<br>      }<br>   }<br><br>   private async Task LoadBooksCoreAsync()<br>   {<br>      await using var db =<br>         await DbFactory.CreateDbContextAsync();<br><br>      var query = db.Books.AsNoTracking();<br><br>      if (selectedPublisherId.HasValue)<br>      {<br>         query = query.Where(<br>            b =&gt; b.PublisherId == selectedPublisherId.Value);<br>      }<br><br>      books = await query<br>         .OrderBy(b =&gt; b.Title)<br>         .Select(b =&gt; new BookListItem<br>         {<br>            Id = b.Id,<br>            Title = b.Title,<br>            Description = b.Description,<br>            ISBN = b.ISBN,<br>            PublishedYear = b.PublishedYear,<br>            PublisherName = b.Publisher != null<br>               ? b.Publisher.Name<br>               : &quot;Not assigned&quot;<br>         })<br>         .ToListAsync();<br>   }<br><br>   private async Task DeleteBookAsync(int id)<br>   {<br>      var confirmed = await JS.InvokeAsync&lt;bool&gt;(<br>         &quot;confirm&quot;,<br>         &quot;Are you sure you want to delete this book?&quot;);<br><br>      if (!confirmed)<br>      {<br>         return;<br>      }<br><br>      isLoading = true;<br>      errorMessage = null;<br><br>      try<br>      {<br>         await using var db =<br>            await DbFactory.CreateDbContextAsync();<br><br>         var book = await db.Books.FindAsync(id);<br><br>         if (book is null)<br>         {<br>            errorMessage =<br>               &quot;The book could not be found. &quot; +<br>               &quot;The catalog will be refreshed.&quot;;<br><br>            await LoadBooksCoreAsync();<br>            return;<br>         }<br><br>         db.Books.Remove(book);<br>         await db.SaveChangesAsync();<br><br>         // Refresh the catalog after deletion.<br>         await LoadBooksCoreAsync();<br>      }<br>      catch (Exception ex)<br>      {<br>         Logger.LogError(<br>            ex,<br>            &quot;Failed to delete book with ID {BookId}.&quot;,<br>            id);<br><br>         errorMessage =<br>            &quot;Unable to delete the book. &quot; +<br>            &quot;The database may be unavailable, &quot; +<br>            &quot;or the book may be referenced by another record.&quot;;<br>      }<br>      finally<br>      {<br>         isLoading = false;<br>      }<br>   }<br><br>   private sealed class PublisherOption<br>   {<br>      public int Id { get; set; }<br>      public string Name { get; set; } = &quot;&quot;;<br>   }<br><br>   private sealed class BookListItem<br>   {<br>      public int Id { get; set; }<br>      public string Title { get; set; } = &quot;&quot;;<br>      public string? Description { get; set; }<br>      public string? ISBN { get; set; }<br>      public int PublishedYear { get; set; }<br>      public string PublisherName { get; set; } = &quot;&quot;;<br>   }<br>}</pre><h4>A. Understanding @inject ILogger&lt;Books&gt; Logger</h4><pre>@inject ILogger&lt;Books&gt; Logger</pre><p>This directive injects an instance of ILogger&lt;Books&gt; into the Books Razor component. Logging is a built-in feature of ASP.NET Core that allows an application to record information about its execution, including warnings, errors, and diagnostic details.</p><p>Let’s break down the declaration:</p><ul><li>@inject is a Razor directive that makes a service available to the component through dependency injection.</li><li>ILogger&lt;Books&gt; is a logging service associated with the Books component. The generic type Books identifies the category used for log messages, making it easier to determine which component produced a message.</li><li>Logger is the variable name used to access the logging service in the component&#39;s C# code.</li></ul><p>For example, the following statement records an error when loading the library catalog fails:</p><pre>catch (Exception ex)<br>{<br>    Logger.LogError(<br>        ex,<br>        &quot;Failed to load the library catalog.&quot;);<br><br>errorMessage =<br>        &quot;Unable to load the library catalog. &quot; +<br>        &quot;Check the database connection and try again.&quot;;<br>}</pre><p>The call to Logger.LogError() records the exception and its details through the configured logging providers. During development, these details can help you investigate problems such as database connection failures, SQL errors, or unexpected application exceptions.</p><p>Notice that the application also assigns a message to errorMessage. This message is intended for the user, whereas the log entry is intended primarily for developers and administrators.</p><p>Why use both?</p><p>Displaying the full exception to users is generally undesirable because technical details may be confusing and could reveal sensitive implementation information. Logging the exception preserves diagnostic information without exposing it directly in the interface.</p><p>ASP.NET Core provides logging infrastructure by default, so you do not need to install an additional logging package for this example.</p><h4>B. Understanding errorMessage</h4><p>In the Books.razor component, we declare:</p><pre>private string? errorMessage;</pre><p>This declaration creates a private field that stores an error message to be displayed when a database operation fails.</p><p>Let’s examine its parts:</p><ul><li>private means the field is accessible only within the component&#39;s C# code.</li><li>string specifies that the field stores text.</li><li>? indicates that the string may be null.</li><li>errorMessage is the field&#39;s name.</li></ul><p>Because the field is not initialized explicitly, its initial value is null. This is appropriate because no error message is needed when the page first loads successfully.</p><p><strong>How is </strong><strong>errorMessage used?</strong></p><p>Consider this Razor markup:</p><pre>@if (!string.IsNullOrWhiteSpace(errorMessage))<br>{<br>    &lt;div class=&quot;alert alert-danger&quot; role=&quot;alert&quot;&gt;<br>        @errorMessage<br>    &lt;/div&gt;<br>}</pre><p>The expression string.IsNullOrWhiteSpace(errorMessage) checks whether the string is null, empty, or contains only whitespace. The ! operator reverses the result, so the block is rendered only when a meaningful error message exists.</p><p>For example, if a database operation fails, the component can assign:</p><pre>errorMessage =<br>    &quot;Unable to load the library catalog. &quot; +<br>    &quot;Check the database connection and try again.&quot;;</pre><p>Blazor then renders the message inside a Bootstrap alert element. The alert alert-danger classes give the message the familiar red error styling, while role=&quot;alert&quot; helps assistive technologies recognize the message as an alert.</p><p>When the operation starts again, the component clears the previous message:</p><pre>errorMessage = null;</pre><p>This prevents an old error from remaining visible after a new attempt begins.</p><p><strong>How does this relate to Blazor rendering?</strong></p><p>Blazor components automatically render again when their event handlers or lifecycle methods update component state in the normal Blazor execution flow. Therefore, when errorMessage changes during an event handler, the component can update its displayed markup without manually manipulating the DOM.</p><p>This is an important difference from traditional JavaScript code, where a developer might need to locate an HTML element and update its contents directly.</p><h4>C. Understanding isLoading</h4><p>The component also declares:</p><pre>private bool isLoading;</pre><p>This Boolean field indicates whether a database operation is currently in progress. For example:</p><pre>private async Task LoadBooksAsync()<br>{<br>    isLoading = true;<br>    errorMessage = null;<br><br>    try<br>    {<br>        await LoadBooksCoreAsync();<br>    }<br>    catch (Exception ex)<br>    {<br>        Logger.LogError(<br>            ex,<br>            &quot;Failed to load books.&quot;);<br>        errorMessage = &quot;Unable to load books.&quot;;<br>    }<br>    finally<br>    {<br>        isLoading = false;<br>    }<br>}</pre><p>The method sets isLoading to true before starting the operation and resets it to false in the finally block. The finally block executes whether the operation succeeds or throws an exception.</p><p>The markup can use this state to display a loading message:</p><pre>@if (isLoading)<br>{<br>    &lt;p&gt;Loading books...&lt;/p&gt;<br>}</pre><p>It can also disable a button while the operation is running:</p><pre>&lt;button disabled=&quot;@isLoading&quot;&gt;<br>    Delete<br>&lt;/button&gt;</pre><p>This improves the user experience by providing feedback and helping prevent repeated clicks during an operation.</p><h4>D. How These Three Pieces Work Together</h4><p>The three fields and services have different responsibilities:</p><pre><a href="http://_"><strong>Name</strong></a><strong>          </strong><a href="http://_"><strong>Purpose</strong></a><strong><br></strong><br>Logger        Records diagnostic information for<br>              developers and administrators.<br><br>errorMessage  Stores a message that can be displayed<br>              to the user when an operation fails.<br><br>isLoading     Indicates whether an operation is in progress.</pre><p>For a typical database operation, the flow is:</p><ol><li>Start the operation<br>Set isLoading = true and clear the previous error.</li><li>Execute the database query<br>Retrieve or modify data asynchronously.<br>Success: Display the results and refresh the relevant data.<br>Failure: Log the exception and set errorMessage.</li><li>Finish the operation<br>Set isLoading = false in the finally block.</li></ol><p>Together, these mechanisms make a Blazor page easier to use and easier to troubleshoot. They are not required for every simple demonstration, but they are valuable when building a more robust application.</p><p><strong>Important Notes</strong></p><ul><li>No Include() is needed. The query projects the publisher name directly into BookListItem. EF Core retrieves the selected values without materializing complete Book and Publisher entities.</li><li>Filtering happens in SQL. Selecting a publisher adds a Where() condition before ToListAsync() executes the query.</li><li>AsNoTracking() suits this read-only page. The component displays results rather than editing the retrieved entities.</li><li>Each operation uses a short-lived context. The context is disposed after the query finishes.</li><li>The UI handles common states. Readers can see loading feedback, an empty result, or a database error.</li></ul><h4>E. Test It in Visual Studio</h4><ul><li>Build and run the project. From the Home page, click Manage Books link. The page should display:</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*TCkuPED_XV3d2MudySpuSA.png" /></figure><ul><li>Filter by publisher: for example, select Pearson Education in the dropdown list.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*cEQ_dxZVoTdEP3Q3U2q1SQ.png" /></figure><ul><li>Continue the test. You can click Add Book, Edit, or Delete.</li></ul><h3>Conclusion</h3><p>Entity Framework Core does more than map C# objects to database tables. It gives us tools to control how data is loaded, how queries are composed, and how much information is returned from SQL Server.</p><p>Include() remains valuable when related entities are needed. Explicit loading can help when related data is required only under certain conditions, while lazy loading deserves care because it can introduce hidden database operations.</p><p>LINQ projections and aggregate operations offer another important improvement: instead of retrieving entire entities for every task, we can ask the database for precisely the information the application needs. Short-lived contexts, asynchronous queries, and tools such as ToQueryString() help keep the data-access layer easier to reason about.</p><p>The central lesson is straightforward: <strong>good EF Core code is not about writing the fewest lines. It is about expressing the data requirement clearly and retrieving that data efficiently.</strong></p><p>In the next article, we will build on these ideas by exploring LINQ filtering, sorting, and searching in greater depth, turning the library catalog into a more interactive application.</p><p>The journey will then look like this:</p><p><a href="https://medium.com/informatics/ef-core-blazor-web-connecting-a-net-web-app-to-sql-server-e49345c63ab3">Article 1 — Connecting a Blazor Web App to SQL Server</a>.<br><a href="https://medium.com/informatics/ef-core-blazor-web-configure-fluent-api-and-migrations-to-sql-server-database-05dba312321d">Article 2 — Configuring the model with Fluent API and migrations</a>.<br><a href="https://medium.com/informatics/ef-core-blazor-web-the-ultimate-crud-blueprint-e2847e98d7b6">Article 3 — Implementing CRUD with EF Core.<br></a><a href="https://medium.com/informatics/ef-core-blazor-web-one-to-many-relationships-f071ba3d4f43">Article 4 — Modeling relationships between publishers and books</a>.<br>Article 5 — Loading related data and writing more advanced queries.<br>Article 6 — LINQ queries: filtering, sorting, and searching data</p><p><strong>Your turn:</strong> In your own EF Core projects, do you prefer Include() or projection for read-only pages? Have you encountered unexpected database queries or performance issues? Share your experience in the comments, and if this article helped you understand EF Core data loading, consider sharing it with another .NET developer.</p><h3>References</h3><ol><li>Microsoft. <em>Efficient Querying</em>. Microsoft Learn. <a href="https://learn.microsoft.com/en-us/ef/core/performance/efficient-querying">https://learn.microsoft.com/en-us/ef/core/performance/efficient-querying</a></li><li>Microsoft. <em>Loading Related Data</em>. Microsoft Learn. <a href="https://learn.microsoft.com/en-us/ef/core/querying/related-data/">https://learn.microsoft.com/en-us/ef/core/querying/related-data/</a></li><li>Microsoft. <em>Tracking vs. No-Tracking Queries</em>. Microsoft Learn. <a href="https://learn.microsoft.com/en-us/ef/core/querying/tracking">https://learn.microsoft.com/en-us/ef/core/querying/tracking</a></li><li>Microsoft. <em>Complex Query Operators</em>. Microsoft Learn. <a href="https://learn.microsoft.com/en-us/ef/core/querying/complex-query-operators">https://learn.microsoft.com/en-us/ef/core/querying/complex-query-operators</a></li></ol><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=fafd2c308979" width="1" height="1" alt=""><hr><p><a href="https://medium.com/informatics/ef-core-blazor-5-writing-smarter-queries-loading-data-efficiently-fafd2c308979">EF Core &amp; Blazor #5: Writing Smarter Queries &amp; Loading Data Efficiently</a> was originally published in <a href="https://medium.com/informatics">Informatics</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[EF Core & Blazor #4: Modeling 1:N Relationships with Fluent API]]></title>
            <description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://medium.com/informatics/ef-core-blazor-web-one-to-many-relationships-f071ba3d4f43?source=rss----191053c6302a---4"><img src="https://cdn-images-1.medium.com/max/1125/1*f7PlxkcJQdBfd4gUOXSLwQ.png" width="1125"></a></p><p class="medium-feed-snippet">Learn how to configure a one-to-many relationship between publishers and books using Entity Framework Core, Fluent API, migrations&#x2026;</p><p class="medium-feed-link"><a href="https://medium.com/informatics/ef-core-blazor-web-one-to-many-relationships-f071ba3d4f43?source=rss----191053c6302a---4">Continue reading on Informatics »</a></p></div>]]></description>
            <link>https://medium.com/informatics/ef-core-blazor-web-one-to-many-relationships-f071ba3d4f43?source=rss----191053c6302a---4</link>
            <guid isPermaLink="false">https://medium.com/p/f071ba3d4f43</guid>
            <category><![CDATA[csharp]]></category>
            <category><![CDATA[one-to-many-relationship]]></category>
            <category><![CDATA[entity-framework-core]]></category>
            <category><![CDATA[blazor]]></category>
            <category><![CDATA[relational-databases]]></category>
            <dc:creator><![CDATA[M. Ramadhan]]></dc:creator>
            <pubDate>Tue, 20 Oct 2026 09:26:41 GMT</pubDate>
            <atom:updated>2026-10-26T13:45:38.841Z</atom:updated>
        </item>
        <item>
            <title><![CDATA[EF Core & Blazor Web #3: The Ultimate CRUD Blueprint]]></title>
            <description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://medium.com/informatics/ef-core-blazor-web-the-ultimate-crud-blueprint-e2847e98d7b6?source=rss----191053c6302a---4"><img src="https://cdn-images-1.medium.com/max/1072/1*H5mNtLpSqvhGe8H9ofaGGg.png" width="1072"></a></p><p class="medium-feed-snippet">A step-by-step guide to building clean, efficient, and thread-safe database operations in Blazor.</p><p class="medium-feed-link"><a href="https://medium.com/informatics/ef-core-blazor-web-the-ultimate-crud-blueprint-e2847e98d7b6?source=rss----191053c6302a---4">Continue reading on Informatics »</a></p></div>]]></description>
            <link>https://medium.com/informatics/ef-core-blazor-web-the-ultimate-crud-blueprint-e2847e98d7b6?source=rss----191053c6302a---4</link>
            <guid isPermaLink="false">https://medium.com/p/e2847e98d7b6</guid>
            <category><![CDATA[ef-core]]></category>
            <category><![CDATA[relational-databases]]></category>
            <category><![CDATA[blazor]]></category>
            <category><![CDATA[crud]]></category>
            <category><![CDATA[entity-framework-core]]></category>
            <dc:creator><![CDATA[M. Ramadhan]]></dc:creator>
            <pubDate>Wed, 14 Oct 2026 09:41:26 GMT</pubDate>
            <atom:updated>2026-10-26T13:36:49.873Z</atom:updated>
        </item>
        <item>
            <title><![CDATA[EF Core & Blazor Web #2: Configure Fluent API and Migrations to SQL Server Database]]></title>
            <description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://medium.com/informatics/ef-core-blazor-web-configure-fluent-api-and-migrations-to-sql-server-database-05dba312321d?source=rss----191053c6302a---4"><img src="https://cdn-images-1.medium.com/max/1125/1*wKJoNCOf8C8KTFh1xxTlGw.png" width="1125"></a></p><p class="medium-feed-snippet">Learn how to configure entity models with Fluent API, handle nullable properties, and generate database migrations for your Blazor&#x2026;</p><p class="medium-feed-link"><a href="https://medium.com/informatics/ef-core-blazor-web-configure-fluent-api-and-migrations-to-sql-server-database-05dba312321d?source=rss----191053c6302a---4">Continue reading on Informatics »</a></p></div>]]></description>
            <link>https://medium.com/informatics/ef-core-blazor-web-configure-fluent-api-and-migrations-to-sql-server-database-05dba312321d?source=rss----191053c6302a---4</link>
            <guid isPermaLink="false">https://medium.com/p/05dba312321d</guid>
            <category><![CDATA[relational-databases]]></category>
            <category><![CDATA[blazor]]></category>
            <category><![CDATA[fluent-api]]></category>
            <category><![CDATA[entity-framework-core]]></category>
            <category><![CDATA[blazor-server]]></category>
            <dc:creator><![CDATA[M. Ramadhan]]></dc:creator>
            <pubDate>Wed, 07 Oct 2026 15:00:15 GMT</pubDate>
            <atom:updated>2026-10-26T13:43:21.321Z</atom:updated>
        </item>
        <item>
            <title><![CDATA[EF Core & Blazor Web #1: Connecting a .NET Web App to SQL Server]]></title>
            <description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://medium.com/informatics/ef-core-blazor-web-connecting-a-net-web-app-to-sql-server-e49345c63ab3?source=rss----191053c6302a---4"><img src="https://cdn-images-1.medium.com/max/1124/1*AaWa-6NBa6qESrGWoHepsg.png" width="1124"></a></p><p class="medium-feed-snippet">A step-by-step introduction to Entity Framework Core, DbContext, dependency injection, and SQL Server using a Blazor Web App</p><p class="medium-feed-link"><a href="https://medium.com/informatics/ef-core-blazor-web-connecting-a-net-web-app-to-sql-server-e49345c63ab3?source=rss----191053c6302a---4">Continue reading on Informatics »</a></p></div>]]></description>
            <link>https://medium.com/informatics/ef-core-blazor-web-connecting-a-net-web-app-to-sql-server-e49345c63ab3?source=rss----191053c6302a---4</link>
            <guid isPermaLink="false">https://medium.com/p/e49345c63ab3</guid>
            <category><![CDATA[blazor-server]]></category>
            <category><![CDATA[entity-framework-core]]></category>
            <category><![CDATA[blazor]]></category>
            <category><![CDATA[relational-databases]]></category>
            <category><![CDATA[code-first]]></category>
            <dc:creator><![CDATA[M. Ramadhan]]></dc:creator>
            <pubDate>Mon, 05 Oct 2026 09:27:06 GMT</pubDate>
            <atom:updated>2026-10-26T13:35:34.567Z</atom:updated>
        </item>
        <item>
            <title><![CDATA[EF Core: Relationships, Foreign Keys, and Referential Actions (Part 3)]]></title>
            <description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://medium.com/informatics/ef-core-relationships-foreign-keys-and-referential-actions-3aa2ef33c9a3?source=rss----191053c6302a---4"><img src="https://cdn-images-1.medium.com/max/2250/0*tQMZgvqvVwhJ2gVQ" width="2250"></a></p><p class="medium-feed-snippet">Learn how to configure 1:1, 1:N, and M:N relationships, map foreign key constraints, and handle SQL Server cascade behaviors safely.</p><p class="medium-feed-link"><a href="https://medium.com/informatics/ef-core-relationships-foreign-keys-and-referential-actions-3aa2ef33c9a3?source=rss----191053c6302a---4">Continue reading on Informatics »</a></p></div>]]></description>
            <link>https://medium.com/informatics/ef-core-relationships-foreign-keys-and-referential-actions-3aa2ef33c9a3?source=rss----191053c6302a---4</link>
            <guid isPermaLink="false">https://medium.com/p/3aa2ef33c9a3</guid>
            <category><![CDATA[blazor]]></category>
            <category><![CDATA[csharp]]></category>
            <category><![CDATA[entity-framework-core]]></category>
            <category><![CDATA[ef-core]]></category>
            <category><![CDATA[relational-databases]]></category>
            <dc:creator><![CDATA[M. Ramadhan]]></dc:creator>
            <pubDate>Tue, 29 Sep 2026 09:08:28 GMT</pubDate>
            <atom:updated>2026-10-27T03:56:32.336Z</atom:updated>
        </item>
        <item>
            <title><![CDATA[EF Core: Precision Data Mapping to SQL Server Database (Part 2)]]></title>
            <description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://medium.com/informatics/precision-data-mapping-in-ef-core-sql-server-preventing-bloat-and-data-corruption-3c60b4cad471?source=rss----191053c6302a---4"><img src="https://cdn-images-1.medium.com/max/2600/1*DAL6Co9XoO_rZ89cG71sfg.jpeg" width="2768"></a></p><p class="medium-feed-snippet">A comprehensive guide to configuring explicit Fluent API mappings for Strings, Temporal Types, Enums, and Precision Decimals</p><p class="medium-feed-link"><a href="https://medium.com/informatics/precision-data-mapping-in-ef-core-sql-server-preventing-bloat-and-data-corruption-3c60b4cad471?source=rss----191053c6302a---4">Continue reading on Informatics »</a></p></div>]]></description>
            <link>https://medium.com/informatics/precision-data-mapping-in-ef-core-sql-server-preventing-bloat-and-data-corruption-3c60b4cad471?source=rss----191053c6302a---4</link>
            <guid isPermaLink="false">https://medium.com/p/3c60b4cad471</guid>
            <category><![CDATA[relational-databases]]></category>
            <category><![CDATA[ef-core]]></category>
            <category><![CDATA[entity-framework-core]]></category>
            <category><![CDATA[blazor]]></category>
            <category><![CDATA[csharp]]></category>
            <dc:creator><![CDATA[M. Ramadhan]]></dc:creator>
            <pubDate>Thu, 24 Sep 2026 10:08:11 GMT</pubDate>
            <atom:updated>2026-10-27T03:55:55.975Z</atom:updated>
        </item>
        <item>
            <title><![CDATA[EF Core: Choosing the Right Key Strategy for Performance & Scalability (Part 1)]]></title>
            <description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://medium.com/informatics/ef-core-1-choosing-the-right-key-strategy-for-performance-scalability-5f4561ffc259?source=rss----191053c6302a---4"><img src="https://cdn-images-1.medium.com/max/791/0*nE9AwNy9e8lMdeAe.png" width="791"></a></p><p class="medium-feed-snippet">Explore Primary Keys (Surrogate vs. Natural), Alternate Keys, and Real-World Best Practices</p><p class="medium-feed-link"><a href="https://medium.com/informatics/ef-core-1-choosing-the-right-key-strategy-for-performance-scalability-5f4561ffc259?source=rss----191053c6302a---4">Continue reading on Informatics »</a></p></div>]]></description>
            <link>https://medium.com/informatics/ef-core-1-choosing-the-right-key-strategy-for-performance-scalability-5f4561ffc259?source=rss----191053c6302a---4</link>
            <guid isPermaLink="false">https://medium.com/p/5f4561ffc259</guid>
            <category><![CDATA[blazor]]></category>
            <category><![CDATA[entity-framework-core]]></category>
            <category><![CDATA[ef-core]]></category>
            <category><![CDATA[csharp]]></category>
            <category><![CDATA[relational-databases]]></category>
            <dc:creator><![CDATA[M. Ramadhan]]></dc:creator>
            <pubDate>Wed, 23 Sep 2026 09:45:13 GMT</pubDate>
            <atom:updated>2026-10-27T03:55:10.137Z</atom:updated>
        </item>
        <item>
            <title><![CDATA[SQL Server 2025: When “Classic” Databases Begin to Think Like Humans]]></title>
            <description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://medium.com/informatics/sql-server-2025-when-classic-databases-begin-to-think-like-humans-2622cf97d598?source=rss----191053c6302a---4"><img src="https://cdn-images-1.medium.com/max/600/0*YNBG5AhqymzfRgWF.png" width="600"></a></p><p class="medium-feed-snippet">A deep dive into Vector Support integration, generative AI capabilities, and why SQL Server 2025 serves as the new foundation for&#x2026;</p><p class="medium-feed-link"><a href="https://medium.com/informatics/sql-server-2025-when-classic-databases-begin-to-think-like-humans-2622cf97d598?source=rss----191053c6302a---4">Continue reading on Informatics »</a></p></div>]]></description>
            <link>https://medium.com/informatics/sql-server-2025-when-classic-databases-begin-to-think-like-humans-2622cf97d598?source=rss----191053c6302a---4</link>
            <guid isPermaLink="false">https://medium.com/p/2622cf97d598</guid>
            <category><![CDATA[iqp]]></category>
            <category><![CDATA[ai]]></category>
            <category><![CDATA[artificial-intelligence]]></category>
            <category><![CDATA[relational-databases]]></category>
            <category><![CDATA[sql-server]]></category>
            <dc:creator><![CDATA[M. Ramadhan]]></dc:creator>
            <pubDate>Fri, 18 Sep 2026 10:17:09 GMT</pubDate>
            <atom:updated>2026-09-18T10:17:09.314Z</atom:updated>
        </item>
        <item>
            <title><![CDATA[Choosing the Right Database Approach for C# and Blazor Applications]]></title>
            <description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://medium.com/informatics/choosing-the-right-database-approach-for-c-and-blazor-applications-b0bbdf21c427?source=rss----191053c6302a---4"><img src="https://cdn-images-1.medium.com/max/2250/0*4VP-tGTGhLNwYAI9" width="2250"></a></p><p class="medium-feed-snippet">Demystifying Code-First, Database-First, EF Core, ADO.NET, and Dapper to help you pick the right tool for your next project.</p><p class="medium-feed-link"><a href="https://medium.com/informatics/choosing-the-right-database-approach-for-c-and-blazor-applications-b0bbdf21c427?source=rss----191053c6302a---4">Continue reading on Informatics »</a></p></div>]]></description>
            <link>https://medium.com/informatics/choosing-the-right-database-approach-for-c-and-blazor-applications-b0bbdf21c427?source=rss----191053c6302a---4</link>
            <guid isPermaLink="false">https://medium.com/p/b0bbdf21c427</guid>
            <category><![CDATA[blazor]]></category>
            <category><![CDATA[csharp]]></category>
            <category><![CDATA[dapper]]></category>
            <category><![CDATA[relational-databases]]></category>
            <category><![CDATA[entity-framework-core]]></category>
            <dc:creator><![CDATA[M. Ramadhan]]></dc:creator>
            <pubDate>Thu, 17 Sep 2026 09:00:44 GMT</pubDate>
            <atom:updated>2026-09-17T09:00:44.644Z</atom:updated>
        </item>
    </channel>
</rss>