<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Chethan Ramaswamy</title>
    <description>The latest articles on DEV Community by Chethan Ramaswamy (@chethanramaswamy).</description>
    <link>https://dev.to/chethanramaswamy</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3854098%2F1084dc39-c5b0-4f76-b77c-c4063d78d3f5.png</url>
      <title>DEV Community: Chethan Ramaswamy</title>
      <link>https://dev.to/chethanramaswamy</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/chethanramaswamy"/>
    <language>en</language>
    <item>
      <title>Entity Framework Core Performance Optimization: A Practical Guide for .NET Developers</title>
      <dc:creator>Chethan Ramaswamy</dc:creator>
      <pubDate>Sat, 26 Sep 2026 13:55:27 +0000</pubDate>
      <link>https://dev.to/chethanramaswamy/entity-framework-core-performance-optimization-a-practical-guide-for-net-developers-4d35</link>
      <guid>https://dev.to/chethanramaswamy/entity-framework-core-performance-optimization-a-practical-guide-for-net-developers-4d35</guid>
      <description>&lt;h1&gt;
  
  
  Entity Framework Core Performance Optimization: A Practical Guide for .NET Developers
&lt;/h1&gt;

&lt;p&gt;Entity Framework Core (EF Core) simplifies database development by allowing .NET developers to work with strongly typed C# objects and LINQ instead of writing SQL for every database operation.&lt;/p&gt;

&lt;p&gt;However, the convenience of an ORM can sometimes hide expensive operations.&lt;/p&gt;

&lt;p&gt;A LINQ query that looks simple in C# can result in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Unnecessary database round trips&lt;/li&gt;
&lt;li&gt;Large result sets&lt;/li&gt;
&lt;li&gt;Excessive change tracking&lt;/li&gt;
&lt;li&gt;N+1 queries&lt;/li&gt;
&lt;li&gt;Expensive joins&lt;/li&gt;
&lt;li&gt;Unnecessary columns being retrieved&lt;/li&gt;
&lt;li&gt;Poor pagination performance&lt;/li&gt;
&lt;li&gt;Excessive memory usage&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The goal of performance optimization is not to avoid EF Core. It is to understand how EF Core translates and executes queries and then design those queries appropriately.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Good EF Core performance comes from minimizing unnecessary database work, data transfer, object materialization, and application-side processing.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This article walks through the most important EF Core performance considerations, from query execution and related-data loading to CRUD operations, &lt;code&gt;DbContext&lt;/code&gt;, diagnostics, and practical optimization patterns.&lt;/p&gt;




&lt;h2&gt;
  
  
  Contents Snapshot
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
1. Understanding Entity Framework Core Performance

&lt;ul&gt;
&lt;li&gt;How EF Core executes queries&lt;/li&gt;
&lt;li&gt;LINQ-to-SQL translation&lt;/li&gt;
&lt;li&gt;Where EF Core performance bottlenecks occur&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
2. Common EF Core Performance Problems

&lt;ul&gt;
&lt;li&gt;Loading unnecessary data&lt;/li&gt;
&lt;li&gt;Excessive entity tracking&lt;/li&gt;
&lt;li&gt;N+1 queries&lt;/li&gt;
&lt;li&gt;Lazy loading&lt;/li&gt;
&lt;li&gt;Excessive &lt;code&gt;Include()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Premature query execution&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
3. Optimizing EF Core Queries

&lt;ul&gt;
&lt;li&gt;Projection with &lt;code&gt;Select()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;DTO-based queries&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AsNoTracking()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Filtering and sorting&lt;/li&gt;
&lt;li&gt;Avoiding client-side processing&lt;/li&gt;
&lt;li&gt;Inspecting generated SQL&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
4. Managing Related Data

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Include()&lt;/code&gt; and &lt;code&gt;ThenInclude()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Eager loading&lt;/li&gt;
&lt;li&gt;Explicit loading&lt;/li&gt;
&lt;li&gt;Lazy loading&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;AsSplitQuery()&lt;/code&gt; vs single queries&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
5. Pagination and Large Data Sets

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Skip()&lt;/code&gt; and &lt;code&gt;Take()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Keyset pagination&lt;/li&gt;
&lt;li&gt;Efficient querying of large tables&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
6. Insert, Update, and Delete Performance

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;SaveChangesAsync()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Batch operations&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ExecuteUpdateAsync()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ExecuteDeleteAsync()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Handling large data operations&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
7. DbContext Performance

&lt;ul&gt;
&lt;li&gt;DbContext lifetime&lt;/li&gt;
&lt;li&gt;Change tracking&lt;/li&gt;
&lt;li&gt;Connection management&lt;/li&gt;
&lt;li&gt;Async database operations&lt;/li&gt;
&lt;li&gt;Context pooling&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
8. EF Core Performance Patterns and Anti-Patterns

&lt;ul&gt;
&lt;li&gt;Common mistakes&lt;/li&gt;
&lt;li&gt;Recommended patterns&lt;/li&gt;
&lt;li&gt;When to use or avoid specific EF Core features&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
9. Measuring and Diagnosing EF Core Performance

&lt;ul&gt;
&lt;li&gt;Logging&lt;/li&gt;
&lt;li&gt;Generated SQL&lt;/li&gt;
&lt;li&gt;Query timing&lt;/li&gt;
&lt;li&gt;Profiling&lt;/li&gt;
&lt;li&gt;Identifying slow queries&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
10. Practical EF Core Performance Checklist

&lt;ul&gt;
&lt;li&gt;Query checklist&lt;/li&gt;
&lt;li&gt;Tracking checklist&lt;/li&gt;
&lt;li&gt;Loading checklist&lt;/li&gt;
&lt;li&gt;Write-operation checklist&lt;/li&gt;
&lt;li&gt;Production checklist&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;11. Key Takeaways&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  1. Understanding Entity Framework Core Performance
&lt;/h2&gt;

&lt;p&gt;Before optimizing EF Core, it is important to understand what happens when a query is executed.&lt;/p&gt;

&lt;p&gt;Consider this simple query:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;customers&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;dbContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Customers&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsActive&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToListAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At a high level, the execution flow looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;C# LINQ Query
      |
      v
EF Core
      |
      v
Expression Translation
      |
      v
Generated SQL
      |
      v
Database
      |
      v
Result Set
      |
      v
EF Core Materialization
      |
      v
C# Objects
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Several stages can contribute to the overall execution time.&lt;/p&gt;

&lt;h3&gt;
  
  
  How EF Core Executes Queries
&lt;/h3&gt;

&lt;p&gt;When an &lt;code&gt;IQueryable&lt;/code&gt; is built, EF Core does not immediately execute the query.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dbContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Customers&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsActive&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;OrderBy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point, the query has been composed but not necessarily executed.&lt;/p&gt;

&lt;p&gt;Execution occurs when a terminal operation is called:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;customers&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToListAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Other examples include:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FirstAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SingleAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CountAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AnyAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This distinction is important because adding filters and projections before query execution allows EF Core to translate more of the work into SQL.&lt;/p&gt;

&lt;h3&gt;
  
  
  LINQ-to-SQL Translation
&lt;/h3&gt;

&lt;p&gt;Consider:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;customers&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;dbContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Customers&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsActive&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToListAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Conceptually, EF Core translates the LINQ expression into SQL similar to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="n"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Name&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;Customers&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;IsActive&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The exact SQL depends on the EF Core version, provider, model configuration, and query.&lt;/p&gt;

&lt;p&gt;This means developers should understand both sides:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;LINQ
  |
  v
EF Core Translation
  |
  v
SQL
  |
  v
Database Execution
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A query that looks efficient in C# is not necessarily efficient after translation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Where EF Core Performance Bottlenecks Occur
&lt;/h3&gt;

&lt;p&gt;Potential bottlenecks include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Query translation&lt;/li&gt;
&lt;li&gt;Database execution&lt;/li&gt;
&lt;li&gt;Network transfer&lt;/li&gt;
&lt;li&gt;Entity materialization&lt;/li&gt;
&lt;li&gt;Change tracking&lt;/li&gt;
&lt;li&gt;Application-side processing&lt;/li&gt;
&lt;li&gt;Excessive database round trips&lt;/li&gt;
&lt;li&gt;Large result sets&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Therefore:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Do not optimize EF Core based only on how the C# code looks. Measure what the application and database actually do.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  2. Common EF Core Performance Problems
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Loading Unnecessary Data
&lt;/h2&gt;

&lt;p&gt;One of the most common problems is retrieving more data than the application needs.&lt;/p&gt;

&lt;p&gt;Consider:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;customers&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;dbContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Customers&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsActive&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToListAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Suppose the &lt;code&gt;Customer&lt;/code&gt; entity contains:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Id&lt;/li&gt;
&lt;li&gt;Name&lt;/li&gt;
&lt;li&gt;Email&lt;/li&gt;
&lt;li&gt;Phone&lt;/li&gt;
&lt;li&gt;Address&lt;/li&gt;
&lt;li&gt;ProfileImage&lt;/li&gt;
&lt;li&gt;Preferences&lt;/li&gt;
&lt;li&gt;Audit fields&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If the API only needs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Id
Name
Email
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;loading the entire entity is unnecessary.&lt;/p&gt;

&lt;p&gt;This can increase:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Database I/O&lt;/li&gt;
&lt;li&gt;Network traffic&lt;/li&gt;
&lt;li&gt;Memory usage&lt;/li&gt;
&lt;li&gt;Entity materialization&lt;/li&gt;
&lt;li&gt;Serialization cost&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Projection is usually a better approach for read-only API scenarios.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;customers&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;dbContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Customers&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsActive&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;CustomerDto&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Email&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Email&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToListAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Only the required fields are requested by the query.&lt;/p&gt;




&lt;h2&gt;
  
  
  Excessive Entity Tracking
&lt;/h2&gt;

&lt;p&gt;EF Core uses change tracking to detect modifications to entities.&lt;/p&gt;

&lt;p&gt;This is useful when updating data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;customer&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;dbContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Customers&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FirstAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="n"&gt;customerId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"John"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;dbContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SaveChangesAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;EF Core tracks the customer and can determine what changed.&lt;/p&gt;

&lt;p&gt;For read-only scenarios, tracking may not be necessary.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;customers&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;dbContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Customers&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AsNoTracking&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsActive&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToListAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;AsNoTracking()&lt;/code&gt; tells EF Core not to track the returned entities for changes.&lt;/p&gt;

&lt;p&gt;This can reduce tracking overhead for appropriate read-heavy workloads.&lt;/p&gt;

&lt;p&gt;However, it should not be added blindly everywhere.&lt;/p&gt;

&lt;p&gt;Ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Does this query need to modify the returned entities?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If the answer is no, no-tracking may be appropriate.&lt;/p&gt;




&lt;h2&gt;
  
  
  The N+1 Query Problem
&lt;/h2&gt;

&lt;p&gt;The N+1 query problem occurs when an application executes one query to retrieve a collection and then performs additional queries for each item.&lt;/p&gt;

&lt;p&gt;Consider:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;dbContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Orders&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToListAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Customer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Depending on the relationship-loading configuration, this can result in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1 query  -&amp;gt; Orders

N queries -&amp;gt; Customers
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If there are 5,000 orders, the application could potentially perform thousands of additional database operations.&lt;/p&gt;

&lt;p&gt;Even when individual queries are fast, the combined overhead can become significant.&lt;/p&gt;

&lt;h3&gt;
  
  
  Better Approach
&lt;/h3&gt;

&lt;p&gt;Projection can retrieve the required related data as part of the query:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;dbContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Orders&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;OrderDto&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Amount&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;CustomerName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Customer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToListAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important principle is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Minimize unnecessary database round trips.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Lazy Loading
&lt;/h2&gt;

&lt;p&gt;Lazy loading retrieves related data when a navigation property is accessed.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;dbContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Orders&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FirstAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="n"&gt;orderId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;customerName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Customer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With lazy loading enabled, accessing &lt;code&gt;Customer&lt;/code&gt; can trigger another database query.&lt;/p&gt;

&lt;p&gt;This can be convenient, but it can also hide database operations inside ordinary property access.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Customer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What looks like a simple loop may result in many database calls.&lt;/p&gt;

&lt;h3&gt;
  
  
  Practical Consideration
&lt;/h3&gt;

&lt;p&gt;Lazy loading can be useful in some scenarios, but it should be used carefully in performance-sensitive applications.&lt;/p&gt;

&lt;p&gt;For API workloads, explicit query shaping and projection often make database access easier to understand and control.&lt;/p&gt;




&lt;h2&gt;
  
  
  Excessive &lt;code&gt;Include()&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;Include()&lt;/code&gt; is useful when related entities are genuinely required.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;dbContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Orders&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Include&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Customer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToListAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, adding many relationships to a query can result in complex SQL and large result sets.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;dbContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Orders&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Include&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Customer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Include&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Items&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Include&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Payments&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Include&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Shipments&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToListAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This may result in a large query and repeated data across joined rows.&lt;/p&gt;

&lt;p&gt;If the API only needs selected fields, projection is often clearer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;dbContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Orders&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;OrderSummaryDto&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Amount&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;CustomerName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Customer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;ItemCount&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Count&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToListAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use &lt;code&gt;Include()&lt;/code&gt; when you actually need entity graphs.&lt;/p&gt;

&lt;p&gt;Use projection when you need a specific result shape.&lt;/p&gt;




&lt;h2&gt;
  
  
  Premature Query Execution
&lt;/h2&gt;

&lt;p&gt;Consider:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;customers&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;dbContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Customers&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToListAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;activeCustomers&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;customers&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsActive&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToList&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The entire customer table is loaded into memory before filtering.&lt;/p&gt;

&lt;p&gt;A better approach is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;activeCustomers&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;dbContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Customers&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsActive&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToListAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the filtering happens in the database.&lt;/p&gt;

&lt;p&gt;A useful rule is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Keep the query as &lt;code&gt;IQueryable&lt;/code&gt; while composing database operations, and execute it only when you actually need the results.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  3. Optimizing EF Core Queries
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Projection with &lt;code&gt;Select()&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Projection is one of the most useful EF Core optimization techniques.&lt;/p&gt;

&lt;p&gt;Instead of retrieving complete entities:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;dbContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Products&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsActive&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToListAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;project only the required fields:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;dbContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Products&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsActive&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;ProductDto&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Price&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Price&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToListAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This reduces the amount of data transferred from the database.&lt;/p&gt;

&lt;p&gt;It can also reduce application memory consumption and object materialization.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why Projection Matters
&lt;/h3&gt;

&lt;p&gt;Consider an entity with 30 columns.&lt;/p&gt;

&lt;p&gt;If an API requires only 4 columns:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Entity
  |
  +-- 30 columns
  |
  v
Database
  |
  v
Application
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Projection changes this to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Entity
  |
  +-- 4 required columns
  |
  v
Database
  |
  v
Application
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This becomes increasingly important as data volume grows.&lt;/p&gt;




&lt;h2&gt;
  
  
  DTO-Based Queries
&lt;/h2&gt;

&lt;p&gt;Using DTOs also creates a clear boundary between persistence models and API contracts.&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CustomerDto&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;Id&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;init&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;init&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Empty&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Email&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;init&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Empty&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Query:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;customers&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;dbContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Customers&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AsNoTracking&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsActive&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;CustomerDto&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Email&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Email&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToListAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This approach has several advantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Explicit data requirements&lt;/li&gt;
&lt;li&gt;Smaller result sets&lt;/li&gt;
&lt;li&gt;Reduced coupling&lt;/li&gt;
&lt;li&gt;Clear API contracts&lt;/li&gt;
&lt;li&gt;Less unnecessary entity materialization&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  &lt;code&gt;AsNoTracking()&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;For read-only queries:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;dbContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Products&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AsNoTracking&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsActive&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToListAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use tracking when you need to modify the entity:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;product&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;dbContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Products&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FirstAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="n"&gt;productId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Price&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;100&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;dbContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SaveChangesAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use no-tracking when the result is simply being read:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GET API
  |
  v
Query database
  |
  v
Return response
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The choice should be based on the query's behavior, not applied as a blanket rule.&lt;/p&gt;




&lt;h2&gt;
  
  
  Filtering and Sorting
&lt;/h2&gt;

&lt;p&gt;Filtering should generally happen in the database rather than after materialization.&lt;/p&gt;

&lt;p&gt;Avoid:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;customers&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;dbContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Customers&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToListAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;customers&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Country&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"India"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;OrderBy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToList&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Prefer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;dbContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Customers&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Country&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"India"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;OrderBy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToListAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This allows the database to perform the filtering and sorting.&lt;/p&gt;

&lt;p&gt;For large datasets, database-side operations are generally preferable to loading unnecessary rows into application memory.&lt;/p&gt;




&lt;h2&gt;
  
  
  Avoiding Client-Side Processing
&lt;/h2&gt;

&lt;p&gt;A common performance problem occurs when developers unintentionally move processing from the database into application memory.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;customers&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;dbContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Customers&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToListAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;customers&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;StartsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"A"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToList&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;dbContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Customers&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;StartsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"A"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToListAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The database can process the filtering before returning the result.&lt;/p&gt;

&lt;p&gt;The general pattern is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Avoid:

Database
   |
   v
Large result set
   |
   v
Application filtering


Prefer:

Database
   |
   +-- Filter
   +-- Sort
   +-- Project
   |
   v
Small result set
   |
   v
Application
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Inspecting Generated SQL
&lt;/h2&gt;

