<?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: victor mwangi</title>
    <description>The latest articles on DEV Community by victor mwangi (@victor_mwangi_d224324203c).</description>
    <link>https://dev.to/victor_mwangi_d224324203c</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3843544%2F4e60083a-245a-49d9-b3ec-658eb382959b.jpg</url>
      <title>DEV Community: victor mwangi</title>
      <link>https://dev.to/victor_mwangi_d224324203c</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/victor_mwangi_d224324203c"/>
    <language>en</language>
    <item>
      <title>Stop Using useEffect for Data Fetching: Understanding TanStack Query</title>
      <dc:creator>victor mwangi</dc:creator>
      <pubDate>Wed, 27 May 2026 16:28:02 +0000</pubDate>
      <link>https://dev.to/victor_mwangi_d224324203c/stop-using-useeffect-for-data-fetching-understanding-tanstack-query-10gf</link>
      <guid>https://dev.to/victor_mwangi_d224324203c/stop-using-useeffect-for-data-fetching-understanding-tanstack-query-10gf</guid>
      <description>&lt;p&gt;If you are still fetching data inside a useEffect hook, manually managing loading states, and storing server responses inside local component state, your frontend architecture is carrying unnecessary complexity.&lt;/p&gt;

&lt;p&gt;Managing asynchronous API data — also called Server State — is one of the hardest problems in frontend development. Trying to handle it with traditional client-side state tools like Redux, Zustand, or plain React state often leads to repetitive, fragile, and difficult-to-maintain codebases.&lt;/p&gt;

&lt;p&gt;That is where TanStack Query changes everything.&lt;/p&gt;

&lt;p&gt;It is not just a data-fetching library. It acts as:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;An intelligent cache&lt;br&gt;
A server-state manager&lt;br&gt;
A synchronization layer between your frontend and backend&lt;br&gt;
A background updater for stale data&lt;/em&gt;&lt;br&gt;
Let’s break down how it actually works.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Client State vs Server State&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Before understanding TanStack Query, you need to separate two completely different categories of state.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;1. Client State&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This is synchronous state that exists entirely inside the browser.&lt;/p&gt;

&lt;p&gt;Examples:&lt;/p&gt;

&lt;p&gt;Modal visibility&lt;br&gt;
Theme toggles&lt;br&gt;
Sidebar state&lt;br&gt;
Form inputs&lt;/p&gt;

&lt;p&gt;This data is fully controlled by the frontend.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;2. Server State&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This is asynchronous state that lives remotely on a backend server or database.&lt;/p&gt;

&lt;p&gt;Examples:&lt;/p&gt;

&lt;p&gt;Product lists&lt;br&gt;
User profiles&lt;br&gt;
Notifications&lt;br&gt;
Dashboard analytics&lt;br&gt;
Orders from an API&lt;/p&gt;

&lt;p&gt;Server state has unique challenges:&lt;/p&gt;

&lt;p&gt;You do not own the data&lt;br&gt;
Multiple users can modify it&lt;br&gt;
It can become outdated instantly&lt;br&gt;
It requires async network requests&lt;br&gt;
It needs synchronization&lt;/p&gt;

&lt;p&gt;Using useState + useEffect forces server state to behave like client state.&lt;/p&gt;

&lt;p&gt;That creates problems like:&lt;/p&gt;

&lt;p&gt;Duplicate requests&lt;br&gt;
Boilerplate loading logic&lt;br&gt;
Manual error handling&lt;br&gt;
Race conditions&lt;br&gt;
No caching&lt;br&gt;
Difficult synchronization&lt;/p&gt;

&lt;p&gt;TanStack Query solves these automatically.&lt;/p&gt;

&lt;p&gt;Declarative Data Fetching with useQuery&lt;/p&gt;

&lt;p&gt;TanStack Query replaces imperative fetching with a declarative approach.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The core building blocks are:&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Query Key&lt;br&gt;
Query Function&lt;/p&gt;

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

&lt;p&gt;_**&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { useQuery } from '@tanstack/react-query';