&lt;p&gt;Do not assume that a LINQ query generates the SQL you expect.&lt;/p&gt;

&lt;p&gt;EF Core provides &lt;code&gt;ToQueryString()&lt;/code&gt; for inspecting the SQL representation of a query.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dbContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Customers&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsActive&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;CustomerDto&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;sql&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToQueryString&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is useful when diagnosing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Unexpected joins&lt;/li&gt;
&lt;li&gt;Missing filters&lt;/li&gt;
&lt;li&gt;Excessive columns&lt;/li&gt;
&lt;li&gt;Complex queries&lt;/li&gt;
&lt;li&gt;Unexpected query shapes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For production diagnosis, application logging and database monitoring should also be used rather than relying only on &lt;code&gt;ToQueryString()&lt;/code&gt;.&lt;/p&gt;




&lt;h1&gt;
  
  
  4. Managing Related Data
&lt;/h1&gt;

&lt;p&gt;Relationships are one of the areas where EF Core can generate unexpected database work.&lt;/p&gt;

&lt;p&gt;Consider:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Order
 |
 +-- Customer
 |
 +-- OrderItems
 |
 +-- Payments
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The correct loading strategy depends on what the application actually needs.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;code&gt;Include()&lt;/code&gt; and &lt;code&gt;ThenInclude()&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;Include()&lt;/code&gt; loads related data.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;dbContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Orders&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Include&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Customer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToListAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For nested relationships:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;dbContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Orders&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Include&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Customer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ThenInclude&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Address&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToListAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use this when the related entities are actually required.&lt;/p&gt;

&lt;p&gt;Avoid adding &lt;code&gt;Include()&lt;/code&gt; simply because the relationship exists.&lt;/p&gt;




&lt;h2&gt;
  
  
  Eager Loading
&lt;/h2&gt;

&lt;p&gt;Eager loading retrieves related data as part of the query.&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;dbContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Orders&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Include&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Customer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToListAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Advantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Explicit&lt;/li&gt;
&lt;li&gt;Easy to understand&lt;/li&gt;
&lt;li&gt;Avoids some hidden database calls&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Potential downside:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Large entity graphs can produce complex queries and large result sets&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For read-only APIs, projection can often provide a more precise result shape.&lt;/p&gt;




&lt;h2&gt;
  
  
  Explicit Loading
&lt;/h2&gt;

&lt;p&gt;Explicit loading allows the application to deliberately load related data.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;dbContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Orders&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FirstAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="n"&gt;orderId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;dbContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Entry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Reference&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Customer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LoadAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives the application explicit control over when related data is loaded.&lt;/p&gt;

&lt;p&gt;It can be useful when the related data is conditionally required.&lt;/p&gt;

&lt;p&gt;However, repeated explicit loading inside loops can still create N+1 behavior.&lt;/p&gt;




&lt;h2&gt;
  
  
  Lazy Loading
&lt;/h2&gt;

&lt;p&gt;Lazy loading loads related entities when they are accessed.&lt;/p&gt;

&lt;p&gt;Although convenient, it can make database activity less visible in application code.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Customer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The loop does not visibly contain a database query, but accessing &lt;code&gt;Customer&lt;/code&gt; can trigger one when lazy loading is enabled.&lt;/p&gt;

&lt;p&gt;For performance-sensitive applications, make database access explicit whenever practical.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;code&gt;AsSplitQuery()&lt;/code&gt; vs Single Queries
&lt;/h2&gt;

&lt;p&gt;Consider a query with multiple collection relationships:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;dbContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Orders&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Include&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Items&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Include&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Payments&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToListAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A single-query approach can produce large joins.&lt;/p&gt;

&lt;p&gt;EF Core supports split queries:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;dbContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Orders&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Include&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Items&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Include&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Payments&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AsSplitQuery&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToListAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Conceptually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Single Query

Orders
  |
  +-- Items
  |
  +-- Payments
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;versus:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Split Query

Query 1 -&amp;gt; Orders
Query 2 -&amp;gt; Items
Query 3 -&amp;gt; Payments
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Split queries can reduce some join-related duplication, but they also mean multiple database round trips.&lt;/p&gt;

&lt;p&gt;Therefore:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Choose between single and split queries based on the actual query shape and measured performance.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  5. Pagination and Large Data Sets
&lt;/h1&gt;

&lt;p&gt;Returning thousands or millions of rows from an API is rarely appropriate.&lt;/p&gt;

&lt;p&gt;Pagination limits the amount of data processed and returned.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;code&gt;Skip()&lt;/code&gt; and &lt;code&gt;Take()&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;A common approach is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;customers&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;dbContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Customers&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;OrderBy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Skip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="n"&gt;pageSize&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Take&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pageSize&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToListAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Page size = 50
Page      = 2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The query retrieves the next 50 records after the previous 100 records.&lt;/p&gt;

&lt;p&gt;This approach is simple and useful for many applications.&lt;/p&gt;

&lt;p&gt;However, very large offsets can become increasingly expensive because the database may need to process rows that are skipped.&lt;/p&gt;




&lt;h2&gt;
  
  
  Keyset Pagination
&lt;/h2&gt;

&lt;p&gt;For large datasets, keyset pagination can be an alternative.&lt;/p&gt;

&lt;p&gt;Suppose the last item from the previous page has:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Id = 50000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The next query can be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;customers&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;dbContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Customers&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;lastCustomerId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;OrderBy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Take&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToListAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Conceptually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Previous page
     |
     v
Last Id = 50000
     |
     v
WHERE Id &amp;gt; 50000
     |
     v
Take 50
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This approach is especially useful for large, sequential datasets.&lt;/p&gt;

&lt;h3&gt;
  
  
  When to Consider Keyset Pagination
&lt;/h3&gt;

&lt;p&gt;Keyset pagination is useful when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The dataset is large&lt;/li&gt;
&lt;li&gt;Users navigate sequentially&lt;/li&gt;
&lt;li&gt;A stable ordering key exists&lt;/li&gt;
&lt;li&gt;Very deep pages are expected&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Offset pagination can still be appropriate when users need direct access to arbitrary pages.&lt;/p&gt;




&lt;h1&gt;
  
  
  6. Insert, Update, and Delete Performance
&lt;/h1&gt;

&lt;p&gt;EF Core performance is not limited to queries.&lt;/p&gt;

&lt;p&gt;Write operations can also become expensive when large numbers of entities are involved.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;code&gt;SaveChangesAsync()&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Avoid calling &lt;code&gt;SaveChangesAsync()&lt;/code&gt; inside every iteration.&lt;/p&gt;

&lt;h3&gt;
  
  
  Inefficient
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;customer&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;customers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsActive&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;dbContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SaveChangesAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This can create many database round trips.&lt;/p&gt;

&lt;h3&gt;
  
  
  Better
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;customer&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;customers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsActive&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;dbContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SaveChangesAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This reduces the number of explicit save operations.&lt;/p&gt;

&lt;p&gt;However, for very large datasets, loading and tracking every entity can still be expensive.&lt;/p&gt;

&lt;p&gt;That is where set-based operations become useful.&lt;/p&gt;




&lt;h2&gt;
  
  
  Batch Operations
&lt;/h2&gt;

&lt;p&gt;When processing large numbers of entities, consider the size of the batch.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1,000,000 records
      |
      v
Process in manageable batches
      |
      +-- Batch 1
      +-- Batch 2
      +-- Batch 3
      +-- ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Batching can help control:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Memory consumption&lt;/li&gt;
&lt;li&gt;Transaction size&lt;/li&gt;
&lt;li&gt;Change tracker size&lt;/li&gt;
&lt;li&gt;Execution time&lt;/li&gt;
&lt;li&gt;Database pressure&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The appropriate batch size depends on the workload and should be validated through testing.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;code&gt;ExecuteUpdateAsync()&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;For operations that can be expressed as a set-based update, &lt;code&gt;ExecuteUpdateAsync()&lt;/code&gt; can avoid loading entities into memory.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;dbContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Customers&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsActive&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ExecuteUpdateAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;setters&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="n"&gt;setters&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsArchived&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Conceptually, the operation becomes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Application
     |
     v
UPDATE statement
     |
     v
Database
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;rather than:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Database
     |
     v
Load entities
     |
     v
Application
     |
     v
Track entities
     |
     v
Modify entities
     |
     v
Save changes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For large set-based updates, this can substantially reduce application-side work.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;code&gt;ExecuteDeleteAsync()&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Similarly, records can be deleted directly without loading every entity.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;dbContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Customers&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsInactive&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ExecuteDeleteAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is useful when the business operation is simply:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Delete all records matching condition
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;rather than:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Load each entity
  |
  v
Run application logic
  |
  v
Delete entities
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before using set-based operations, consider whether entity-level business rules, events, auditing, or other application logic need to run.&lt;/p&gt;




&lt;h2&gt;
  
  
  Handling Large Data Operations
&lt;/h2&gt;

&lt;p&gt;For very large workloads, consider:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Batching&lt;/li&gt;
&lt;li&gt;Set-based operations&lt;/li&gt;
&lt;li&gt;Appropriate transaction boundaries&lt;/li&gt;
&lt;li&gt;Memory consumption&lt;/li&gt;
&lt;li&gt;Database throughput&lt;/li&gt;
&lt;li&gt;Error recovery&lt;/li&gt;
&lt;li&gt;Idempotency&lt;/li&gt;
&lt;li&gt;Retry behavior&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The right strategy depends on whether the operation is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;User-driven&lt;/li&gt;
&lt;li&gt;Scheduled&lt;/li&gt;
&lt;li&gt;Background processing&lt;/li&gt;
&lt;li&gt;Bulk migration&lt;/li&gt;
&lt;li&gt;Data correction&lt;/li&gt;
&lt;li&gt;Real-time processing&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  7. DbContext Performance
&lt;/h1&gt;

&lt;p&gt;&lt;code&gt;DbContext&lt;/code&gt; is central to EF Core applications.&lt;/p&gt;

&lt;p&gt;Understanding its lifecycle and behavior is important for performance and correctness.&lt;/p&gt;




&lt;h2&gt;
  
  
  DbContext Lifetime
&lt;/h2&gt;

&lt;p&gt;In ASP.NET Core applications, a common configuration is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddDbContext&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;AppDbContext&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseSqlServer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;connectionString&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This typically creates a scoped &lt;code&gt;DbContext&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The conceptual request lifecycle is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;HTTP Request
     |
     v
DbContext
     |
     +-- Query
     +-- Query
     +-- Update
     |
     v
SaveChanges
     |
     v
Request Complete
     |
     v
DbContext Disposed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Avoid using a single &lt;code&gt;DbContext&lt;/code&gt; instance across unrelated concurrent requests.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;DbContext&lt;/code&gt; is not designed for concurrent operations on the same instance.&lt;/p&gt;




&lt;h2&gt;
  
  
  Change Tracking
&lt;/h2&gt;

&lt;p&gt;The change tracker maintains information about entities being tracked.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;customer&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;dbContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Customers&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FirstAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="n"&gt;customerId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Updated Name"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;dbContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SaveChangesAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;EF Core can determine that &lt;code&gt;Name&lt;/code&gt; changed.&lt;/p&gt;

&lt;p&gt;However, tracking thousands of entities can increase memory and processing overhead.&lt;/p&gt;

&lt;p&gt;For read-heavy workloads, no-tracking queries can reduce this overhead:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;customers&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;dbContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Customers&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AsNoTracking&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToListAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The right approach depends on the workload.&lt;/p&gt;




&lt;h2&gt;
  
  
  Connection Management
&lt;/h2&gt;

&lt;p&gt;EF Core relies on the underlying database provider for connection management and pooling.&lt;/p&gt;

&lt;p&gt;A simplified flow is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Application
     |
     v
EF Core
     |
     v
Connection Pool
     |
     v
Database
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Potential problems include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Connection pool exhaustion&lt;/li&gt;
&lt;li&gt;Long-running database operations&lt;/li&gt;
&lt;li&gt;Excessive concurrency&lt;/li&gt;
&lt;li&gt;Connections held longer than necessary&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If an application experiences database timeouts, investigate whether the problem is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Connection acquisition
        |
        or
        v
Query execution
        |
        or
        v
Database resource contention
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Do not assume all database timeout problems have the same root cause.&lt;/p&gt;




&lt;h2&gt;
  
  
  Async Database Operations
&lt;/h2&gt;

&lt;p&gt;Use asynchronous database APIs in ASP.NET Core applications:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;customers&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;dbContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Customers&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToListAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Similarly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;dbContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SaveChangesAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Asynchronous I/O allows the application to avoid blocking request threads while waiting for database operations.&lt;/p&gt;

&lt;p&gt;The benefit is particularly relevant for applications handling many concurrent requests.&lt;/p&gt;




&lt;h2&gt;
  
  
  Context Pooling
&lt;/h2&gt;

&lt;p&gt;For some workloads, EF Core supports context pooling.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddDbContextPool&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;AppDbContext&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseSqlServer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;connectionString&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pooling allows EF Core to reuse &lt;code&gt;DbContext&lt;/code&gt; instances rather than creating a new instance each time.&lt;/p&gt;

&lt;p&gt;However, pooled contexts require careful consideration of state that might be stored on the context or related services.&lt;/p&gt;

&lt;p&gt;Context pooling is an optimization that should be measured rather than enabled simply because it exists.&lt;/p&gt;




&lt;h1&gt;
  
  
  8. EF Core Performance Patterns and Anti-Patterns
&lt;/h1&gt;

&lt;p&gt;Understanding what not to do is just as important as understanding the recommended patterns.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common Mistakes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Loading Everything
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;customers&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;dbContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Customers&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToListAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;followed by application-side filtering.&lt;/p&gt;

&lt;p&gt;Prefer database-side filtering:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;customers&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;dbContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Customers&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsActive&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToListAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Returning Entities Directly From APIs
&lt;/h3&gt;

&lt;p&gt;Returning EF entities directly can expose persistence models to API consumers.&lt;/p&gt;

&lt;p&gt;Prefer DTOs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;customers&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;dbContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Customers&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;CustomerDto&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToListAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Calling &lt;code&gt;SaveChangesAsync()&lt;/code&gt; Repeatedly
&lt;/h3&gt;

&lt;p&gt;Avoid:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;dbContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;dbContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SaveChangesAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Prefer a suitable batching or set-based strategy.&lt;/p&gt;




&lt;h3&gt;
  
  
  Using Lazy Loading Without Understanding Its Cost
&lt;/h3&gt;

&lt;p&gt;Lazy loading can make database calls invisible in application code.&lt;/p&gt;

&lt;p&gt;Be particularly careful with loops and large collections.&lt;/p&gt;




&lt;h3&gt;
  
  
  Adding &lt;code&gt;Include()&lt;/code&gt; Everywhere
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;Include()&lt;/code&gt; is not a replacement for query design.&lt;/p&gt;

&lt;p&gt;Ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Do I really need the complete related entity?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If the answer is no, projection may be more appropriate.&lt;/p&gt;




&lt;h2&gt;
  
  
  Recommended Patterns
&lt;/h2&gt;

&lt;p&gt;For read-heavy API endpoints:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;dbContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Customers&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AsNoTracking&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsActive&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;CustomerDto&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Email&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Email&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToListAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This combines several useful principles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Database-side filtering&lt;/li&gt;
&lt;li&gt;Projection&lt;/li&gt;
&lt;li&gt;Reduced tracking&lt;/li&gt;
&lt;li&gt;Explicit result shape&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For large updates:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;dbContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Customers&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsActive&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ExecuteUpdateAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;setters&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="n"&gt;setters&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsArchived&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For large result sets:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;dbContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Customers&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;lastCustomerId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;OrderBy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Take&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToListAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  When to Use or Avoid Specific EF Core Features
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Useful When&lt;/th&gt;
&lt;th&gt;Be Careful When&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;AsNoTracking()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Read-only queries&lt;/td&gt;
&lt;td&gt;Entities need to be updated through the context&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Include()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Related entities are required&lt;/td&gt;
&lt;td&gt;Many large collection relationships are included&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;AsSplitQuery()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Large relationship graphs&lt;/td&gt;
&lt;td&gt;Additional database round trips matter&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lazy loading&lt;/td&gt;
&lt;td&gt;Convenience is important&lt;/td&gt;
&lt;td&gt;Query count must be predictable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Projection&lt;/td&gt;
&lt;td&gt;APIs need specific fields&lt;/td&gt;
&lt;td&gt;Complex result shapes require careful testing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ExecuteUpdateAsync()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Large set-based updates&lt;/td&gt;
&lt;td&gt;Entity-level business logic must execute&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ExecuteDeleteAsync()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Large set-based deletes&lt;/td&gt;
&lt;td&gt;Per-entity processing is required&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Context pooling&lt;/td&gt;
&lt;td&gt;High-throughput workloads&lt;/td&gt;
&lt;td&gt;Context-specific mutable state is used&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The important point is that there is no single EF Core feature that makes every application faster.&lt;/p&gt;




&lt;h1&gt;
  
  
  9. Measuring and Diagnosing EF Core Performance
&lt;/h1&gt;

&lt;p&gt;Performance optimization should start with measurement.&lt;/p&gt;

&lt;p&gt;Do not optimize based solely on assumptions.&lt;/p&gt;




&lt;h2&gt;
  
  
  Logging
&lt;/h2&gt;

&lt;p&gt;EF Core can provide database-related logging that helps identify queries and execution behavior.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddDbContext&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;AppDbContext&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;options&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseSqlServer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;connectionString&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;EnableDetailedErrors&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Be careful when enabling detailed logging in production because sensitive information can potentially appear in logs depending on the configuration.&lt;/p&gt;