function ProductList() {
  const { data, isLoading, isError, error } = useQuery({
    queryKey: ['products'],
    queryFn: async () =&amp;gt; {
      const res = await fetch('https://api.yourdomain.com/products');

      if (!res.ok) {
        throw new Error('Failed to fetch products');
      }

      return res.json();
    },
  });

  if (isLoading) {
    return &amp;lt;div&amp;gt;Loading products...&amp;lt;/div&amp;gt;;
  }

  if (isError) {
    return &amp;lt;div&amp;gt;Error: {error.message}&amp;lt;/div&amp;gt;;
  }

  return (
    &amp;lt;ul&amp;gt;
      {data.map((product) =&amp;gt; (
        &amp;lt;li key={product.id}&amp;gt;{product.name}&amp;lt;/li&amp;gt;
      ))}
    &amp;lt;/ul&amp;gt;
  );
}_
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;**&lt;br&gt;
Why This Is Powerful&lt;/p&gt;

&lt;p&gt;Instead of manually controlling the fetch lifecycle, you simply describe:&lt;/p&gt;

&lt;p&gt;“This component depends on the products query.”&lt;/p&gt;

&lt;p&gt;TanStack Query handles everything else automatically.&lt;/p&gt;

&lt;p&gt;Network Request Deduplication&lt;/p&gt;

&lt;p&gt;Imagine three different components all requesting the same product list:&lt;/p&gt;

&lt;p&gt;Sidebar&lt;br&gt;
Dashboard&lt;br&gt;
Analytics widget&lt;/p&gt;

&lt;p&gt;Without TanStack Query:&lt;/p&gt;

&lt;p&gt;3 components&lt;br&gt;
3 fetch calls&lt;br&gt;
3 HTTP requests&lt;/p&gt;

&lt;p&gt;With TanStack Query:&lt;/p&gt;

&lt;p&gt;queryKey: ['products']&lt;/p&gt;

&lt;p&gt;TanStack Query recognizes the identical query key and:&lt;/p&gt;

&lt;p&gt;Sends only ONE network request&lt;br&gt;
Shares the response across all components&lt;br&gt;
Prevents network waterfalls&lt;/p&gt;

&lt;p&gt;This significantly improves performance.&lt;/p&gt;

&lt;p&gt;Understanding the Cache Architecture&lt;/p&gt;

&lt;p&gt;TanStack Query uses a strategy called:&lt;/p&gt;

&lt;p&gt;Stale-While-Revalidate&lt;/p&gt;

&lt;p&gt;The workflow looks like this:&lt;/p&gt;

&lt;p&gt;Serve cached data immediately&lt;br&gt;
Fetch fresh data in the background&lt;br&gt;
Update the UI automatically if data changed&lt;/p&gt;

&lt;p&gt;This creates extremely fast-feeling applications because users instantly see cached content instead of loading spinners.&lt;/p&gt;

&lt;p&gt;Client State vs Server State&lt;/p&gt;

&lt;p&gt;Before understanding TanStack Query, you need to separate two completely different categories of state.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Client State&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is synchronous state that exists entirely inside the browser.&lt;/p&gt;

&lt;p&gt;Examples:&lt;/p&gt;

&lt;p&gt;Modal visibility&lt;br&gt;
Theme toggles&lt;br&gt;
Sidebar state&lt;br&gt;
Form inputs&lt;/p&gt;

&lt;p&gt;This data is fully controlled by the frontend.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Server State&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is asynchronous state that lives remotely on a backend server or database.&lt;/p&gt;

&lt;p&gt;Examples:&lt;/p&gt;

&lt;p&gt;Product lists&lt;br&gt;
User profiles&lt;br&gt;
Notifications&lt;br&gt;
Dashboard analytics&lt;br&gt;
Orders from an API&lt;/p&gt;

&lt;p&gt;Server state has unique challenges:&lt;/p&gt;

&lt;p&gt;You do not own the data&lt;br&gt;
Multiple users can modify it&lt;br&gt;
It can become outdated instantly&lt;br&gt;
It requires async network requests&lt;br&gt;
It needs synchronization&lt;/p&gt;

&lt;p&gt;Using useState + useEffect forces server state to behave like client state.&lt;/p&gt;

&lt;p&gt;That creates problems like:&lt;/p&gt;