&lt;h2&gt;
  
  
  Generated SQL
&lt;/h2&gt;

&lt;p&gt;For a specific query:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dbContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Customers&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsActive&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;CustomerDto&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;sql&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToQueryString&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inspecting generated SQL can reveal unexpected behavior.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Expected:

SELECT Id, Name
FROM Customers
WHERE IsActive = 1


Unexpected:

SELECT many columns
FROM Customers
JOIN ...
JOIN ...
JOIN ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This can lead to further investigation.&lt;/p&gt;




&lt;h2&gt;
  
  
  Query Timing
&lt;/h2&gt;

&lt;p&gt;Measure actual execution time rather than assuming a query is slow.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;stopwatch&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Stopwatch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;StartNew&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;customers&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;dbContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Customers&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AsNoTracking&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsActive&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToListAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="n"&gt;stopwatch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Stop&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;$"Query completed in &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;stopwatch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ElapsedMilliseconds&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt; ms"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For production applications, use structured logging and telemetry instead of ad-hoc console output.&lt;/p&gt;




&lt;h2&gt;
  
  
  Profiling
&lt;/h2&gt;

&lt;p&gt;For a production-grade application, consider observing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;HTTP Request
      |
      v
Application Code
      |
      v
EF Core
      |
      v
Database Dependency
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Useful measurements include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;API latency&lt;/li&gt;
&lt;li&gt;Database query duration&lt;/li&gt;
&lt;li&gt;Number of database calls&lt;/li&gt;
&lt;li&gt;Result-set size&lt;/li&gt;
&lt;li&gt;Error rate&lt;/li&gt;
&lt;li&gt;Request throughput&lt;/li&gt;
&lt;li&gt;Memory usage&lt;/li&gt;
&lt;li&gt;Connection utilization&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The objective is to identify where the time is actually being spent.&lt;/p&gt;




&lt;h2&gt;
  
  
  Identifying Slow Queries
&lt;/h2&gt;

&lt;p&gt;A useful troubleshooting process is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Slow API
   |
   v
Measure API latency
   |
   v
Identify database dependency
   |
   v
Identify slow query
   |
   v
Inspect generated SQL
   |
   v
Analyze execution
   |
   v
Optimize EF Core query
   |
   v
Measure again
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the database query itself is efficient but the API is still slow, investigate other parts of the request pipeline.&lt;/p&gt;




&lt;h1&gt;
  
  
  10. Practical EF Core Performance Checklist
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Query Checklist
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Are only required columns being selected?&lt;/li&gt;
&lt;li&gt;[ ] Is filtering performed in the database?&lt;/li&gt;
&lt;li&gt;[ ] Is projection used where appropriate?&lt;/li&gt;
&lt;li&gt;[ ] Are queries executed only when needed?&lt;/li&gt;
&lt;li&gt;[ ] Are N+1 queries eliminated?&lt;/li&gt;
&lt;li&gt;[ ] Is generated SQL understood?&lt;/li&gt;
&lt;li&gt;[ ] Are large result sets paginated?&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Tracking Checklist
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Does the query actually require change tracking?&lt;/li&gt;
&lt;li&gt;[ ] Is &lt;code&gt;AsNoTracking()&lt;/code&gt; used for appropriate read-only queries?&lt;/li&gt;
&lt;li&gt;[ ] Is the number of tracked entities reasonable?&lt;/li&gt;
&lt;li&gt;[ ] Are long-lived &lt;code&gt;DbContext&lt;/code&gt; instances avoided?&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Loading Checklist
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Is &lt;code&gt;Include()&lt;/code&gt; used only when required?&lt;/li&gt;
&lt;li&gt;[ ] Are large relationship graphs avoided?&lt;/li&gt;
&lt;li&gt;[ ] Is lazy loading being used intentionally?&lt;/li&gt;
&lt;li&gt;[ ] Could projection replace entity loading?&lt;/li&gt;
&lt;li&gt;[ ] Should &lt;code&gt;AsSplitQuery()&lt;/code&gt; be considered for the query shape?&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Write-Operation Checklist
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Is &lt;code&gt;SaveChangesAsync()&lt;/code&gt; being called unnecessarily inside loops?&lt;/li&gt;
&lt;li&gt;[ ] Can the operation be performed in batches?&lt;/li&gt;
&lt;li&gt;[ ] Can &lt;code&gt;ExecuteUpdateAsync()&lt;/code&gt; be used?&lt;/li&gt;
&lt;li&gt;[ ] Can &lt;code&gt;ExecuteDeleteAsync()&lt;/code&gt; be used?&lt;/li&gt;
&lt;li&gt;[ ] Are transaction boundaries appropriate?&lt;/li&gt;
&lt;li&gt;[ ] Is application-level business logic required for each entity?&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Production Checklist
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Is query performance measured?&lt;/li&gt;
&lt;li&gt;[ ] Are database calls observable?&lt;/li&gt;
&lt;li&gt;[ ] Are slow queries identifiable?&lt;/li&gt;
&lt;li&gt;[ ] Are application logs configured appropriately?&lt;/li&gt;
&lt;li&gt;[ ] Are connection issues monitored?&lt;/li&gt;
&lt;li&gt;[ ] Are realistic datasets used for performance testing?&lt;/li&gt;
&lt;li&gt;[ ] Are performance changes validated before and after deployment?&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  11. Key Takeaways
&lt;/h1&gt;

&lt;p&gt;Entity Framework Core provides powerful abstractions for working with relational databases, but those abstractions do not remove the need to understand database access patterns.&lt;/p&gt;

&lt;p&gt;The most important principles are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Understand how LINQ becomes SQL.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Retrieve only the data the application actually needs.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Use projection for precise read models.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Use &lt;code&gt;AsNoTracking()&lt;/code&gt; for appropriate read-only workloads.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Avoid N+1 queries.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Use &lt;code&gt;Include()&lt;/code&gt; carefully.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Understand the trade-offs of lazy loading.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Keep filtering and sorting in the database where appropriate.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Avoid premature query execution.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Use appropriate pagination for large datasets.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Avoid unnecessary &lt;code&gt;SaveChangesAsync()&lt;/code&gt; calls.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Consider set-based operations for large updates and deletes.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Manage &lt;code&gt;DbContext&lt;/code&gt; lifetime correctly.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Use asynchronous database operations.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Measure performance instead of relying on assumptions.&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A useful mental model is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                EF Core Performance
                       |
        +--------------+--------------+
        |              |              |
        v              v              v
      Query          Tracking       Loading
        |              |              |
        v              v              v
   Projection      NoTracking       Include
   Filtering       Context         Lazy Load
   Pagination      Lifetime        Split Query
        |              |              |
        +--------------+--------------+
                       |
                       v
                 Database Work
                       |
                       v
                 Measure &amp;amp; Tune
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;EF Core performance optimization is not about applying every available optimization.&lt;/p&gt;

&lt;p&gt;It is about understanding the workload and choosing the simplest approach that avoids unnecessary work.&lt;/p&gt;

&lt;p&gt;When an EF Core query becomes slow, start with a few fundamental questions:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;What data do I actually need?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How many database calls am I making?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is EF Core tracking entities unnecessarily?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What SQL is actually being generated?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can the operation be performed efficiently at the database level?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Once these questions become part of the development and code-review process, many EF Core performance issues can be identified before they become production problems.&lt;/p&gt;

&lt;p&gt;The best optimization is often not a complicated EF Core technique. It is simply &lt;strong&gt;doing less unnecessary work&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Tags
&lt;/h2&gt;

&lt;h1&gt;
  
  
  dotnet #csharp #efcore #entityframework #aspnetcore #performance #softwarearchitecture #programming
&lt;/h1&gt;

</description>
      <category>dotnet</category>
      <category>efcore</category>
      <category>csharp</category>
      <category>performanceoptimization</category>
    </item>
    <item>
      <title>SOLID Principles in C#: A Practical Guide with Real-World Examples and Design Patterns</title>
      <dc:creator>Chethan Ramaswamy</dc:creator>
      <pubDate>Sat, 26 Sep 2026 09:30:35 +0000</pubDate>
      <link>https://dev.to/chethanramaswamy/solid-principles-in-c-a-practical-guide-with-real-world-examples-and-design-patterns-3lk</link>
      <guid>https://dev.to/chethanramaswamy/solid-principles-in-c-a-practical-guide-with-real-world-examples-and-design-patterns-3lk</guid>
      <description>&lt;h2&gt;
  
  
  📑 Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;What Does SOLID Mean?&lt;/li&gt;
&lt;li&gt;SOLID Is Not the Same as Design Patterns&lt;/li&gt;
&lt;li&gt;
1. Single Responsibility Principle — SRP

&lt;ul&gt;
&lt;li&gt;The Simple Explanation&lt;/li&gt;
&lt;li&gt;A Common Problem&lt;/li&gt;
&lt;li&gt;Refactoring the Design&lt;/li&gt;
&lt;li&gt;Design Patterns and Techniques That Can Support SRP&lt;/li&gt;
&lt;li&gt;Common SRP Mistake&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
2. Open/Closed Principle — OCP

&lt;ul&gt;
&lt;li&gt;The Simple Explanation&lt;/li&gt;
&lt;li&gt;A Common Problem&lt;/li&gt;
&lt;li&gt;Applying the Strategy Pattern&lt;/li&gt;
&lt;li&gt;Strategy Pattern and OCP&lt;/li&gt;
&lt;li&gt;Factory Pattern and OCP&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
3. Liskov Substitution Principle — LSP

&lt;ul&gt;
&lt;li&gt;The Simple Explanation&lt;/li&gt;
&lt;li&gt;The Classic Example&lt;/li&gt;
&lt;li&gt;A Better Design&lt;/li&gt;
&lt;li&gt;LSP in Enterprise Applications&lt;/li&gt;
&lt;li&gt;LSP and Composition&lt;/li&gt;
&lt;li&gt;Common LSP Warning Signs&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
4. Interface Segregation Principle — ISP

&lt;ul&gt;
&lt;li&gt;The Simple Explanation&lt;/li&gt;
&lt;li&gt;A Common Problem&lt;/li&gt;
&lt;li&gt;Split the Interface&lt;/li&gt;
&lt;li&gt;ISP in an Enterprise Service&lt;/li&gt;
&lt;li&gt;Patterns That Can Support ISP&lt;/li&gt;
&lt;li&gt;Common ISP Mistake&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
5. Dependency Inversion Principle — DIP

&lt;ul&gt;
&lt;li&gt;The Simple Explanation&lt;/li&gt;
&lt;li&gt;A Common Problem&lt;/li&gt;
&lt;li&gt;Introduce an Abstraction&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
Dependency Inversion vs Dependency Injection

&lt;ul&gt;
&lt;li&gt;Dependency Inversion Principle&lt;/li&gt;
&lt;li&gt;Dependency Injection&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;SOLID and Design Patterns — The Practical Connection&lt;/li&gt;
&lt;li&gt;SOLID Does Not Mean "More Interfaces"&lt;/li&gt;
&lt;li&gt;SOLID Does Not Mean "Create More Classes"&lt;/li&gt;
&lt;li&gt;SOLID Does Not Mean "Remove Every if/else"&lt;/li&gt;
&lt;li&gt;SOLID Does Not Mean "Use Every Design Pattern"&lt;/li&gt;
&lt;li&gt;
A Real-World Example: Notification System

&lt;ul&gt;
&lt;li&gt;Introduce an Abstraction&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
How to Identify SOLID Issues During Code Review

&lt;ul&gt;
&lt;li&gt;SRP Questions&lt;/li&gt;
&lt;li&gt;OCP Questions&lt;/li&gt;
&lt;li&gt;LSP Questions&lt;/li&gt;
&lt;li&gt;ISP Questions&lt;/li&gt;
&lt;li&gt;DIP Questions&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
A Practical SOLID Refactoring Workflow

&lt;ul&gt;
&lt;li&gt;Step 1 — Identify What Changes&lt;/li&gt;
&lt;li&gt;Step 2 — Identify the Coupling&lt;/li&gt;
&lt;li&gt;Step 3 — Identify the Responsibility&lt;/li&gt;
&lt;li&gt;Step 4 — Introduce an Abstraction When It Adds Value&lt;/li&gt;
&lt;li&gt;Step 5 — Choose a Pattern If It Fits&lt;/li&gt;
&lt;li&gt;Step 6 — Keep the Design Simple&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;SOLID in a Typical .NET Application&lt;/li&gt;
&lt;li&gt;SOLID vs Design Patterns&lt;/li&gt;
&lt;li&gt;When Should You Not Apply SOLID Aggressively?&lt;/li&gt;
&lt;li&gt;A Simple SOLID Mental Model&lt;/li&gt;
&lt;li&gt;SOLID Design Pattern Quick Reference&lt;/li&gt;
&lt;li&gt;
Common SOLID Misconceptions

&lt;ul&gt;
&lt;li&gt;Misconception 1: "SOLID means more interfaces."&lt;/li&gt;
&lt;li&gt;Misconception 2: "SOLID means every class should have one method."&lt;/li&gt;
&lt;li&gt;Misconception 3: "SOLID means no if/else statements."&lt;/li&gt;
&lt;li&gt;Misconception 4: "DIP and Dependency Injection are the same."&lt;/li&gt;
&lt;li&gt;Misconception 5: "Every SOLID problem requires a design pattern."&lt;/li&gt;
&lt;li&gt;Misconception 6: "Inheritance is always better than composition."&lt;/li&gt;
&lt;li&gt;Misconception 7: "Following SOLID guarantees good architecture."&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;The Most Important Lesson&lt;/li&gt;
&lt;li&gt;Final Takeaways&lt;/li&gt;
&lt;li&gt;Practical SOLID Checklist&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;SOLID is one of the most discussed topics in object-oriented programming.&lt;/p&gt;

&lt;p&gt;Most developers have heard of the five principles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;S&lt;/strong&gt; — Single Responsibility Principle&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;O&lt;/strong&gt; — Open/Closed Principle&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;L&lt;/strong&gt; — Liskov Substitution Principle&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;I&lt;/strong&gt; — Interface Segregation Principle&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;D&lt;/strong&gt; — Dependency Inversion Principle&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But knowing what the letters stand for is very different from knowing how to apply them.&lt;/p&gt;

&lt;p&gt;A developer may remember that SRP means &lt;em&gt;"a class should have one reason to change"&lt;/em&gt; but still struggle to identify an SRP violation in a real application.&lt;/p&gt;

&lt;p&gt;Similarly, developers often learn design patterns such as &lt;strong&gt;Strategy, Factory, Adapter, Decorator, and Repository&lt;/strong&gt;, but may not understand how these patterns relate to SOLID.&lt;/p&gt;

&lt;p&gt;This article takes a practical approach.&lt;/p&gt;

&lt;p&gt;Instead of simply memorizing definitions, we will look at:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The problem each SOLID principle tries to solve&lt;/li&gt;
&lt;li&gt;How that problem appears in real applications&lt;/li&gt;
&lt;li&gt;How to recognize the problem&lt;/li&gt;
&lt;li&gt;How to refactor the design&lt;/li&gt;
&lt;li&gt;Which design patterns or techniques can help&lt;/li&gt;
&lt;li&gt;Common misconceptions&lt;/li&gt;
&lt;li&gt;How the principles work together&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The examples use &lt;strong&gt;C# and .NET&lt;/strong&gt;, but the underlying concepts apply to object-oriented software in general.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Does SOLID Mean?
&lt;/h2&gt;

&lt;p&gt;SOLID is an acronym for five object-oriented design principles:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Principle&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;th&gt;Practical Question&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;S&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Single Responsibility Principle&lt;/td&gt;
&lt;td&gt;Does this class have more than one reason to change?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;O&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Open/Closed Principle&lt;/td&gt;
&lt;td&gt;Can I add new behavior without repeatedly modifying stable code?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;L&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Liskov Substitution Principle&lt;/td&gt;
&lt;td&gt;Can a derived type safely replace its base type?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;I&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Interface Segregation Principle&lt;/td&gt;
&lt;td&gt;Are implementations forced to depend on methods they do not need?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;D&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Dependency Inversion Principle&lt;/td&gt;
&lt;td&gt;Does my business logic depend directly on implementation details?&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A simple way to remember them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;S → Keep responsibilities focused

O → Make changing behavior extensible

L → Make inheritance behaviorally trustworthy

I → Keep interfaces focused

D → Depend on abstractions
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;SOLID should not be treated as five rules that must be applied mechanically.&lt;/p&gt;

&lt;p&gt;The goal is not to create more interfaces, classes, or abstractions.&lt;/p&gt;

&lt;p&gt;The goal is to create software that is easier to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Understand&lt;/li&gt;
&lt;li&gt;Change&lt;/li&gt;
&lt;li&gt;Test&lt;/li&gt;
&lt;li&gt;Extend&lt;/li&gt;
&lt;li&gt;Maintain&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  SOLID Is Not the Same as Design Patterns
&lt;/h2&gt;

&lt;p&gt;This distinction is important.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SOLID principles are design principles.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Design patterns are reusable approaches to recurring software design problems.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Design Problem
      ↓
Identify the underlying design principle
      ↓
Consider possible design approaches
      ↓
Choose a suitable pattern or technique
      ↓
Keep the solution as simple as possible
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A design pattern does not automatically make code SOLID.&lt;/p&gt;

&lt;p&gt;Likewise, SOLID does not require the use of design patterns.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Use SOLID to guide your design decisions. Use a design pattern when it provides a practical solution to the problem you actually have.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Also, not every pattern discussed in this article is a &lt;strong&gt;Gang of Four (GoF)&lt;/strong&gt; design pattern. For example, &lt;strong&gt;Repository&lt;/strong&gt; and &lt;strong&gt;Dependency Injection&lt;/strong&gt; are commonly used design or architectural techniques in modern enterprise applications, but they are not GoF patterns.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Single Responsibility Principle — SRP
&lt;/h2&gt;

&lt;h2&gt;
  
  
  The Simple Explanation
&lt;/h2&gt;

&lt;p&gt;The Single Responsibility Principle states:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;A class should have one reason to change.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is often misunderstood as:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"A class should contain only one method."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That is not what SRP means.&lt;/p&gt;

&lt;p&gt;A class can contain multiple methods and still have a single responsibility.&lt;/p&gt;

&lt;p&gt;The better question is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Do the responsibilities in this class change for different reasons?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  A Common Problem
&lt;/h2&gt;

&lt;p&gt;Imagine an e-commerce application with this class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OrderService&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;CreateOrder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Order&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Save order to database&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;ProcessPayment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Order&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Process payment&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;SendConfirmationEmail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Order&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Send email&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="nf"&gt;GenerateInvoice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Order&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Generate PDF invoice&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At first glance, this may look convenient.&lt;/p&gt;

&lt;p&gt;Everything related to an order is in one place.&lt;/p&gt;

&lt;p&gt;But this class actually contains several responsibilities:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;OrderService
    |
    +-- Order persistence
    |
    +-- Payment processing
    |
    +-- Email communication
    |
    +-- Invoice generation
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now consider the following changes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The database technology changes.&lt;/li&gt;
&lt;li&gt;The payment provider changes.&lt;/li&gt;
&lt;li&gt;The email provider changes.&lt;/li&gt;
&lt;li&gt;The invoice format changes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These changes are driven by different concerns.&lt;/p&gt;

&lt;p&gt;If all of them require modifying &lt;code&gt;OrderService&lt;/code&gt;, the class has multiple reasons to change.&lt;/p&gt;

&lt;p&gt;That is a warning sign for SRP.&lt;/p&gt;




&lt;h2&gt;
  
  
  Refactoring the Design
&lt;/h2&gt;

&lt;p&gt;We can separate the responsibilities:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;IOrderRepository&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;SaveAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Order&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;IPaymentService&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;ProcessAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Order&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;INotificationService&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;SendOrderConfirmationAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Order&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;IInvoiceService&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;]&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;GenerateAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Order&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The application service can then coordinate the workflow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OrderService&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;IOrderRepository&lt;/span&gt; &lt;span class="n"&gt;_orderRepository&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;IPaymentService&lt;/span&gt; &lt;span class="n"&gt;_paymentService&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;INotificationService&lt;/span&gt; &lt;span class="n"&gt;_notificationService&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;OrderService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;IOrderRepository&lt;/span&gt; &lt;span class="n"&gt;orderRepository&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;IPaymentService&lt;/span&gt; &lt;span class="n"&gt;paymentService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;INotificationService&lt;/span&gt; &lt;span class="n"&gt;notificationService&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_orderRepository&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;orderRepository&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;_paymentService&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;paymentService&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;_notificationService&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;notificationService&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;CreateOrderAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Order&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_orderRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SaveAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_paymentService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ProcessAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_notificationService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SendOrderConfirmationAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the responsibilities are clearer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;OrderService
     |
     +── IOrderRepository
     +── IPaymentService
     +── INotificationService
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;OrderService&lt;/code&gt; coordinates the workflow instead of implementing every technical detail itself.&lt;/p&gt;




&lt;h2&gt;
  
  
  Design Patterns and Techniques That Can Support SRP
&lt;/h2&gt;

&lt;p&gt;SRP does not require a particular design pattern.&lt;/p&gt;

&lt;p&gt;However, several patterns or techniques can help separate responsibilities:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pattern / Technique&lt;/th&gt;
&lt;th&gt;How it can help&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Facade&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Provides a simplified interface over multiple components&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Strategy&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Separates interchangeable business algorithms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Command&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Encapsulates an operation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Decorator&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Separates cross-cutting behavior&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Mediator&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Separates request handling responsibilities&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Repository&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Separates persistence concerns from business logic&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;These are not "SRP patterns."&lt;/p&gt;

&lt;p&gt;They are approaches that can help create a design with better separation of responsibilities.&lt;/p&gt;




&lt;h2&gt;
  
  
  Common SRP Mistake
&lt;/h2&gt;

&lt;p&gt;A common misunderstanding is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Every class must do only one thing."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That can lead to unnecessary fragmentation.&lt;/p&gt;

&lt;p&gt;For example, creating a separate class for every small method may produce a system with too many abstractions and dependencies.&lt;/p&gt;

&lt;p&gt;Instead, ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Do these responsibilities change independently?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If they do, separating them may make the design easier to maintain.&lt;/p&gt;

&lt;p&gt;If they do not, keeping them together may be perfectly reasonable.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Open/Closed Principle — OCP
&lt;/h2&gt;

&lt;h2&gt;
  
  
  The Simple Explanation
&lt;/h2&gt;

&lt;p&gt;The Open/Closed Principle states:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Software entities should be open for extension but closed for modification.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A practical interpretation is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Design areas of expected variation so that new behavior can be added without repeatedly changing stable, already-tested code.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This does not mean existing code can never be modified.&lt;/p&gt;

&lt;p&gt;Requirements change, bugs need fixing, and designs evolve.&lt;/p&gt;

&lt;p&gt;The principle is about avoiding unnecessary modification of stable code every time a new variation is introduced.&lt;/p&gt;




&lt;h2&gt;
  
  
  A Common Problem
&lt;/h2&gt;

&lt;p&gt;Consider payment processing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PaymentService&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;paymentType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;decimal&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;paymentType&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"CreditCard"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// Credit card payment&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;paymentType&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"PayPal"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// PayPal payment&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;paymentType&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"UPI"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// UPI payment&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Today we support:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Credit Card&lt;/li&gt;
&lt;li&gt;PayPal&lt;/li&gt;
&lt;li&gt;UPI&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Tomorrow we might add:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Bank Transfer&lt;/li&gt;
&lt;li&gt;Apple Pay&lt;/li&gt;
&lt;li&gt;Google Pay&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If the conditional keeps growing, every new payment method requires modifying the same class.&lt;/p&gt;

&lt;p&gt;The problem is not that &lt;code&gt;if&lt;/code&gt; or &lt;code&gt;switch&lt;/code&gt; statements are inherently bad.&lt;/p&gt;

&lt;p&gt;The problem is that independently changing behavior is becoming tightly coupled to one piece of code.&lt;/p&gt;




&lt;h2&gt;
  
  
  Applying the Strategy Pattern
&lt;/h2&gt;

&lt;p&gt;We can introduce an abstraction:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;IPaymentStrategy&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Pay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;decimal&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then create separate implementations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CreditCardPayment&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IPaymentStrategy&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Pay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;decimal&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Process credit card payment&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UpiPayment&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IPaymentStrategy&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Pay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;decimal&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Process UPI payment&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The service depends on the abstraction:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PaymentService&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;IPaymentStrategy&lt;/span&gt; &lt;span class="n"&gt;_paymentStrategy&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;PaymentService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IPaymentStrategy&lt;/span&gt; &lt;span class="n"&gt;paymentStrategy&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_paymentStrategy&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;paymentStrategy&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;decimal&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_paymentStrategy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Pay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now a new payment method can be introduced:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BankTransferPayment&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IPaymentStrategy&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Pay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;decimal&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Process bank transfer&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The existing payment implementations do not need to change.&lt;/p&gt;




&lt;h2&gt;
  
  
  Strategy Pattern and OCP
&lt;/h2&gt;

&lt;p&gt;This is one of the clearest relationships between SOLID and design patterns:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Problem
   ↓
Multiple interchangeable behaviors
   ↓
Identify the variation
   ↓
Encapsulate each behavior
   ↓
Strategy Pattern
   ↓
Add new strategies without changing existing strategies
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Strategy Pattern is particularly useful when several algorithms or business rules implement the same conceptual operation.&lt;/p&gt;




&lt;h2&gt;
  
  
  Factory Pattern and OCP
&lt;/h2&gt;

&lt;p&gt;A Factory can be useful when object creation is itself a concern.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;IPaymentProcessor&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;decimal&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Implementations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;IPaymentProcessor
       |
       +---- CreditCardProcessor
       +---- UpiProcessor
       +---- BankTransferProcessor
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A simple factory might select an implementation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PaymentProcessorFactory&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IPaymentProcessor&lt;/span&gt; &lt;span class="nf"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;type&lt;/span&gt; &lt;span class="k"&gt;switch&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="s"&gt;"CreditCard"&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;CreditCardProcessor&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="s"&gt;"UPI"&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;UpiProcessor&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="s"&gt;"BankTransfer"&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;BankTransferProcessor&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ArgumentException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Unsupported payment type"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, notice something important.&lt;/p&gt;

&lt;p&gt;The factory itself must change when a new payment type is added.&lt;/p&gt;

&lt;p&gt;Therefore, this factory is not automatically an example of a completely closed-for-modification design.&lt;/p&gt;

&lt;p&gt;It can still be a useful solution if the creation logic is intentionally centralized and the trade-off is acceptable.&lt;/p&gt;

&lt;p&gt;In larger .NET applications, dependency injection, keyed services, configuration-driven registration, or other approaches may sometimes provide a better fit.&lt;/p&gt;

&lt;p&gt;The lesson is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Do not use a pattern simply because its name appears in a SOLID discussion. Evaluate the actual design trade-off.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  3. Liskov Substitution Principle — LSP
&lt;/h2&gt;

&lt;h2&gt;
  
  
  The Simple Explanation
&lt;/h2&gt;

&lt;p&gt;The Liskov Substitution Principle states that:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;A derived type should be usable wherever its base type is expected without violating the expectations of the consuming code.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In simpler terms:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;If &lt;code&gt;B&lt;/code&gt; is a subtype of &lt;code&gt;A&lt;/code&gt;, replacing an &lt;code&gt;A&lt;/code&gt; with a &lt;code&gt;B&lt;/code&gt; should not unexpectedly break the program's correctness or behavioral assumptions.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;LSP is less about syntax and more about &lt;strong&gt;behavioral contracts&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Classic Example
&lt;/h2&gt;

&lt;p&gt;Consider:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Bird&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;virtual&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Fly&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Flying"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now imagine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Penguin&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Bird&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Fly&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;NotSupportedException&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We have created a problem.&lt;/p&gt;

&lt;p&gt;The base class exposes &lt;code&gt;Fly()&lt;/code&gt; as a behavior that consumers can call.&lt;/p&gt;

&lt;p&gt;But &lt;code&gt;Penguin&lt;/code&gt; cannot provide that behavior.&lt;/p&gt;

&lt;p&gt;This code becomes problematic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;Bird&lt;/span&gt; &lt;span class="n"&gt;bird&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Penguin&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="n"&gt;bird&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Fly&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The derived type cannot safely satisfy the expectations created by the base abstraction.&lt;/p&gt;




&lt;h2&gt;
  
  
  A Better Design
&lt;/h2&gt;

&lt;p&gt;The problem is the abstraction.&lt;/p&gt;

&lt;p&gt;Not every bird needs to support flying.&lt;/p&gt;

&lt;p&gt;We can separate the concepts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;abstract&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Bird&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;abstract&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Move&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then define flying separately:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;IFlyingBird&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Fly&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An eagle can implement it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Eagle&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Bird&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IFlyingBird&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Move&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;Fly&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Fly&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Eagle is flying"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A penguin does not need to implement &lt;code&gt;IFlyingBird&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Penguin&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Bird&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Move&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Penguin is walking"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The abstraction now better represents the actual capabilities of the types.&lt;/p&gt;




&lt;h2&gt;
  
  
  LSP in Enterprise Applications
&lt;/h2&gt;

&lt;p&gt;The Bird/Penguin example is useful for understanding the principle, but enterprise applications often encounter LSP problems in more subtle ways.&lt;/p&gt;

&lt;p&gt;Consider a payment processor:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;abstract&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PaymentProcessor&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;abstract&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;decimal&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;abstract&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Refund&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;decimal&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Suppose an implementation does not support refunds:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SomePaymentProcessor&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;PaymentProcessor&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;decimal&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Process payment&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Refund&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;decimal&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;NotSupportedException&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If consumers of &lt;code&gt;PaymentProcessor&lt;/code&gt; reasonably expect every implementation to support refunds, the abstraction is too broad.&lt;/p&gt;

&lt;p&gt;A better design may separate the capabilities:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;IPaymentProcessor&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;decimal&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;IRefundProcessor&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Refund&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;decimal&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An implementation can then support the capabilities it actually provides.&lt;/p&gt;

&lt;p&gt;This example also connects naturally to the &lt;strong&gt;Interface Segregation Principle&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  LSP and Composition
&lt;/h2&gt;

&lt;p&gt;A useful way to avoid problematic inheritance is to consider composition.&lt;/p&gt;

&lt;p&gt;Instead of creating a complex hierarchy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;BaseProcessor
     |
     +-- ProcessorA
     +-- ProcessorB
     +-- ProcessorC
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;consider whether behavior can be composed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Processor
    |
    +-- Strategy
    +-- Validator
    +-- Formatter
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Composition is not a replacement for inheritance in every situation, but it can reduce problems caused by incorrect inheritance relationships.&lt;/p&gt;




&lt;h2&gt;
  
  
  Common LSP Warning Signs
&lt;/h2&gt;

&lt;p&gt;Look carefully when you see:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;NotSupportedException&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;inside an overridden method.&lt;/p&gt;

&lt;p&gt;This is &lt;strong&gt;not automatically an LSP violation&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Sometimes an unsupported operation is legitimate.&lt;/p&gt;

&lt;p&gt;But it should trigger a design question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Does the base abstraction promise behavior that this implementation cannot provide?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Other warning signs include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Derived classes disabling base behavior&lt;/li&gt;
&lt;li&gt;Derived classes significantly changing the meaning of base methods&lt;/li&gt;
&lt;li&gt;Consumers checking concrete subtypes before using them&lt;/li&gt;
&lt;li&gt;Large type-checking blocks&lt;/li&gt;
&lt;li&gt;Frequent &lt;code&gt;is&lt;/code&gt; checks against derived types&lt;/li&gt;
&lt;li&gt;Special-case handling for particular subclasses&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  4. Interface Segregation Principle — ISP
&lt;/h2&gt;

&lt;h2&gt;
  
  
  The Simple Explanation
&lt;/h2&gt;

&lt;p&gt;The Interface Segregation Principle states:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Clients should not be forced to depend on methods they do not use.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In simpler terms:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Prefer focused interfaces that represent meaningful capabilities over large interfaces containing unrelated operations.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  A Common Problem
&lt;/h2&gt;

&lt;p&gt;Consider an employee management system:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;IEmployee&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Work&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Eat&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;AttendMeeting&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;WriteCode&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;ManageTeam&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now imagine a developer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Developer&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IEmployee&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Work&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Eat&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;AttendMeeting&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;WriteCode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;ManageTeam&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;NotSupportedException&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The developer is forced to implement &lt;code&gt;ManageTeam()&lt;/code&gt; even though that capability is not part of the developer's responsibility.&lt;/p&gt;

&lt;p&gt;This is a sign of a &lt;strong&gt;fat interface&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Split the Interface
&lt;/h2&gt;

&lt;p&gt;Instead, define smaller, cohesive interfaces:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;IWorker&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Work&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;IDeveloper&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;WriteCode&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;IManager&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;ManageTeam&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Developer&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IWorker&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IDeveloper&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Work&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;WriteCode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;EngineeringManager&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IWorker&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IManager&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Work&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;ManageTeam&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each class depends only on the capabilities it needs.&lt;/p&gt;




&lt;h2&gt;
  
  
  ISP in an Enterprise Service
&lt;/h2&gt;

&lt;p&gt;Imagine a large customer service interface:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;ICustomerService&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Customer&lt;/span&gt; &lt;span class="nf"&gt;GetCustomer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;CreateCustomer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Customer&lt;/span&gt; &lt;span class="n"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;UpdateCustomer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Customer&lt;/span&gt; &lt;span class="n"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;DeleteCustomer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;SendMarketingEmail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;ExportCustomers&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Different consumers may need completely different capabilities.&lt;/p&gt;

&lt;p&gt;Instead, we could separate them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;ICustomerReader&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Customer&lt;/span&gt; &lt;span class="nf"&gt;GetCustomer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;ICustomerWriter&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;CreateCustomer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Customer&lt;/span&gt; &lt;span class="n"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;UpdateCustomer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Customer&lt;/span&gt; &lt;span class="n"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;DeleteCustomer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;ICustomerExporter&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;ExportCustomers&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This creates smaller contracts that can be consumed independently.&lt;/p&gt;




&lt;h2&gt;
  
  
  Patterns That Can Support ISP
&lt;/h2&gt;

&lt;p&gt;Some patterns can complement interface segregation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Adapter&lt;/strong&gt; — exposes the interface a client actually needs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Facade&lt;/strong&gt; — provides a focused interface over a more complex subsystem&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Proxy&lt;/strong&gt; — can provide a focused boundary around another component&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Again, the pattern does not implement ISP automatically.&lt;/p&gt;

&lt;p&gt;The important design decision is to ensure clients are not forced to depend on irrelevant operations.&lt;/p&gt;