&lt;p&gt;Duplicate requests&lt;br&gt;
Boilerplate loading logic&lt;br&gt;
Manual error handling&lt;br&gt;
Race conditions&lt;br&gt;
No caching&lt;br&gt;
Difficult synchronization&lt;/p&gt;

&lt;p&gt;TanStack Query solves these automatically.&lt;/p&gt;

&lt;p&gt;Declarative Data Fetching with useQuery&lt;/p&gt;

&lt;p&gt;TanStack Query replaces imperative fetching with a declarative approach.&lt;/p&gt;

&lt;p&gt;The core building blocks are:&lt;/p&gt;

&lt;p&gt;Query Key&lt;br&gt;
Query Function&lt;/p&gt;

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

&lt;p&gt;_&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { useQuery } from '@tanstack/react-query';

function ProductList() {
  const { data, isLoading, isError, error } = useQuery({
    queryKey: ['products'],
    queryFn: async () =&amp;gt; {
      const res = await fetch('https://api.yourdomain.com/products');

      if (!res.ok) {
        throw new Error('Failed to fetch products');
      }

      return res.json();
    },
  });

  if (isLoading) {
    return &amp;lt;div&amp;gt;Loading products...&amp;lt;/div&amp;gt;;
  }

  if (isError) {
    return &amp;lt;div&amp;gt;Error: {error.message}&amp;lt;/div&amp;gt;;
  }

  return (
    &amp;lt;ul&amp;gt;
      {data.map((product) =&amp;gt; (
        &amp;lt;li key={product.id}&amp;gt;{product.name}&amp;lt;/li&amp;gt;
      ))}
    &amp;lt;/ul&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;_&lt;br&gt;
Why This Is Powerful&lt;/p&gt;

&lt;p&gt;Instead of manually controlling the fetch lifecycle, you simply describe:&lt;/p&gt;

&lt;p&gt;“This component depends on the products query.”&lt;/p&gt;

&lt;p&gt;TanStack Query handles everything else automatically.&lt;/p&gt;

&lt;p&gt;Network Request Deduplication&lt;/p&gt;

&lt;p&gt;Imagine three different components all requesting the same product list:&lt;/p&gt;

&lt;p&gt;Sidebar&lt;br&gt;
Dashboard&lt;br&gt;
Analytics widget&lt;/p&gt;

&lt;p&gt;Without TanStack Query:&lt;/p&gt;

&lt;p&gt;3 components&lt;br&gt;
3 fetch calls&lt;br&gt;
3 HTTP requests&lt;/p&gt;

&lt;p&gt;With TanStack Query:&lt;/p&gt;

&lt;p&gt;queryKey: ['products']&lt;/p&gt;

&lt;p&gt;TanStack Query recognizes the identical query key and:&lt;/p&gt;

&lt;p&gt;Sends only ONE network request&lt;br&gt;
Shares the response across all components&lt;br&gt;
Prevents network waterfalls&lt;/p&gt;

&lt;p&gt;This significantly improves performance.&lt;/p&gt;

&lt;p&gt;Understanding the Cache Architecture&lt;/p&gt;

&lt;p&gt;TanStack Query uses a strategy called:&lt;/p&gt;

&lt;p&gt;Stale-While-Revalidate&lt;/p&gt;

&lt;p&gt;The workflow looks like this:&lt;/p&gt;

&lt;p&gt;Serve cached data immediately&lt;br&gt;
Fetch fresh data in the background&lt;br&gt;
Update the UI automatically if data changed&lt;/p&gt;

&lt;p&gt;This creates extremely fast-feeling applications because users instantly see cached content instead of loading spinners.&lt;br&gt;
Understanding staleTime&lt;/p&gt;

&lt;p&gt;Default:&lt;/p&gt;

&lt;p&gt;staleTime: 0&lt;/p&gt;

&lt;p&gt;Meaning:&lt;/p&gt;

&lt;p&gt;Data becomes stale immediately after fetching.&lt;/p&gt;

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

&lt;p&gt;staleTime: 1000 * 60 * 5&lt;/p&gt;

&lt;p&gt;This means:&lt;/p&gt;

&lt;p&gt;Data is considered fresh for 5 minutes&lt;br&gt;
No refetch happens during that period&lt;br&gt;
Components reuse cached memory&lt;/p&gt;

&lt;p&gt;Best for:&lt;/p&gt;

&lt;p&gt;Country lists&lt;br&gt;
User settings&lt;br&gt;
Static configurations&lt;br&gt;
Understanding gcTime (Garbage Collection)&lt;/p&gt;

&lt;p&gt;Previously called cacheTime.&lt;/p&gt;

&lt;p&gt;Default:&lt;/p&gt;

&lt;p&gt;gcTime: 1000 * 60 * 5&lt;/p&gt;

&lt;p&gt;Meaning:&lt;/p&gt;

&lt;p&gt;When no component uses a query anymore:&lt;/p&gt;

&lt;p&gt;The query becomes inactive&lt;br&gt;
A garbage collection timer starts&lt;br&gt;
Cache is deleted after 5 minutes&lt;/p&gt;

&lt;p&gt;This prevents memory leaks.&lt;/p&gt;

&lt;p&gt;Automatic Background Refetching&lt;/p&gt;

&lt;p&gt;TanStack Query automatically keeps your application synchronized.&lt;/p&gt;

&lt;p&gt;It refetches data when:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;1. Components Mount Again&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;When a component using the query reappears.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;2. Window Focus Changes&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If the user switches tabs and comes back, the data revalidates automatically.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;3. Network Reconnects&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If the internet disconnects and reconnects, TanStack Query immediately synchronizes stale data.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;4. Polling Intervals&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;refetchInterval: 10000&lt;/p&gt;

&lt;p&gt;This refetches data every 10 seconds.&lt;/p&gt;

&lt;p&gt;Useful for:&lt;/p&gt;

&lt;p&gt;Real-time dashboards&lt;br&gt;
Notifications&lt;br&gt;
Live analytics&lt;br&gt;
Mutating Data with useMutation&lt;/p&gt;

&lt;p&gt;Fetching data is only half the story.&lt;/p&gt;

&lt;p&gt;What happens after:&lt;/p&gt;

&lt;p&gt;POST requests&lt;br&gt;
PUT updates&lt;br&gt;
DELETE operations&lt;/p&gt;

&lt;p&gt;The browser cache immediately becomes outdated.&lt;/p&gt;

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

&lt;p&gt;_&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import {
  useMutation,
  useQueryClient,
} from '@tanstack/react-query';

const queryClient = useQueryClient();

const addProductMutation = useMutation({
  mutationFn: async (newProduct) =&amp;gt; {
    const res = await fetch(
      'https://api.yourdomain.com/products',
      {
        method: 'POST',
        body: JSON.stringify(newProduct),
      }
    );

    return res.json();
  },

  onSuccess: () =&amp;gt; {
    queryClient.invalidateQueries({
      queryKey: ['products'],
    });
  },
});
Cache Invalidation

Instead of manually updating arrays in state:

setProducts(...)

You invalidate the query:

queryClient.invalidateQueries({
  queryKey: ['products'],
});

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;TanStack Query then:&lt;/p&gt;

&lt;p&gt;Marks the cache as stale&lt;br&gt;
Automatically refetches fresh data&lt;br&gt;
Synchronizes the UI with the database&lt;/p&gt;

&lt;p&gt;This removes fragile state management logic entirely.&lt;/p&gt;

&lt;p&gt;Using TanStack Query DevTools&lt;/p&gt;

&lt;p&gt;Debugging async cache systems with console.log becomes painful quickly.&lt;/p&gt;

&lt;p&gt;Install the DevTools:&lt;/p&gt;

&lt;p&gt;npm install @tanstack/react-query-devtools&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import {
  QueryClient,
  QueryClientProvider,
} from '@tanstack/react-query';

import {
  ReactQueryDevtools,
} from '@tanstack/react-query-devtools';

const queryClient = new QueryClient();

export default function App() {
  return (
    &amp;lt;QueryClientProvider client={queryClient}&amp;gt;
      &amp;lt;MainApplication /&amp;gt;

      &amp;lt;ReactQueryDevtools
        initialIsOpen={false}
      /&amp;gt;
    &amp;lt;/QueryClientProvider&amp;gt;
  );
}_
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Query Lifecycle States&lt;/p&gt;

&lt;p&gt;Inside the DevTools panel, queries appear in different lifecycle states:&lt;/p&gt;

&lt;p&gt;State   Meaning&lt;br&gt;
Fresh   Data is still trusted&lt;br&gt;
Stale   Data exists but needs revalidation&lt;br&gt;
Fetching    Active network request&lt;br&gt;
Inactive    No component currently uses the query&lt;/p&gt;

&lt;p&gt;This gives you full visibility into your async cache architecture.&lt;/p&gt;

</description>
      <category>react</category>
      <category>typescript</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>Mastering Dependency Injection in .NET: A Software Engineer's Secret Weapon</title>
      <dc:creator>victor mwangi</dc:creator>
      <pubDate>Wed, 27 May 2026 15:39:10 +0000</pubDate>
      <link>https://dev.to/victor_mwangi_d224324203c/mastering-dependency-injection-in-net-a-software-engineers-secret-weapon-34b5</link>
      <guid>https://dev.to/victor_mwangi_d224324203c/mastering-dependency-injection-in-net-a-software-engineers-secret-weapon-34b5</guid>
      <description>&lt;p&gt;Imagine you are building a modern car. Instead of welding the engine directly to the chassis, you bolt it in. Why? Because if the engine breaks—or if you want to upgrade from a gas engine to an electric one—you don’t want to tear the whole car apart. You just unbolt the old one and slot the new one in.&lt;/p&gt;

&lt;p&gt;In software engineering, Dependency Injection (DI) is that bolt.&lt;/p&gt;

&lt;p&gt;If you are building applications in .NET, DI isn’t just a "nice-to-have" design pattern; it’s a core part of the framework. Let’s break down what DI is, why you need it, and how to master it in .NET.&lt;/p&gt;

&lt;p&gt;**&lt;/p&gt;

&lt;h2&gt;
  
  
  What on Earth is Dependency Injection?
&lt;/h2&gt;

&lt;p&gt;**&lt;br&gt;
Before we talk about injection, let’s talk about dependencies. If Class A uses Class B to get its job done, Class A depends on Class B. Class B is a dependency.&lt;/p&gt;

&lt;p&gt;Without DI, Class A creates Class B itself. Look at this tightly coupled mess:&lt;/p&gt;

&lt;p&gt;_C#&lt;br&gt;
public class Car&lt;br&gt;
{&lt;br&gt;
    private Engine _engine;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public Car()
{
    // The car is trapped! It can only ever use this specific V8Engine.
    this._engine = new V8Engine(); 
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;}_&lt;br&gt;
With Dependency Injection, we invert that control (hence Inversion of Control, or IoC). Instead of the Car creating the Engine, we "inject" the engine through the constructor:&lt;/p&gt;

&lt;p&gt;_C#&lt;br&gt;
public class Car&lt;br&gt;
{&lt;br&gt;
    private readonly IEngine _engine;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// The car doesn't care what kind of engine it gets, as long as it implements IEngine
public Car(IEngine engine)
{
    _engine = engine;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;}_&lt;br&gt;
**&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Should You Care? (The Benefits)
&lt;/h2&gt;

&lt;p&gt;**Maintainability: If you need to change how Engine works, you don't risk breaking Car.&lt;/p&gt;

&lt;p&gt;Testability: This is the big one. When writing unit tests for Car, you don't want to spin up a real database or hit a real API. With DI, you can inject a MockEngine and test the Car in isolation.&lt;/p&gt;

&lt;p&gt;Flexibility: Want to swap your SQL database for MongoDB? Just change the registration in one central place, and the rest of your app keeps running seamlessly.&lt;/p&gt;

&lt;p&gt;Service Lifetimes in .NET: The Big Three&lt;br&gt;
The built-in .NET IoC container manages the creation and disposal of your dependencies. When you register a service, you have to tell .NET how long that service should live.&lt;/p&gt;

&lt;p&gt;Choosing the wrong lifetime is the #1 cause of bugs in .NET DI. Here is the breakdown:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Transient (AddTransient)&lt;/strong&gt;&lt;br&gt;
How it works: A brand-new instance is created every single time it is requested.&lt;/p&gt;

&lt;p&gt;Best used for: Lightweight, stateless services (like a quick formatting helper or a validation service).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Scoped (AddScoped)&lt;/strong&gt;&lt;br&gt;
How it works: An instance is created once per HTTP request (in web apps). All components handling that specific web request will share that same instance.&lt;/p&gt;

&lt;p&gt;Best used for: Services that hold state for a single request, like your Entity Framework Core DbContext.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Singleton (AddSingleton)&lt;/strong&gt;&lt;br&gt;
How it works: An instance is created the first time it’s requested and lives forever until the application shuts down. Every subsequent request uses that exact same instance.&lt;/p&gt;

&lt;p&gt;Best used for: Caching services, configuration settings, or anything that is expensive to create and needs to be shared globally.&lt;/p&gt;

&lt;p&gt;⚠️ Danger Zone Warning: Never inject a Scoped service into a Singleton service. Because the Singleton lives forever, the Scoped service will get trapped inside it forever, effectively turning it into a Singleton. This is called a Captive Dependency, and it causes massive database connection bugs!&lt;/p&gt;

&lt;p&gt;Wire It Up: A Real-World .NET Example&lt;br&gt;
Let's look at how to actually implement this in a modern .NET Web API or Minimal API (Program.cs).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Define the Contract and Implementation&lt;/strong&gt;&lt;br&gt;
_C#&lt;br&gt;
public interface IEmailService&lt;br&gt;
{&lt;br&gt;
    void SendEmail(string to, string subject);&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;public class SendGridEmailService : IEmailService&lt;br&gt;
{&lt;br&gt;
    public void SendEmail(string to, string subject)&lt;br&gt;
    {&lt;br&gt;
        Console.WriteLine($"Email sent to {to} via SendGrid!");&lt;br&gt;
    }&lt;br&gt;
}_&lt;br&gt;
&lt;strong&gt;Step 2: Register it in Program.cs&lt;/strong&gt;&lt;br&gt;
Open your Program.cs file. This is where the magic happens. We register our IEmailService with the service collection.&lt;/p&gt;

&lt;p&gt;_C#&lt;br&gt;
var builder = WebApplication.CreateBuilder(args);&lt;/p&gt;

&lt;p&gt;// Register your dependency here!&lt;br&gt;
builder.Services.AddScoped();&lt;/p&gt;

&lt;p&gt;var app = builder.Build();_&lt;br&gt;
&lt;strong&gt;Step 3: Inject and Use It&lt;/strong&gt;&lt;br&gt;
Now, .NET will automatically look at the constructor of your Controllers or Minimal API endpoints, see that it requires an IEmailService, and pass it in.&lt;/p&gt;

&lt;p&gt;_C#&lt;br&gt;
app.MapPost("/register", (string email, IEmailService emailService) =&amp;gt;&lt;br&gt;
{&lt;br&gt;
    // .NET automatically provided 'emailService' for us!&lt;br&gt;
    emailService.SendEmail(email, "Welcome to our App!");&lt;br&gt;
    return Results.Ok("User registered successfully");&lt;br&gt;
});&lt;/p&gt;

&lt;p&gt;app.Run();_&lt;br&gt;
Wrapping Up&lt;br&gt;
Dependency Injection isn't just an academic concept; it's the glue that keeps professional .NET applications clean, testable, and scalable. By shifting the responsibility of object creation from your classes to the .NET framework, you write code that is ready for change.&lt;/p&gt;

&lt;p&gt;Next time you write a class, don't reach for the new keyword right away. Ask yourself: "Should this be injected instead?"&lt;/p&gt;

&lt;p&gt;What are your thoughts on .NET's built-in DI container? Have you ever run into a captive dependency nightmare? Let's chat in the comments below!&lt;/p&gt;

&lt;p&gt;Happy coding! 🚀&lt;/p&gt;

</description>
      <category>programming</category>
      <category>dotnet</category>
      <category>software</category>
      <category>softwaredevelopment</category>
    </item>
  </channel>
</rss>