&lt;h2&gt;
  
  
  Common ISP Mistake
&lt;/h2&gt;

&lt;p&gt;Do not interpret ISP as:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Every interface should contain exactly one method."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That is not the principle.&lt;/p&gt;

&lt;p&gt;An interface can contain several methods when they represent one cohesive capability.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;IOrderRepository&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Order&lt;/span&gt;&lt;span class="p"&gt;?&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;GetByIdAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;AddAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Order&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;UpdateAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Order&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These operations may form a cohesive persistence contract.&lt;/p&gt;

&lt;p&gt;The goal is &lt;strong&gt;cohesion&lt;/strong&gt;, not an arbitrary number of methods.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. Dependency Inversion Principle — DIP
&lt;/h2&gt;

&lt;h2&gt;
  
  
  The Simple Explanation
&lt;/h2&gt;

&lt;p&gt;The Dependency Inversion Principle states:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;High-level modules should not depend on low-level modules. Both should depend on abstractions.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It also states:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Abstractions should not depend on details. Details should depend on abstractions.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In simpler terms:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Business logic should not be tightly coupled to infrastructure details.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  A Common Problem
&lt;/h2&gt;

&lt;p&gt;Consider:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OrderService&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;SqlOrderRepository&lt;/span&gt; &lt;span class="n"&gt;_repository&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;OrderService&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_repository&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;SqlOrderRepository&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;CreateOrder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Order&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_repository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;OrderService&lt;/code&gt; directly creates &lt;code&gt;SqlOrderRepository&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The business service now knows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Which persistence implementation is being used&lt;/li&gt;
&lt;li&gt;How the repository is created&lt;/li&gt;
&lt;li&gt;That the repository is backed by SQL Server&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This creates tight coupling.&lt;/p&gt;




&lt;h2&gt;
  
  
  Introduce an Abstraction
&lt;/h2&gt;

&lt;p&gt;Define:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;IOrderRepository&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Order&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The infrastructure implementation can be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SqlOrderRepository&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IOrderRepository&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Order&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Save to SQL Server&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the service depends on the abstraction:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OrderService&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;IOrderRepository&lt;/span&gt; &lt;span class="n"&gt;_repository&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;OrderService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IOrderRepository&lt;/span&gt; &lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_repository&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;CreateOrder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Order&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_repository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The dependency direction becomes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Before:

OrderService
     |
     ↓
SqlOrderRepository


After:

OrderService
     |
     ↓
IOrderRepository
     ↑
     |
SqlOrderRepository
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The high-level business logic no longer needs to know the concrete persistence implementation.&lt;/p&gt;




&lt;h1&gt;
  
  
  Dependency Inversion vs Dependency Injection
&lt;/h1&gt;

&lt;p&gt;These concepts are closely related but are not the same thing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dependency Inversion Principle
&lt;/h3&gt;

&lt;p&gt;DIP is a &lt;strong&gt;design principle&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It describes how high-level and low-level components should depend on abstractions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dependency Injection
&lt;/h3&gt;

&lt;p&gt;Dependency Injection is a &lt;strong&gt;technique&lt;/strong&gt; for providing dependencies to a class from outside the class.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;OrderService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IOrderRepository&lt;/span&gt; &lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;_repository&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is constructor injection.&lt;/p&gt;

&lt;p&gt;In ASP.NET Core, the built-in dependency injection container can register the implementation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddScoped&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IOrderRepository&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SqlOrderRepository&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddScoped&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;OrderService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The framework can then provide &lt;code&gt;SqlOrderRepository&lt;/code&gt; when &lt;code&gt;OrderService&lt;/code&gt; is created.&lt;/p&gt;

&lt;p&gt;So the relationship is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DIP
 ↓
Design principle

Dependency Injection
 ↓
Implementation technique
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Dependency Injection is one common way to implement a design that follows DIP.&lt;/p&gt;




&lt;h1&gt;
  
  
  SOLID and Design Patterns — The Practical Connection
&lt;/h1&gt;

&lt;p&gt;Now let's connect the principles with patterns and techniques that can help address related design problems.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;SOLID Principle&lt;/th&gt;
&lt;th&gt;Patterns / Techniques That May Help&lt;/th&gt;
&lt;th&gt;Typical Design Problem&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SRP&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Command, Strategy, Decorator, Facade, Repository&lt;/td&gt;
&lt;td&gt;Too many independent responsibilities&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;OCP&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Strategy, Decorator, Template Method, Factory&lt;/td&gt;
&lt;td&gt;New behavior repeatedly requires modifying existing code&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;LSP&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Strategy, Adapter, Composition&lt;/td&gt;
&lt;td&gt;Incorrect or overly restrictive inheritance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ISP&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Adapter, Facade, Proxy&lt;/td&gt;
&lt;td&gt;Clients depend on unnecessary operations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;DIP&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Dependency Injection, Adapter, Repository, Factory&lt;/td&gt;
&lt;td&gt;Business logic depends directly on implementation details&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This is &lt;strong&gt;not a strict one-to-one mapping&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A pattern can support multiple SOLID principles, and a SOLID principle can be applied without using a design pattern.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Strategy
   ↓
Can support OCP
   ↓
Can also help SRP
   ↓
May also improve testability
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Likewise:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Adapter
   ↓
Can help isolate an external dependency
   ↓
Can support DIP
   ↓
Can expose a focused interface
   ↓
Can support ISP
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The correct approach is to start with the design problem, not the pattern name.&lt;/p&gt;




&lt;h1&gt;
  
  
  SOLID Does Not Mean "More Interfaces"
&lt;/h1&gt;

&lt;p&gt;One of the biggest misconceptions about SOLID is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"If I create interfaces everywhere, my code is SOLID."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Not necessarily.&lt;/p&gt;

&lt;p&gt;Consider:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;IUserService&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;CreateUser&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserService&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IUserService&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;CreateUser&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the interface provides no meaningful abstraction or architectural boundary, it may simply add indirection.&lt;/p&gt;

&lt;p&gt;Instead, ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Where do I actually need an abstraction?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Useful boundaries often include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;External systems&lt;/li&gt;
&lt;li&gt;Infrastructure&lt;/li&gt;
&lt;li&gt;Business strategies&lt;/li&gt;
&lt;li&gt;Multiple implementations&lt;/li&gt;
&lt;li&gt;Testing boundaries&lt;/li&gt;
&lt;li&gt;Plugin-style architectures&lt;/li&gt;
&lt;li&gt;Components that are expected to vary independently&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Abstraction has a cost.&lt;/p&gt;

&lt;p&gt;Use it where it provides a meaningful benefit.&lt;/p&gt;




&lt;h1&gt;
  
  
  SOLID Does Not Mean "Create More Classes"
&lt;/h1&gt;

&lt;p&gt;Another common mistake is over-fragmentation.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;OrderService
OrderValidator
OrderCalculator
OrderMapper
OrderLogger
OrderFormatter
OrderHelper
OrderUtility
OrderManager
OrderProcessor
OrderHandler
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Having many classes does not automatically mean the design is better.&lt;/p&gt;

&lt;p&gt;A highly fragmented design can introduce:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;More dependencies&lt;/li&gt;
&lt;li&gt;More files&lt;/li&gt;
&lt;li&gt;More abstractions&lt;/li&gt;
&lt;li&gt;More cognitive overhead&lt;/li&gt;
&lt;li&gt;More difficult debugging&lt;/li&gt;
&lt;li&gt;More complex dependency graphs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The objective is &lt;strong&gt;appropriate separation&lt;/strong&gt;, not maximum separation.&lt;/p&gt;




&lt;h1&gt;
  
  
  SOLID Does Not Mean "Remove Every if/else"
&lt;/h1&gt;

&lt;p&gt;Another misconception is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"SOLID means removing every if/else statement."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's not true.&lt;/p&gt;

&lt;p&gt;This can be perfectly reasonable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Total&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="m"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;ApplyDiscount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The question is whether the conditional represents a stable rule or whether it is continuously growing with independently changing behavior.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;paymentType&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"CreditCard"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;paymentType&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"UPI"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;paymentType&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"PayPal"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;paymentType&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"BankTransfer"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If this list continuously grows and each branch represents an independent behavior, the Strategy Pattern may be worth considering.&lt;/p&gt;

&lt;p&gt;The pattern should be introduced because it improves the design, not simply because an &lt;code&gt;if&lt;/code&gt; statement exists.&lt;/p&gt;




&lt;h1&gt;
  
  
  SOLID Does Not Mean "Use Every Design Pattern"
&lt;/h1&gt;

&lt;p&gt;Design patterns are tools.&lt;/p&gt;

&lt;p&gt;Using them everywhere can create unnecessary complexity.&lt;/p&gt;

&lt;p&gt;For example, a simple calculation does not need:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Abstract Factory
      ↓
Factory Method
      ↓
Strategy
      ↓
Decorator
      ↓
Mediator
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;when this may be sufficient:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;decimal&lt;/span&gt; &lt;span class="nf"&gt;CalculateTax&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;decimal&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="m"&gt;0.18m&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A good engineer knows not only how to apply patterns, but also when &lt;strong&gt;not&lt;/strong&gt; to apply them.&lt;/p&gt;




&lt;h1&gt;
  
  
  A Real-World Example: Notification System
&lt;/h1&gt;

&lt;p&gt;Let's bring several SOLID principles together using a notification system.&lt;/p&gt;

&lt;p&gt;Suppose an application needs to send:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Email&lt;/li&gt;
&lt;li&gt;SMS&lt;/li&gt;
&lt;li&gt;Push notifications&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A simple implementation might look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;NotificationService&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;recipient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"Email"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// Send email&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"SMS"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// Send SMS&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"Push"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// Send push notification&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It works.&lt;/p&gt;

&lt;p&gt;But what happens when we add:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;WhatsApp&lt;/li&gt;
&lt;li&gt;Microsoft Teams&lt;/li&gt;
&lt;li&gt;Slack&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The class keeps growing.&lt;/p&gt;




&lt;h2&gt;
  
  
  Introduce an Abstraction
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;INotificationSender&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;SendAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;recipient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Email:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;EmailNotificationSender&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;INotificationSender&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;SendAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;recipient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Send email&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CompletedTask&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;SMS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SmsNotificationSender&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;INotificationSender&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;SendAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;recipient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Send SMS&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CompletedTask&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Push:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PushNotificationSender&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;INotificationSender&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;SendAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;recipient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Send push notification&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CompletedTask&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The application service can work with the abstraction:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;NotificationService&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;INotificationSender&lt;/span&gt; &lt;span class="n"&gt;_sender&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;NotificationService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;INotificationSender&lt;/span&gt; &lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_sender&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;SendAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;recipient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;_sender&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SendAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;recipient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The resulting design is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NotificationService
        |
        ↓
INotificationSender
        ↑
        |
  +-----+-----+------+
  |           |      |
Email        SMS    Push
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Several principles are represented here.&lt;/p&gt;

&lt;h3&gt;
  
  
  SRP
&lt;/h3&gt;

&lt;p&gt;Each sender is responsible for one notification mechanism.&lt;/p&gt;

&lt;h3&gt;
  
  
  OCP
&lt;/h3&gt;

&lt;p&gt;A new notification mechanism can be introduced as another implementation without changing the existing senders.&lt;/p&gt;

&lt;h3&gt;
  
  
  LSP
&lt;/h3&gt;

&lt;p&gt;Each implementation should honor the behavioral contract defined by &lt;code&gt;INotificationSender&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  ISP
&lt;/h3&gt;

&lt;p&gt;The interface contains only the operation needed by notification senders.&lt;/p&gt;

&lt;h3&gt;
  
  
  DIP
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;NotificationService&lt;/code&gt; depends on &lt;code&gt;INotificationSender&lt;/code&gt;, not directly on an email, SMS, or push provider.&lt;/p&gt;

&lt;p&gt;This is a good example of how SOLID principles can work together rather than being treated as five isolated rules.&lt;/p&gt;




&lt;h1&gt;
  
  
  How to Identify SOLID Issues During Code Review
&lt;/h1&gt;

&lt;p&gt;Instead of checking the five principles mechanically, ask practical questions.&lt;/p&gt;

&lt;h2&gt;
  
  
  SRP Questions
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Does this class have unrelated responsibilities?&lt;/li&gt;
&lt;li&gt;Would different concerns cause different changes to this class?&lt;/li&gt;
&lt;li&gt;Does the class interact with too many unrelated technologies?&lt;/li&gt;
&lt;li&gt;Is the class becoming a "God class"?&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  OCP Questions
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Does adding a new behavior require modifying existing logic?&lt;/li&gt;
&lt;li&gt;Is a conditional structure continuously growing?&lt;/li&gt;
&lt;li&gt;Are new business rules repeatedly being added to the same class?&lt;/li&gt;
&lt;li&gt;Can the changing behavior be isolated?&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  LSP Questions
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Does a subclass throw &lt;code&gt;NotSupportedException&lt;/code&gt; for inherited behavior?&lt;/li&gt;
&lt;li&gt;Does a subclass change the expected meaning of a base method?&lt;/li&gt;
&lt;li&gt;Do consumers need to check the concrete subtype before using it?&lt;/li&gt;
&lt;li&gt;Is inheritance actually representing the required behavioral relationship?&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  ISP Questions
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Are implementations forced to implement irrelevant methods?&lt;/li&gt;
&lt;li&gt;Is the interface becoming very large?&lt;/li&gt;
&lt;li&gt;Do different consumers use completely different subsets of the interface?&lt;/li&gt;
&lt;li&gt;Can the contract be divided into meaningful capabilities?&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  DIP Questions
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Does business logic directly instantiate infrastructure classes?&lt;/li&gt;
&lt;li&gt;Does a service depend directly on a database provider?&lt;/li&gt;
&lt;li&gt;Does business logic know about external SDK implementation details?&lt;/li&gt;
&lt;li&gt;Can an infrastructure implementation be replaced without changing business logic?&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  A Practical SOLID Refactoring Workflow
&lt;/h1&gt;

&lt;p&gt;You do not need to redesign an entire application whenever you discover a SOLID issue.&lt;/p&gt;

&lt;p&gt;A practical workflow is:&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1 — Identify What Changes
&lt;/h2&gt;

&lt;p&gt;Ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;What is likely to change independently?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Examples:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Payment provider
Notification channel
Database
Pricing algorithm
Authentication mechanism
File storage provider
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 2 — Identify the Coupling
&lt;/h2&gt;

&lt;p&gt;Ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Which component knows too much about this changing behavior?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Step 3 — Identify the Responsibility
&lt;/h2&gt;

&lt;p&gt;Ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Can this behavior be separated without making the design unnecessarily complex?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Step 4 — Introduce an Abstraction When It Adds Value
&lt;/h2&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;IPaymentProcessor&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;ProcessAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;decimal&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 5 — Choose a Pattern If It Fits
&lt;/h2&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Multiple interchangeable algorithms
        ↓
Strategy Pattern
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;External system has an incompatible API
        ↓
Adapter Pattern
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Object creation is complex or needs encapsulation
        ↓
Factory Pattern
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 6 — Keep the Design Simple
&lt;/h2&gt;

&lt;p&gt;Do not introduce five abstractions when one abstraction solves the problem.&lt;/p&gt;




&lt;h1&gt;
  
  
  SOLID in a Typical .NET Application
&lt;/h1&gt;

&lt;p&gt;A typical enterprise application may have a structure similar to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                 API Controller
                       |
                       ↓
              Application Service
                       |
          +------------+------------+
          |            |            |
          ↓            ↓            ↓
     Repository     Payment     Notification
          |         Strategy       Service
          ↓            |
      Database      +--+--+
                    |     |
                  Card   UPI
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This does not mean every .NET application should have exactly this structure.&lt;/p&gt;

&lt;p&gt;It simply demonstrates how the principles can work together.&lt;/p&gt;

&lt;h3&gt;
  
  
  SRP
&lt;/h3&gt;

&lt;p&gt;Components have focused responsibilities.&lt;/p&gt;

&lt;h3&gt;
  
  
  OCP
&lt;/h3&gt;

&lt;p&gt;New payment strategies can be introduced without modifying existing payment strategies.&lt;/p&gt;

&lt;h3&gt;
  
  
  LSP
&lt;/h3&gt;

&lt;p&gt;Payment implementations should honor the abstraction's behavioral contract.&lt;/p&gt;

&lt;h3&gt;
  
  
  ISP
&lt;/h3&gt;

&lt;p&gt;Consumers can depend on focused interfaces.&lt;/p&gt;

&lt;h3&gt;
  
  
  DIP
&lt;/h3&gt;

&lt;p&gt;Application-level code depends on abstractions rather than infrastructure implementations.&lt;/p&gt;




&lt;h1&gt;
  
  
  SOLID vs Design Patterns
&lt;/h1&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;SOLID Principles&lt;/th&gt;
&lt;th&gt;Design Patterns&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Design principles&lt;/td&gt;
&lt;td&gt;Reusable design approaches&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Help guide design decisions&lt;/td&gt;
&lt;td&gt;Help solve recurring design problems&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;More abstract&lt;/td&gt;
&lt;td&gt;More concrete&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Help identify desirable design characteristics&lt;/td&gt;
&lt;td&gt;Provide implementation techniques&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Can be applied without design patterns&lt;/td&gt;
&lt;td&gt;Can be used independently of SOLID&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Focus on maintainability and flexibility&lt;/td&gt;
&lt;td&gt;Focus on recurring structural or behavioral problems&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A useful mental model is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;SOLID tells you what characteristics to aim for. Design patterns provide techniques that may help you achieve those characteristics.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  When Should You Not Apply SOLID Aggressively?
&lt;/h1&gt;

&lt;p&gt;SOLID is valuable, but context matters.&lt;/p&gt;

&lt;p&gt;For a small application with a simple and stable requirement:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Simple problem
     ↓
Simple implementation
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;may be better than:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Simple problem
     ↓
Multiple interfaces
     ↓
Multiple factories
     ↓
Multiple strategies
     ↓
Complex dependency graph
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a larger application with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Multiple teams&lt;/li&gt;
&lt;li&gt;Frequent requirement changes&lt;/li&gt;
&lt;li&gt;External integrations&lt;/li&gt;
&lt;li&gt;Complex business rules&lt;/li&gt;
&lt;li&gt;Long-term maintenance&lt;/li&gt;
&lt;li&gt;Significant automated testing requirements&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;appropriate abstractions can provide greater value.&lt;/p&gt;

&lt;p&gt;The right design depends on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Complexity&lt;/li&gt;
&lt;li&gt;Change frequency&lt;/li&gt;
&lt;li&gt;Number of implementations&lt;/li&gt;
&lt;li&gt;Team size&lt;/li&gt;
&lt;li&gt;Testing requirements&lt;/li&gt;
&lt;li&gt;Expected lifetime of the software&lt;/li&gt;
&lt;li&gt;Operational requirements&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;SOLID is a design guide, not a requirement to maximize abstraction.&lt;/p&gt;




&lt;h1&gt;
  
  
  A Simple SOLID Mental Model
&lt;/h1&gt;

&lt;p&gt;When reviewing a class, ask these five questions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. S — Does this class have too many independent reasons to change?

2. O — Do I repeatedly modify this code whenever a new behavior is introduced?

3. L — Can every implementation genuinely honor the behavioral contract
       of its abstraction?

4. I — Is the interface asking implementations to support things they
       don't need?

5. D — Does my business logic depend directly on infrastructure or
       concrete implementations?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These questions are often more useful during code reviews than simply memorizing five definitions.&lt;/p&gt;




&lt;h1&gt;
  
  
  SOLID Design Pattern Quick Reference
&lt;/h1&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Principle&lt;/th&gt;
&lt;th&gt;Common Design Smell&lt;/th&gt;
&lt;th&gt;Possible Refactoring&lt;/th&gt;
&lt;th&gt;Patterns / Techniques That May Help&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SRP&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;One class has unrelated responsibilities&lt;/td&gt;
&lt;td&gt;Extract cohesive responsibilities&lt;/td&gt;
&lt;td&gt;Command, Strategy, Decorator, Facade, Repository&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;OCP&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;New behavior repeatedly requires modifying existing code&lt;/td&gt;
&lt;td&gt;Encapsulate variation behind abstractions&lt;/td&gt;
&lt;td&gt;Strategy, Decorator, Template Method, Factory&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;LSP&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Derived type cannot honor the base contract&lt;/td&gt;
&lt;td&gt;Redesign the abstraction or prefer composition&lt;/td&gt;
&lt;td&gt;Strategy, Adapter, Composition&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ISP&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Large interface with irrelevant methods&lt;/td&gt;
&lt;td&gt;Split into cohesive capabilities&lt;/td&gt;
&lt;td&gt;Adapter, Facade, Proxy&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;DIP&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Business logic directly depends on infrastructure&lt;/td&gt;
&lt;td&gt;Introduce appropriate abstractions&lt;/td&gt;
&lt;td&gt;Dependency Injection, Adapter, Repository, Factory&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This table is a &lt;strong&gt;practical association&lt;/strong&gt;, not a strict rule.&lt;/p&gt;

&lt;p&gt;There is no requirement that a particular SOLID principle must always be implemented using a particular pattern.&lt;/p&gt;




&lt;h1&gt;
  
  
  Common SOLID Misconceptions
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Misconception 1: "SOLID means more interfaces."
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Reality:&lt;/strong&gt; Interfaces are useful when they represent meaningful abstractions or boundaries.&lt;/p&gt;




&lt;h2&gt;
  
  
  Misconception 2: "SOLID means every class should have one method."
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Reality:&lt;/strong&gt; SRP is about reasons to change, not method count.&lt;/p&gt;




&lt;h2&gt;
  
  
  Misconception 3: "SOLID means no if/else statements."
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Reality:&lt;/strong&gt; Conditional logic is perfectly valid. The question is whether independently changing behavior is becoming tightly coupled.&lt;/p&gt;




&lt;h2&gt;
  
  
  Misconception 4: "DIP and Dependency Injection are the same."
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Reality:&lt;/strong&gt; DIP is a design principle. Dependency Injection is one technique that can help implement it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Misconception 5: "Every SOLID problem requires a design pattern."
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Reality:&lt;/strong&gt; Sometimes a simple refactoring is the best solution.&lt;/p&gt;




&lt;h2&gt;
  
  
  Misconception 6: "Inheritance is always better than composition."
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Reality:&lt;/strong&gt; Inheritance is useful when the subtype genuinely satisfies the behavioral contract of the base abstraction. Composition can be preferable when behavior needs to vary independently.&lt;/p&gt;




&lt;h2&gt;
  
  
  Misconception 7: "Following SOLID guarantees good architecture."
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Reality:&lt;/strong&gt; SOLID addresses important object-oriented design concerns, but good architecture also requires consideration of performance, security, reliability, scalability, maintainability, operational complexity, and business requirements.&lt;/p&gt;




&lt;h1&gt;
  
  
  The Most Important Lesson
&lt;/h1&gt;

&lt;p&gt;When developers learn SOLID, it is tempting to memorize statements such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SRP → One responsibility

OCP → Open for extension, closed for modification

LSP → Subtypes must be substitutable

ISP → Small interfaces

DIP → Depend on abstractions
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These definitions are useful, but they are only the starting point.&lt;/p&gt;

&lt;p&gt;A more useful engineering mindset is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;What is changing?
       ↓
What is coupled to that change?
       ↓
Is the responsibility cohesive?
       ↓
Is the abstraction meaningful?
       ↓
Can the dependency be inverted?
       ↓
Would a design pattern simplify the solution?
       ↓
Is the resulting design actually easier to maintain?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is where SOLID becomes practical.&lt;/p&gt;




&lt;h1&gt;
  
  
  Final Takeaways
&lt;/h1&gt;

&lt;p&gt;SOLID is not about writing more code.&lt;/p&gt;

&lt;p&gt;It is about making change easier to manage.&lt;/p&gt;

&lt;p&gt;The five principles can be summarized as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SRP
→ Keep responsibilities focused.

OCP
→ Isolate expected variation so new behavior can be added with
  minimal modification to stable code.

LSP
→ Make abstractions behaviorally trustworthy.

ISP
→ Keep contracts focused on meaningful capabilities.

DIP
→ Keep high-level business logic independent from implementation details.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Design patterns and techniques can support these principles:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SRP → Command, Strategy, Decorator, Facade, Repository

OCP → Strategy, Decorator, Template Method, Factory

LSP → Strategy, Adapter, Composition

ISP → Adapter, Facade, Proxy

DIP → Dependency Injection, Adapter, Repository, Factory
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But there is an even more important lesson:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Don't start with a design pattern. Start with the problem.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Ask:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;What is changing?&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;What is tightly coupled to that change?&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Can the responsibility be separated?&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Is an abstraction actually useful?&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Would a design pattern simplify the solution?&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Does the resulting design reduce complexity rather than increase it?&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Good software design is not about following SOLID mechanically.&lt;/p&gt;

&lt;p&gt;It is about creating a codebase where &lt;strong&gt;responsibilities are clear, dependencies are manageable, expected changes are easier to accommodate, and complexity is kept under control&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The best SOLID design is not the one with the most abstractions.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;It is the one that makes the important changes easier without introducing unnecessary complexity.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Practical SOLID Checklist
&lt;/h2&gt;

&lt;p&gt;Before committing a significant design change, ask:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;□ Does each component have a clear and cohesive responsibility?

□ Can expected variations be added without repeatedly changing stable code?

□ Can implementations safely honor the contracts of their abstractions?

□ Are interfaces focused on meaningful capabilities?

□ Does business logic avoid unnecessary dependencies on infrastructure details?

□ Am I introducing an abstraction because I need it, rather than because
  SOLID tells me to?

□ Am I using a design pattern because it solves a real problem?

□ Has the solution become simpler or more maintainable?

□ Have I avoided unnecessary layers and abstractions?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the answer to these questions is clear, you are applying SOLID as an &lt;strong&gt;engineering principle&lt;/strong&gt;, rather than simply memorizing its definitions.&lt;/p&gt;




&lt;h1&gt;
  
  
  programming
&lt;/h1&gt;

&lt;h1&gt;
  
  
  csharp
&lt;/h1&gt;

&lt;h1&gt;
  
  
  dotnet
&lt;/h1&gt;

&lt;h1&gt;
  
  
  solid
&lt;/h1&gt;

&lt;h1&gt;
  
  
  designpatterns
&lt;/h1&gt;

&lt;h1&gt;
  
  
  softwarearchitecture
&lt;/h1&gt;

</description>
      <category>csharp</category>
      <category>dotnet</category>
      <category>solid</category>
      <category>designpatterns</category>
    </item>
    <item>
      <title>RAG Explained: A Beginner's Guide to Retrieval-Augmented Generation</title>
      <dc:creator>Chethan Ramaswamy</dc:creator>
      <pubDate>Sat, 26 Sep 2026 06:31:04 +0000</pubDate>
      <link>https://dev.to/chethanramaswamy/rag-explained-a-beginners-guide-to-retrieval-augmented-generation-2dn9</link>
      <guid>https://dev.to/chethanramaswamy/rag-explained-a-beginners-guide-to-retrieval-augmented-generation-2dn9</guid>
      <description>&lt;h2&gt;
  
  
  📑 Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Imagine You Have 10,000 Company Documents&lt;/li&gt;
&lt;li&gt;So, What Is RAG?&lt;/li&gt;
&lt;li&gt;A Simple Real-World Example&lt;/li&gt;
&lt;li&gt;
How Does RAG Actually Work?

&lt;ul&gt;
&lt;li&gt;Stage 1: Prepare the Knowledge&lt;/li&gt;
&lt;li&gt;Step 1: Collect Your Documents&lt;/li&gt;
&lt;li&gt;Step 2: Break Large Documents into Smaller Pieces&lt;/li&gt;
&lt;li&gt;Step 3: Create Embeddings&lt;/li&gt;
&lt;li&gt;Step 4: Store the Information&lt;/li&gt;
&lt;li&gt;Stage 2: Answer the User's Question&lt;/li&gt;
&lt;li&gt;Step 5: The User Asks a Question&lt;/li&gt;
&lt;li&gt;Step 6: Give the Information to the AI&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
The Complete RAG Flow

&lt;ul&gt;
&lt;li&gt;Preparing the knowledge&lt;/li&gt;
&lt;li&gt;Answering a question&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Is RAG Just Search?&lt;/li&gt;
&lt;li&gt;RAG vs Fine-Tuning&lt;/li&gt;
&lt;li&gt;
Where Can We Use RAG?

&lt;ul&gt;
&lt;li&gt;Developer Assistant&lt;/li&gt;
&lt;li&gt;HR Assistant&lt;/li&gt;
&lt;li&gt;Customer Support&lt;/li&gt;
&lt;li&gt;Enterprise Knowledge Assistant&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Does RAG Make AI 100% Accurate?&lt;/li&gt;
&lt;li&gt;What Does RAG Look Like with .NET and Azure?&lt;/li&gt;
&lt;li&gt;The 5 Things You Need to Remember&lt;/li&gt;
&lt;li&gt;Your Turn&lt;/li&gt;
&lt;li&gt;Key Takeaway&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you've started learning &lt;strong&gt;Generative AI&lt;/strong&gt;, you've probably come across terms like:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;LLM. Embeddings. Vector Search. RAG.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;At first, these terms can sound complicated.&lt;/p&gt;

&lt;p&gt;But the basic idea behind &lt;strong&gt;RAG&lt;/strong&gt; is actually quite simple.&lt;/p&gt;

&lt;p&gt;Let's understand it with a real-world example. 👇&lt;/p&gt;




&lt;h2&gt;
  
  
  🤔 Imagine You Have 10,000 Company Documents
&lt;/h2&gt;

&lt;p&gt;Imagine your company has thousands of documents:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;HR policies&lt;/li&gt;
&lt;li&gt;Travel policies&lt;/li&gt;
&lt;li&gt;Product documentation&lt;/li&gt;
&lt;li&gt;Technical guides&lt;/li&gt;
&lt;li&gt;Customer FAQs&lt;/li&gt;
&lt;li&gt;Security guidelines&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now an employee asks:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"How much can I claim for a hotel during business travel?"&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Would you expect a general AI model to know your company's specific policy?&lt;/p&gt;

&lt;p&gt;Probably not.&lt;/p&gt;

&lt;p&gt;The information is inside your company's documents.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This is where RAG becomes useful.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  💡 So, What Is RAG?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;RAG stands for Retrieval-Augmented Generation.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Think of RAG like this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Find the right information first, then ask the AI to answer using that information.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's the basic idea.&lt;/p&gt;

&lt;p&gt;A simplified flow looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User asks a question
        ↓
Find relevant information
        ↓
Give that information to the AI
        ↓
AI generates an answer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's see how this works.&lt;/p&gt;




&lt;h2&gt;
  
  
  🏨 A Simple Real-World Example
&lt;/h2&gt;

&lt;p&gt;Suppose your company's travel policy says:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Employees can claim hotel expenses up to ₹5,000 per night when traveling for business.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;An employee asks:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"What is the hotel reimbursement limit?"&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A RAG application can:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Search the company's travel policy.&lt;/li&gt;
&lt;li&gt;Find the relevant information.&lt;/li&gt;
&lt;li&gt;Provide that information to the AI.&lt;/li&gt;
&lt;li&gt;Generate an answer.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The AI can then respond:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"According to the travel policy, employees can claim hotel expenses up to ₹5,000 per night."&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Notice what happened?&lt;/p&gt;

&lt;p&gt;The AI didn't need to already know your company's travel policy.&lt;/p&gt;

&lt;p&gt;The application &lt;strong&gt;retrieved the relevant information first&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That's the basic idea behind RAG.&lt;/p&gt;




&lt;h2&gt;
  
  
  🔍 How Does RAG Actually Work?
&lt;/h2&gt;

&lt;p&gt;Let's break it down into a few simple steps.&lt;/p&gt;

&lt;p&gt;Don't worry — we'll keep the technical jargon to a minimum.&lt;/p&gt;

&lt;p&gt;A basic RAG application can be understood in &lt;strong&gt;two stages&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Prepare the knowledge&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Answer the user's question&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let's look at both.&lt;/p&gt;




&lt;h1&gt;
  
  
  📚 Stage 1: Prepare the Knowledge
&lt;/h1&gt;

&lt;p&gt;Before users can ask questions, we need to prepare the documents so that relevant information can be found quickly.&lt;/p&gt;

&lt;h3&gt;
  
  
  📄 Step 1: Collect Your Documents
&lt;/h3&gt;

&lt;p&gt;Your knowledge could come from many places:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;📄 PDF files&lt;/li&gt;
&lt;li&gt;📝 Word documents&lt;/li&gt;
&lt;li&gt;🌐 Websites&lt;/li&gt;
&lt;li&gt;🗄️ Databases&lt;/li&gt;
&lt;li&gt;☁️ Cloud storage&lt;/li&gt;
&lt;li&gt;📚 Knowledge bases&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Company Knowledge
       │
       ├── HR Policy.pdf
       ├── Travel Policy.pdf
       ├── Security Policy.pdf
       └── Product Manual.pdf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These documents become the &lt;strong&gt;knowledge source&lt;/strong&gt; for our AI application.&lt;/p&gt;




&lt;h3&gt;
  
  
  ✂️ Step 2: Break Large Documents into Smaller Pieces
&lt;/h3&gt;

&lt;p&gt;Imagine you have a &lt;strong&gt;100-page travel policy&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;We don't want to send all 100 pages to the AI every time someone asks a question.&lt;/p&gt;

&lt;p&gt;Instead, we divide the document into smaller pieces called &lt;strong&gt;chunks&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Travel Policy
      │
      ├── Chunk 1
      ├── Chunk 2
      ├── Chunk 3
      ├── Chunk 4
      └── ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why?&lt;/p&gt;

&lt;p&gt;Because when someone asks a question, we usually need only the &lt;strong&gt;small section related to that question&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Question:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
"What is the hotel reimbursement limit?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The system might retrieve:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Chunk 27:

"Employees can claim hotel expenses
up to ₹5,000 per night..."
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's much more useful than sending the entire document to the AI.&lt;/p&gt;

&lt;h3&gt;
  
  
  💡 Think of it this way
&lt;/h3&gt;

&lt;p&gt;Imagine giving a student a 500-page textbook and asking:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Find the paragraph about hotel reimbursement."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It would take time to search through the entire book.&lt;/p&gt;

&lt;p&gt;Instead, if we already know the relevant page, we can go directly there.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Chunking helps us create those smaller searchable pieces of information.&lt;/strong&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  🧠 Step 3: Create Embeddings
&lt;/h3&gt;

&lt;p&gt;Here's our first technical concept:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Embeddings.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Embeddings are numerical representations of text that help systems measure semantic similarity.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Embeddings help a system find information based on semantic similarity, rather than only matching exact words.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;User asks:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"How much can I claim for a hotel?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The document says:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Hotel accommodation expenses are limited to ₹5,000 per night."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The words aren't exactly the same.&lt;/p&gt;

&lt;p&gt;But the &lt;strong&gt;meaning is related&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Embeddings help the system identify this semantic similarity.&lt;/p&gt;

&lt;p&gt;You can think of it like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Text
 ↓
Embedding Model
 ↓
Numerical Vector
 ↓
Compare with other vectors
 ↓
Find semantically similar content
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You don't need to understand the mathematics behind embeddings to start learning RAG.&lt;/p&gt;

&lt;p&gt;The important thing to remember is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Embeddings help a system find information based on semantic similarity, rather than only matching exact words.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  🗂️ Step 4: Store the Information
&lt;/h3&gt;

&lt;p&gt;Now we need somewhere to store our document chunks and their embeddings.&lt;/p&gt;

&lt;p&gt;This is where &lt;strong&gt;vector databases and vector search systems&lt;/strong&gt; come in.&lt;/p&gt;

&lt;p&gt;Some popular options include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Azure AI Search&lt;/li&gt;
&lt;li&gt;PostgreSQL + pgvector&lt;/li&gt;
&lt;li&gt;Pinecone&lt;/li&gt;
&lt;li&gt;Qdrant&lt;/li&gt;
&lt;li&gt;Weaviate&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Think of this as a &lt;strong&gt;smart search system for your documents&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Conceptually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Document Chunk
      +
Embedding
      +
Metadata
      ↓
Vector Search
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Metadata can contain information such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Document: TravelPolicy.pdf
Department: Finance
Year: 2026
DocumentType: Policy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This additional information can help the application filter and organize the data.&lt;/p&gt;




&lt;h2&gt;
  
  
  🔎 Stage 2: Answer the User's Question
&lt;/h2&gt;

&lt;p&gt;Now our knowledge has been prepared.&lt;/p&gt;

&lt;p&gt;Let's see what happens when a user asks a question.&lt;/p&gt;

&lt;h3&gt;
  
  
  🔎 Step 5: The User Asks a Question
&lt;/h3&gt;

&lt;p&gt;The employee asks:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"What is the hotel reimbursement limit?"&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The application converts the question into an embedding and uses it to search the knowledge base.&lt;/p&gt;

&lt;p&gt;The search might return several results:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Result 1:
Employees can claim hotel expenses
up to ₹5,000 per night.

Result 2:
Business travel expenses must be
submitted within 30 days.

Result 3:
International travel requires
manager approval.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The application identifies the information that is &lt;strong&gt;most relevant to the question&lt;/strong&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  🤖 Step 6: Give the Information to the AI
&lt;/h3&gt;

&lt;p&gt;Now we have two important pieces:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Question:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What is the hotel reimbursement limit?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Relevant information:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Employees can claim hotel expenses up to ₹5,000 per night.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The application provides the question and retrieved context to the LLM.&lt;/p&gt;

&lt;p&gt;Conceptually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User Question
      +
Retrieved Context
      ↓
Prompt
      ↓
LLM
      ↓
Generated Answer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The LLM can then generate:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"Employees can claim hotel expenses up to ₹5,000 per night."&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And that's the &lt;strong&gt;Generation&lt;/strong&gt; part of &lt;em&gt;Retrieval-Augmented Generation&lt;/em&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  🧩 The Complete RAG Flow
&lt;/h2&gt;

&lt;p&gt;Now let's separate the process into two simple flows.&lt;/p&gt;

&lt;h3&gt;
  
  
  Preparing the knowledge
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Documents
    ↓
Chunking
    ↓
Embeddings
    ↓
Vector Search Index
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Answering a question
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User Question
      ↓
Question Embedding
      ↓
Search the Knowledge Base
      ↓
Relevant Chunks
      ↓
Question + Retrieved Context
      ↓
LLM
      ↓
Final Answer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the heart of a RAG application.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Retrieve relevant information → provide it as context → generate an answer.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  🔍 Is RAG Just Search?
&lt;/h2&gt;

&lt;p&gt;Not exactly.&lt;/p&gt;

&lt;p&gt;Traditional search might work like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Question
   ↓
Search
   ↓
List of Documents
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You then need to open the documents and find the answer yourself.&lt;/p&gt;

&lt;p&gt;RAG goes one step further:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Question
   ↓
Search Relevant Information
   ↓
Provide Context to AI
   ↓
Generate Answer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So you can think of RAG as:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Search + AI&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Traditional search helps you &lt;strong&gt;find information&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A RAG application helps an AI system &lt;strong&gt;retrieve relevant information and generate an answer using that information&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  🆚 RAG vs Fine-Tuning
&lt;/h2&gt;

&lt;p&gt;This is another question beginners often have:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"Why not fine-tune the model instead?"&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;RAG and fine-tuning solve different problems.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;RAG&lt;/th&gt;
&lt;th&gt;Fine-Tuning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Retrieves external information at query time&lt;/td&gt;
&lt;td&gt;Further trains the model&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Useful when knowledge changes frequently&lt;/td&gt;
&lt;td&gt;Useful for specialized behavior or tasks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Documents can be updated independently&lt;/td&gt;
&lt;td&gt;Requires a training process&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Commonly used for knowledge-based applications&lt;/td&gt;
&lt;td&gt;Commonly used for specialized model behavior&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A simple way to remember it:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;RAG → Give the AI relevant information.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fine-tuning → Adapt the AI for a specific behavior or task.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For example, if your company's HR policy changes next month, you can update the knowledge source used by a RAG application without retraining the language model.&lt;/p&gt;




&lt;h2&gt;
  
  
  🌎 Where Can We Use RAG?
&lt;/h2&gt;

&lt;p&gt;RAG isn't limited to company policies.&lt;/p&gt;

&lt;p&gt;It can be used in many real-world applications.&lt;/p&gt;

&lt;h3&gt;
  
  
  👨‍💻 Developer Assistant
&lt;/h3&gt;

&lt;p&gt;A developer asks:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"How does our authentication service work?"&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The RAG application searches the company's technical documentation and provides relevant context to the LLM.&lt;/p&gt;




&lt;h3&gt;
  
  
  🏢 HR Assistant
&lt;/h3&gt;

&lt;p&gt;An employee asks:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"How many vacation days can I carry forward?"&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The application searches HR policies and generates an answer based on the retrieved information.&lt;/p&gt;




&lt;h3&gt;
  
  
  🛠️ Customer Support
&lt;/h3&gt;

&lt;p&gt;A customer asks:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"How do I reset my device?"&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The application can search product documentation and provide step-by-step guidance.&lt;/p&gt;




&lt;h3&gt;
  
  
  📚 Enterprise Knowledge Assistant
&lt;/h3&gt;

&lt;p&gt;An employee asks:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"What is the process for raising a purchase request?"&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The application searches company procedures and generates a response.&lt;/p&gt;

&lt;p&gt;The common pattern is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Company Knowledge
       ↓
      RAG
       ↓
      LLM
       ↓
  AI Assistant
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  ⚠️ Does RAG Make AI 100% Accurate?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;No.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is important to understand.&lt;/p&gt;

&lt;p&gt;RAG can provide the AI with relevant information, but it does &lt;strong&gt;not guarantee a perfect answer&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For example, if the retrieval system finds the wrong information, the LLM may generate an incorrect response based on that context.&lt;/p&gt;

&lt;p&gt;There can also be problems if:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The source document contains incorrect information.&lt;/li&gt;
&lt;li&gt;Important information was not retrieved.&lt;/li&gt;
&lt;li&gt;The retrieved context is incomplete.&lt;/li&gt;
&lt;li&gt;The prompt doesn't clearly guide the model.&lt;/li&gt;
&lt;li&gt;The model misunderstands the retrieved context.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's why production RAG systems need good:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Document processing&lt;/li&gt;
&lt;li&gt;Search and retrieval&lt;/li&gt;
&lt;li&gt;Security&lt;/li&gt;
&lt;li&gt;Prompt design&lt;/li&gt;
&lt;li&gt;Evaluation&lt;/li&gt;
&lt;li&gt;Monitoring&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So don't think of RAG as a magic solution.&lt;/p&gt;

&lt;p&gt;Instead, think of it as a way to &lt;strong&gt;ground an AI application with relevant external information&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  ☁️ What Does RAG Look Like with .NET and Azure?
&lt;/h2&gt;

&lt;p&gt;If you're a .NET developer, a simple enterprise RAG architecture could look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                 User
                   ↓
              .NET Web API
                   ↓
             Azure AI Search
                   ↓
             Relevant Chunks
                   ↓
             Prompt + Context
                   ↓
             Azure OpenAI
                   ↓
              AI Response
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your .NET application can orchestrate the process:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Receive the user's question.&lt;/li&gt;
&lt;li&gt;Search for relevant information.&lt;/li&gt;
&lt;li&gt;Retrieve the relevant chunks.&lt;/li&gt;
&lt;li&gt;Build the prompt with the retrieved context.&lt;/li&gt;
&lt;li&gt;Send the request to the AI model.&lt;/li&gt;
&lt;li&gt;Return the response.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This makes RAG particularly interesting for developers working with &lt;strong&gt;.NET and Azure&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  🎯 The 5 Things You Need to Remember
&lt;/h2&gt;

&lt;p&gt;If you're completely new to RAG, remember these five concepts:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Documents&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Where your knowledge lives.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Chunks&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Smaller pieces of your documents.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Embeddings&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Numerical representations that help systems measure semantic similarity.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Retrieval&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Finding information relevant to the user's question.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Generation&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
The LLM uses the retrieved information to generate the answer.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Put them together:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Documents
    ↓
Chunks
    ↓
Embeddings
    ↓
Search
    ↓
Relevant Context
    ↓
LLM
    ↓
Answer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's &lt;strong&gt;Retrieval-Augmented Generation&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  💬 Your Turn
&lt;/h2&gt;

&lt;p&gt;Have you tried building a RAG application?&lt;/p&gt;

&lt;p&gt;What would you like to build with RAG?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;👨‍💻 Developer assistant&lt;/li&gt;
&lt;li&gt;📄 Document chatbot&lt;/li&gt;
&lt;li&gt;🏢 Enterprise knowledge assistant&lt;/li&gt;
&lt;li&gt;🛠️ Customer support assistant&lt;/li&gt;
&lt;li&gt;🤖 Something else&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Share your idea in the comments. 👇&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  📌 Key Takeaway
&lt;/h2&gt;

&lt;p&gt;If you remember just one sentence from this article, remember this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;A RAG application retrieves relevant information from a knowledge source and provides it to an LLM so it can generate a more informed response.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And that's the foundation of many modern &lt;strong&gt;Generative AI applications&lt;/strong&gt;.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>generativeai</category>
      <category>rag</category>
    </item>
    <item>
      <title>Designing a Scalable Notification System using .NET, Azure Service Bus, MediatR, and SignalR</title>
      <dc:creator>Chethan Ramaswamy</dc:creator>
      <pubDate>Tue, 31 Mar 2026 19:35:49 +0000</pubDate>
      <link>https://dev.to/chethanramaswamy/designing-a-scalable-notification-system-using-net-azure-service-bus-mediatr-and-signalr-5701</link>
      <guid>https://dev.to/chethanramaswamy/designing-a-scalable-notification-system-using-net-azure-service-bus-mediatr-and-signalr-5701</guid>
      <description>&lt;h2&gt;
  
  
  📌 What We'll Cover
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Why I'm Writing This&lt;/li&gt;
&lt;li&gt;The Problem with the "Simple" Approach&lt;/li&gt;
&lt;li&gt;A Better Approach: Event-Driven Notifications&lt;/li&gt;
&lt;li&gt;High-Level Architecture&lt;/li&gt;
&lt;li&gt;Where MediatR Fits&lt;/li&gt;
&lt;li&gt;
Why Azure Service Bus?

&lt;ul&gt;
&lt;li&gt;Loose Coupling&lt;/li&gt;
&lt;li&gt;Retry and Failure Handling&lt;/li&gt;
&lt;li&gt;Independent Scaling&lt;/li&gt;
&lt;li&gt;Future Integrations&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Topics and Subscriptions&lt;/li&gt;
&lt;li&gt;SignalR for Real-Time Notifications&lt;/li&gt;
&lt;li&gt;SignalR at Scale&lt;/li&gt;
&lt;li&gt;
End-to-End Flow

&lt;ul&gt;
&lt;li&gt;Step 1 — User Performs an Action&lt;/li&gt;
&lt;li&gt;Step 2 — API Receives the Request&lt;/li&gt;
&lt;li&gt;Step 3 — Command Handler Processes the Business Operation&lt;/li&gt;
&lt;li&gt;Step 4 — Outbox Processor Publishes the Event&lt;/li&gt;
&lt;li&gt;Step 5 — Consumers Process the Event&lt;/li&gt;
&lt;li&gt;Step 6 — Real-Time Update Reaches the Client&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;One Important Production Consideration: The Outbox Pattern&lt;/li&gt;
&lt;li&gt;Handling Duplicate Messages&lt;/li&gt;
&lt;li&gt;Trade-Offs&lt;/li&gt;
&lt;li&gt;
Where This Architecture Works Well

&lt;ul&gt;
&lt;li&gt;E-commerce&lt;/li&gt;
&lt;li&gt;Banking&lt;/li&gt;
&lt;li&gt;Monitoring Platforms&lt;/li&gt;
&lt;li&gt;Enterprise Applications&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
Key Design Principles

&lt;ul&gt;
&lt;li&gt;MediatR&lt;/li&gt;
&lt;li&gt;Azure Service Bus&lt;/li&gt;
&lt;li&gt;Azure Service Bus Topics and Subscriptions&lt;/li&gt;
&lt;li&gt;SignalR&lt;/li&gt;
&lt;li&gt;Azure SignalR Service&lt;/li&gt;
&lt;li&gt;Outbox Pattern&lt;/li&gt;
&lt;li&gt;Idempotent Consumers&lt;/li&gt;
&lt;li&gt;Dead-Letter Handling&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Final Thoughts&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why I'm Writing This
&lt;/h2&gt;

&lt;p&gt;“Send a notification when something happens.”&lt;/p&gt;

&lt;p&gt;It sounds simple.&lt;/p&gt;

&lt;p&gt;But in a real application, notifications can quickly become more complicated than expected.&lt;/p&gt;

&lt;p&gt;You may need to support:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Real-time UI notifications&lt;/li&gt;
&lt;li&gt;Email and SMS&lt;/li&gt;
&lt;li&gt;Multiple consumers&lt;/li&gt;
&lt;li&gt;Retries and failure handling&lt;/li&gt;
&lt;li&gt;High traffic&lt;/li&gt;
&lt;li&gt;Future integrations&lt;/li&gt;
&lt;li&gt;Loose coupling between business logic and notification processing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The challenge is not simply &lt;strong&gt;how to send a notification&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The real question is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;How do we design notifications so they don't become tightly coupled to the core business flow?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This article walks through one approach using &lt;strong&gt;.NET, MediatR, Azure Service Bus, SignalR, and the Outbox Pattern&lt;/strong&gt;.&lt;/p&gt;




&lt;h1&gt;
  
  
  The Problem with the "Simple" Approach
&lt;/h1&gt;

&lt;p&gt;A common implementation starts with something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;API
  ↓
Save to Database
  ↓
Send Email
  ↓
Send Notification
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It works.&lt;/p&gt;

&lt;p&gt;At least initially.&lt;/p&gt;

&lt;p&gt;But as the application grows, problems start appearing.&lt;/p&gt;

&lt;p&gt;Every feature begins calling notification logic directly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;OrderService → NotificationService
PaymentService → NotificationService
UserService → NotificationService
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then new requirements arrive:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add SMS&lt;/li&gt;
&lt;li&gt;Add push notifications&lt;/li&gt;
&lt;li&gt;Add in-app notifications&lt;/li&gt;
&lt;li&gt;Add analytics&lt;/li&gt;
&lt;li&gt;Add audit events&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The notification service gradually becomes tightly coupled to multiple parts of the application.&lt;/p&gt;

&lt;p&gt;Another problem is failure handling.&lt;/p&gt;

&lt;p&gt;What happens if the email provider is temporarily unavailable?&lt;/p&gt;

&lt;p&gt;Should the order creation fail?&lt;/p&gt;

&lt;p&gt;Usually, the answer is &lt;strong&gt;no&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The business operation and notification delivery have different reliability requirements.&lt;/p&gt;




&lt;h1&gt;
  
  
  A Better Approach: Event-Driven Notifications
&lt;/h1&gt;

&lt;p&gt;The basic idea is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Business Action
      ↓
Domain/Application Event
      ↓
Outbox
      ↓
Message Broker
      ↓
Notification Consumers
      ↓
Email / UI / SMS / Push / etc.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The business operation completes without waiting for every notification channel to finish.&lt;/p&gt;

&lt;p&gt;This creates a much cleaner boundary between:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Something happened"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;and&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"What should we do because it happened?"&lt;/strong&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  High-Level Architecture
&lt;/h1&gt;

&lt;p&gt;The production-oriented architecture looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                         ┌──────────────────┐
                         │      Client      │
                         └────────┬─────────┘
                                  │
                                  ▼
                         ┌──────────────────┐
                         │   .NET Web API   │
                         └────────┬─────────┘
                                  │
                                  ▼
                         ┌──────────────────┐
                         │     MediatR      │
                         │ Command Handler  │
                         └────────┬─────────┘
                                  │
                                  ▼
                     ┌──────────────────────────┐
                     │    Database Transaction  │
                     │                          │
                     │  ┌────────┐ ┌─────────┐ │
                     │  │ Order  │ │ Outbox  │ │
                     │  └────────┘ └─────────┘ │
                     └────────────┬─────────────┘
                                  │
                                  ▼
                         ┌──────────────────┐
                         │ Outbox Processor │
                         └────────┬─────────┘
                                  │
                                  ▼
                     ┌────────────────────────┐
                     │  Azure Service Bus     │
                     │        Topic           │
                     └────────────┬───────────┘
                                  │
                   ┌──────────────┼──────────────┐
                   │              │              │
                   ▼              ▼              ▼
             ┌──────────┐   ┌───────────┐  ┌──────────┐
             │  Email   │   │  SignalR  │  │  Audit   │
             │ Consumer │   │  Consumer │  │ Consumer │
             └──────────┘   └─────┬─────┘  └──────────┘
                                  │
                                  ▼
                     ┌────────────────────────┐
                     │ Azure SignalR Service  │
                     └────────────┬───────────┘
                                  │
                                  ▼
                           ┌─────────────┐
                           │   Clients   │
                           │Browser/Mobile│
                           └─────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important principle is that each component has a specific responsibility.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The Outbox is part of the primary architecture, not an afterthought.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  Where MediatR Fits
&lt;/h1&gt;

&lt;p&gt;MediatR sits primarily inside the application layer.&lt;/p&gt;

&lt;p&gt;Instead of having a controller directly coordinate business operations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Controller
    ↓
Service
    ↓
Database
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;we can use a request/handler model:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Controller
    ↓
MediatR
    ↓
Command Handler
    ↓
Business Logic
    ↓
Database
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;record&lt;/span&gt; &lt;span class="nc"&gt;CreateOrderCommand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;CustomerId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kt"&gt;decimal&lt;/span&gt; &lt;span class="n"&gt;Amount&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IRequest&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The handler processes the command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CreateOrderHandler&lt;/span&gt;
    &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IRequestHandler&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;CreateOrderCommand&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;Handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;CreateOrderCommand&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;CancellationToken&lt;/span&gt; &lt;span class="n"&gt;cancellationToken&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Validate business rules&lt;/span&gt;
        &lt;span class="c1"&gt;// Create order&lt;/span&gt;
        &lt;span class="c1"&gt;// Save order&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;orderId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;MediatR also provides a clean place for application-level cross-cutting concerns such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Validation&lt;/li&gt;
&lt;li&gt;Logging&lt;/li&gt;
&lt;li&gt;Performance measurement&lt;/li&gt;
&lt;li&gt;Authorization&lt;/li&gt;
&lt;li&gt;Transaction behavior&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The important point is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;MediatR is not the message broker.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It helps with &lt;strong&gt;in-process application decoupling&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Azure Service Bus handles &lt;strong&gt;distributed messaging&lt;/strong&gt; between processes or services.&lt;/p&gt;

&lt;p&gt;That distinction becomes important as the system grows.&lt;/p&gt;




&lt;h1&gt;
  
  
  Why Azure Service Bus?
&lt;/h1&gt;

&lt;p&gt;After the business operation is committed, an event such as &lt;code&gt;OrderCreated&lt;/code&gt; needs to be delivered to downstream consumers.&lt;/p&gt;

&lt;p&gt;With the Outbox Pattern, the event is first persisted as part of the same database transaction and is then published asynchronously.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"eventType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"OrderCreated"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"orderId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;12345&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"customerId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1001&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The resulting flow becomes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Order Service
     ↓
Outbox
     ↓
Outbox Processor
     ↓
Azure Service Bus
     ↓
Notification Consumers
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Azure Service Bus provides the messaging infrastructure between the producer and consumers.&lt;/p&gt;

&lt;p&gt;Instead of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Order Service
     ↓
Notification Service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;we have:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Order Service
     ↓
Outbox
     ↓
Azure Service Bus
     ↓
Notification Consumers
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This provides several benefits.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Loose Coupling
&lt;/h2&gt;

&lt;p&gt;The producer doesn't need to know which consumers exist.&lt;/p&gt;

&lt;p&gt;Today:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;OrderCreated
    ↓
Email
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Tomorrow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;OrderCreated
    ↓
Email
SMS
Analytics
Audit
Push Notification
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The order-processing logic doesn't need to change simply because another consumer was added.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Retry and Failure Handling
&lt;/h2&gt;

&lt;p&gt;Azure Service Bus provides messaging and delivery mechanisms, while retry behavior is typically implemented through the Service Bus client and consumer/application logic.&lt;/p&gt;

&lt;p&gt;Transient failures can therefore be handled without making the original business transaction depend on downstream notification delivery.&lt;/p&gt;

&lt;p&gt;Messages that repeatedly fail processing can also be moved to a &lt;strong&gt;dead-letter queue&lt;/strong&gt; for investigation or later recovery.&lt;/p&gt;

&lt;p&gt;The exact retry strategy should be designed at the consumer level based on the type of failure.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Message
   ↓
Consumer
   ↓
Processing succeeds?
   ├── Yes → Complete message
   │
   └── No
       ↓
   Retry / Redelivery
       ↓
   Repeated failure?
       └── Dead-letter
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  3. Independent Scaling
&lt;/h2&gt;

&lt;p&gt;The notification consumers can scale independently from the API.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;API instances
     ↓
Outbox Processor
     ↓
Service Bus
     ↓
Notification Workers
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If notification volume increases significantly, the consumer side can be scaled independently.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Future Integrations
&lt;/h2&gt;

&lt;p&gt;The same event can potentially be consumed by additional systems without modifying the original producer.&lt;/p&gt;

&lt;p&gt;This is particularly useful in larger enterprise environments.&lt;/p&gt;




&lt;h1&gt;
  
  
  Topics and Subscriptions
&lt;/h1&gt;

&lt;p&gt;For multiple consumers, an Azure Service Bus &lt;strong&gt;Topic&lt;/strong&gt; with multiple &lt;strong&gt;Subscriptions&lt;/strong&gt; is a natural fit.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                  OrderCreated Event
                         │
                         ▼
                ┌─────────────────┐
                │ Azure Service   │
                │ Bus Topic       │
                └────────┬────────┘
                         │
             ┌───────────┼───────────┐
             │           │           │
             ▼           ▼           ▼
        Email Sub    SignalR Sub   Audit Sub
             │           │           │
             ▼           ▼           ▼
          Email       SignalR       Audit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each subscription can receive its own copy of the message.&lt;/p&gt;

&lt;p&gt;This keeps consumers independent.&lt;/p&gt;

&lt;p&gt;It also means that one consumer can fail or be delayed without preventing another subscription from processing its own copy of the event.&lt;/p&gt;




&lt;h1&gt;
  
  
  SignalR for Real-Time Notifications
&lt;/h1&gt;

&lt;p&gt;For browser-based real-time notifications, polling can introduce unnecessary latency and traffic.&lt;/p&gt;

&lt;p&gt;SignalR allows the server to push messages to connected clients.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;OrderCreated Event
        ↓
SignalR Consumer
        ↓
SignalR Hub
        ↓
Azure SignalR Service
        ↓
Connected Client
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The client can receive something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"Order #12345 has been created successfully."
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This works particularly well for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Dashboards&lt;/li&gt;
&lt;li&gt;Order tracking&lt;/li&gt;
&lt;li&gt;Monitoring systems&lt;/li&gt;
&lt;li&gt;Alerts&lt;/li&gt;
&lt;li&gt;Chat-like applications&lt;/li&gt;
&lt;li&gt;In-app notifications&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A SignalR connection can also be associated with a user or group, allowing notifications to be targeted appropriately.&lt;/p&gt;




&lt;h1&gt;
  
  
  SignalR at Scale
&lt;/h1&gt;

&lt;p&gt;A single SignalR server is straightforward.&lt;/p&gt;

&lt;p&gt;The challenge appears when the application is deployed across multiple instances.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                       ┌──────────────────────┐
                       │   Azure SignalR      │
                       │      Service         │
                       └──────────┬───────────┘
                                  │
                    ┌─────────────┼─────────────┐
                    │             │             │
                    ▼             ▼             ▼
               API Instance   API Instance   API Instance
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For Azure-hosted applications, &lt;strong&gt;Azure SignalR Service&lt;/strong&gt; can provide the scale-out layer for SignalR connections.&lt;/p&gt;

&lt;p&gt;This allows application instances to remain focused on application processing while Azure SignalR Service manages persistent client connections.&lt;/p&gt;

&lt;p&gt;The exact deployment model depends on application scale, hosting model, and operational requirements.&lt;/p&gt;




&lt;h1&gt;
  
  
  End-to-End Flow
&lt;/h1&gt;

&lt;p&gt;Let's put everything together.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1 — User performs an action
&lt;/h2&gt;

&lt;p&gt;The user creates an order.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Client → POST /orders
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 2 — API receives the request
&lt;/h2&gt;

&lt;p&gt;The controller sends a command through MediatR.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Controller
    ↓
CreateOrderCommand
    ↓
MediatR
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 3 — Command handler processes the business operation
&lt;/h2&gt;

&lt;p&gt;The handler validates the request and creates the order.&lt;/p&gt;

&lt;p&gt;The order and the corresponding outbox event are persisted in the same database transaction.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Database Transaction
       │
       ├── Order
       │
       └── Outbox Event
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is important because it prevents the application from successfully committing the order while completely losing the event because of a separate publish operation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4 — Outbox processor publishes the event
&lt;/h2&gt;

&lt;p&gt;A background process reads unpublished events from the outbox and publishes them to Azure Service Bus.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Database
   ↓
Outbox Processor
   ↓
Azure Service Bus Topic
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The business transaction does not wait for the downstream consumers to finish.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5 — Consumers process the event
&lt;/h2&gt;

&lt;p&gt;Different consumers can react independently.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                 OrderCreated
                      ↓
              Azure Service Bus
                 /     |      \
                /      |       \
               ▼       ▼        ▼
            Email   SignalR    Audit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 6 — Real-time update reaches the client
&lt;/h2&gt;

&lt;p&gt;The SignalR consumer sends an update to the appropriate connected client.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Service Bus
     ↓
SignalR Consumer
     ↓
SignalR Hub
     ↓
Azure SignalR Service
     ↓
Browser / Mobile
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key principle is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Order creation should not depend on notification delivery succeeding.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  One Important Production Consideration: The Outbox Pattern
&lt;/h1&gt;

&lt;p&gt;There is a subtle issue with a simpler implementation.&lt;/p&gt;

&lt;p&gt;Consider this sequence:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. Save Order
2. Publish OrderCreated
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What happens if step 1 succeeds but step 2 fails?&lt;/p&gt;

&lt;p&gt;The order exists in the database, but the event was never published.&lt;/p&gt;

&lt;p&gt;This is a classic &lt;strong&gt;dual-write problem&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For production systems where reliable event publishing is important, an &lt;strong&gt;Outbox Pattern&lt;/strong&gt; can help.&lt;/p&gt;

&lt;p&gt;Instead of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Save Order
     ↓
Publish Event
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;we persist both the business data and the outgoing event in the same database transaction:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Database Transaction
       │
       ├── Order
       │
       └── Outbox Event
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A background worker then reads the outbox and publishes events to Service Bus:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Database
   ↓
Outbox Processor
   ↓
Azure Service Bus
   ↓
Consumers
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This separates the critical business transaction from downstream message delivery while preserving the event that needs to be published.&lt;/p&gt;




&lt;h1&gt;
  
  
  Handling Duplicate Messages
&lt;/h1&gt;

&lt;p&gt;Messaging systems should generally be designed with the assumption that a message can potentially be delivered more than once.&lt;/p&gt;

&lt;p&gt;Azure Service Bus commonly uses &lt;strong&gt;at-least-once delivery semantics&lt;/strong&gt; with Peek-Lock processing, so consumers should be designed to handle duplicate delivery safely.&lt;/p&gt;

&lt;p&gt;That means consumers should ideally be &lt;strong&gt;idempotent&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Message ID: 8f23...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The consumer can maintain a record of processed message IDs.&lt;/p&gt;

&lt;p&gt;If the same message arrives again:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Message
   ↓
Already processed?
   ├── Yes → Ignore / safely return
   └── No  → Process
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This becomes especially important when processing operations such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sending emails&lt;/li&gt;
&lt;li&gt;Updating notification state&lt;/li&gt;
&lt;li&gt;Creating records&lt;/li&gt;
&lt;li&gt;Calling external APIs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Azure Service Bus also provides &lt;strong&gt;duplicate detection&lt;/strong&gt; capabilities, but duplicate detection should not be treated as a replacement for idempotent consumer design.&lt;/p&gt;




&lt;h1&gt;
  
  
  Trade-Offs
&lt;/h1&gt;

&lt;p&gt;This architecture is not free.&lt;/p&gt;

&lt;p&gt;You are introducing additional infrastructure:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Azure Service Bus&lt;/li&gt;
&lt;li&gt;Outbox storage&lt;/li&gt;
&lt;li&gt;Background consumers&lt;/li&gt;
&lt;li&gt;SignalR / Azure SignalR Service&lt;/li&gt;
&lt;li&gt;Monitoring&lt;/li&gt;
&lt;li&gt;Retry handling&lt;/li&gt;
&lt;li&gt;Dead-letter handling&lt;/li&gt;
&lt;li&gt;Distributed tracing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There is also eventual consistency.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Order Created
      ↓
Outbox Event
      ↓
Event Published
      ↓
Consumer Processes Event
      ↓
Notification Appears
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The notification might appear milliseconds or seconds after the business operation.&lt;/p&gt;

&lt;p&gt;But that trade-off can be worthwhile when the system needs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Scalability&lt;/li&gt;
&lt;li&gt;Resilience&lt;/li&gt;
&lt;li&gt;Independent consumers&lt;/li&gt;
&lt;li&gt;Extensibility&lt;/li&gt;
&lt;li&gt;Loose coupling&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The architecture should still be evaluated against the actual requirements.&lt;/p&gt;

&lt;p&gt;A small application with one notification channel may not need all of these components.&lt;/p&gt;




&lt;h1&gt;
  
  
  Where This Architecture Works Well
&lt;/h1&gt;

&lt;p&gt;This pattern can be useful in systems such as:&lt;/p&gt;

&lt;h2&gt;
  
  
  E-commerce
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;OrderPlaced
    ↓
Email
Inventory
Payment
Shipping
Customer Notification
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Banking
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TransactionCompleted
    ↓
SMS
Email
Mobile Push
Audit
Fraud Monitoring
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Monitoring Platforms
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AlertRaised
    ↓
Dashboard
Email
SMS
Incident Management
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Enterprise Applications
&lt;/h2&gt;

&lt;p&gt;Any system where a business event may need to trigger multiple independent downstream actions can benefit from this approach.&lt;/p&gt;




&lt;h1&gt;
  
  
  Key Design Principles
&lt;/h1&gt;

&lt;p&gt;The technology is important, but the architectural principles matter more.&lt;/p&gt;

&lt;h3&gt;
  
  
  MediatR
&lt;/h3&gt;

&lt;p&gt;Use it for &lt;strong&gt;in-process application decoupling&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Azure Service Bus
&lt;/h3&gt;

&lt;p&gt;Use it for &lt;strong&gt;asynchronous communication between distributed components&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Azure Service Bus Topics and Subscriptions
&lt;/h3&gt;

&lt;p&gt;Use them when multiple independent consumers need to react to the same event.&lt;/p&gt;

&lt;h3&gt;
  
  
  SignalR
&lt;/h3&gt;

&lt;p&gt;Use it for &lt;strong&gt;real-time communication with connected clients&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Azure SignalR Service
&lt;/h3&gt;

&lt;p&gt;Consider it when SignalR needs to scale across multiple application instances in Azure.&lt;/p&gt;

&lt;h3&gt;
  
  
  Outbox Pattern
&lt;/h3&gt;

&lt;p&gt;Use it when you need stronger guarantees between &lt;strong&gt;database state and event publishing&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Idempotent Consumers
&lt;/h3&gt;

&lt;p&gt;Design consumers so duplicate messages don't create unintended side effects.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dead-Letter Handling
&lt;/h3&gt;

&lt;p&gt;Treat repeatedly failed messages as operational data that needs monitoring, investigation, and potentially controlled replay.&lt;/p&gt;




&lt;h1&gt;
  
  
  Final Thoughts
&lt;/h1&gt;

&lt;p&gt;Notifications are easy to implement when a system is small.&lt;/p&gt;

&lt;p&gt;They become much more interesting when the application needs to scale, support multiple channels, handle failures, and evolve over time.&lt;/p&gt;

&lt;p&gt;A useful separation is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Business Logic
      ↓
Database Transaction
      ↓
Outbox Event
      ↓
Message Broker
      ↓
Independent Consumers
      ↓
Notification Channels
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this approach, the core business operation doesn't need to know whether the notification is delivered through email, SignalR, SMS, push notifications, or a future integration.&lt;/p&gt;

&lt;p&gt;The result is an architecture that is more &lt;strong&gt;decoupled, scalable, resilient, and easier to evolve&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;And perhaps the most important lesson:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Don't let notification delivery become part of your critical business transaction unless it truly needs to be.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you've implemented a similar architecture, I'd be interested in hearing how you handled retries, ordering, duplicate messages, dead-letter queues, and the outbox problem.&lt;/p&gt;




&lt;h1&gt;
  
  
  dotnet #azure #systemdesign #microservices #softwarearchitecture #azureservicebus #signalr #mediatr #eventdrivenarchitecture
&lt;/h1&gt;

</description>
      <category>systemdesign</category>
      <category>dotnet</category>
      <category>azure</category>
      <category>architecture</category>
    </item>
  </channel>
</rss>
