<?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>Forem: nikosst</title>
    <description>The latest articles on Forem by nikosst (@__b63657).</description>
    <link>https://forem.com/__b63657</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%2F2710936%2F20075e34-3f33-44d9-a08b-c1703ff98457.jpg</url>
      <title>Forem: nikosst</title>
      <link>https://forem.com/__b63657</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/__b63657"/>
    <language>en</language>
    <item>
      <title>Strategy Pattern Θεωρία, Χρήση και Πρακτική Εφαρμογή</title>
      <dc:creator>nikosst</dc:creator>
      <pubDate>Thu, 30 Apr 2026 13:38:11 +0000</pubDate>
      <link>https://forem.com/__b63657/strategy-pattern-theoria-khrese-kai-praktike-epharmoge-34m</link>
      <guid>https://forem.com/__b63657/strategy-pattern-theoria-khrese-kai-praktike-epharmoge-34m</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdaak7qoeprxvd86enngz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdaak7qoeprxvd86enngz.png" alt=" " width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Τι είναι τα Design Patterns και γιατί υπάρχουν&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Τα design patterns δεν είναι “μαγικές συνταγές”. Είναι δοκιμασμένες λύσεις σε επαναλαμβανόμενα προβλήματα σχεδιασμού λογισμικού. Προέκυψαν γιατί οι προγραμματιστές αντιμετωπίζουν ξανά και ξανά τα ίδια αρχιτεκτονικά διλήμματα: ευελιξία, επεκτασιμότητα, καθαρός διαχωρισμός ευθυνών.&lt;/p&gt;

&lt;p&gt;Το βασικό τους όφελος:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Μειώνουν την πολυπλοκότητα&lt;/li&gt;
&lt;li&gt;Αυξάνουν τη συντηρησιμότητα&lt;/li&gt;
&lt;li&gt;Δημιουργούν κοινή “γλώσσα” μεταξύ developers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Το Strategy Pattern ανήκει στα behavioral patterns και ασχολείται με το πώς αλλάζει η συμπεριφορά ενός αντικειμένου δυναμικά.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Τι είναι το Strategy Pattern&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Το Strategy Pattern επιτρέπει να ορίζεις μια οικογένεια αλγορίθμων, να τους ενθυλακώνεις (encapsulate) και να τους κάνεις εναλλάξιμους.&lt;/p&gt;

&lt;p&gt;Με απλά λόγια:&lt;br&gt;
Αν έχεις πολλές διαφορετικές “στρατηγικές” για να κάνεις το ίδιο πράγμα, δεν γράφεις &lt;code&gt;if/else&lt;/code&gt; παντού αλλά τις απομονώνεις.&lt;/p&gt;



&lt;p&gt;&lt;strong&gt;Το πρόβλημα που λύνει&lt;/strong&gt;&lt;/p&gt;

&lt;p&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;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;"credit"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;PayWithCredit&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;"paypal"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;PayWithPaypal&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;"crypto"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;PayWithCrypto&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Προβλήματα:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Παραβίαση του Open/Closed Principle&lt;/li&gt;
&lt;li&gt;Δύσκολη συντήρηση&lt;/li&gt;
&lt;li&gt;Δύσκολο testing&lt;/li&gt;
&lt;li&gt;Κακή επεκτασιμότητα&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Η ιδέα του Strategy Pattern&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Αντί να έχεις conditional logic, κάνεις:&lt;/p&gt;

&lt;p&gt;“Δίνω” στο αντικείμενο μια στρατηγική και αυτό τη χρησιμοποιεί.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Δομή&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Το pattern έχει 3 βασικά μέρη:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Strategy Interface&lt;/li&gt;
&lt;li&gt;Concrete Strategies&lt;/li&gt;
&lt;li&gt;Context&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Παράδειγμα σε C#&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Strategy Interface
&lt;/li&gt;
&lt;/ol&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;ol&gt;
&lt;li&gt;Concrete Strategies
&lt;/li&gt;
&lt;/ol&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="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;$"Paid &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="s"&gt; with Credit Card"&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;class&lt;/span&gt; &lt;span class="nc"&gt;PaypalPayment&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="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;$"Paid &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="s"&gt; with 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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Context
&lt;/li&gt;
&lt;/ol&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;PaymentContext&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;IPaymentStrategy&lt;/span&gt; &lt;span class="n"&gt;_strategy&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;PaymentContext&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;strategy&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_strategy&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;strategy&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;SetStrategy&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;strategy&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_strategy&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;strategy&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;ExecutePayment&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;_strategy&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;ol&gt;
&lt;li&gt;Χρήση
&lt;/li&gt;
&lt;/ol&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;context&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;PaymentContext&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;CreditCardPayment&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ExecutePayment&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="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetStrategy&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;PaypalPayment&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ExecutePayment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;200&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;&lt;strong&gt;Γιατί να το χρησιμοποιήσεις&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Καθαρός Κώδικας&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Δεν έχεις &lt;code&gt;if/else&lt;/code&gt; παντού.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Open/Closed Principle&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Προσθέτεις νέα στρατηγική χωρίς να αλλάξεις υπάρχοντα κώδικα.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Testability&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Κάθε στρατηγική μπορεί να γίνει unit test ανεξάρτητα.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Επαναχρησιμοποίηση&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Οι στρατηγικές είναι plug-and-play.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Runtime ευελιξία&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Αλλάζεις συμπεριφορά δυναμικά.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Πότε να το χρησιμοποιήσεις&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;✔ Όταν έχεις πολλές παραλλαγές μιας λειτουργίας&lt;br&gt;
✔ Όταν βλέπεις πολλά if/else ή switch&lt;br&gt;
✔ Όταν θέλεις να αλλάζεις behavior στο runtime&lt;br&gt;
✔ Όταν θέλεις καθαρή αρχιτεκτονική&lt;/p&gt;



&lt;p&gt;&lt;strong&gt;Πότε ΔΕΝ χρειάζεται&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;❌ Όταν έχεις 1-2 απλές επιλογές&lt;br&gt;
❌ Όταν προσθέτει άσκοπη πολυπλοκότητα&lt;br&gt;
❌ Όταν δεν υπάρχει πιθανότητα επέκτασης&lt;/p&gt;



&lt;p&gt;&lt;strong&gt;Real-world παραδείγματα&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Payment systems (PayPal, Stripe, Crypto)&lt;/li&gt;
&lt;li&gt;Compression algorithms (zip, rar)&lt;/li&gt;
&lt;li&gt;Sorting strategies&lt;/li&gt;
&lt;li&gt;Authentication methods&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;&lt;strong&gt;Strategy Pattern σε συνδυασμό με Dependency Injection&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Γιατί να το συνδυάσεις με DI&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Το Strategy Pattern από μόνο του λύνει το πρόβλημα των if/else.&lt;br&gt;
Αλλά αν κάνεις αυτό:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;var context = new PaymentContext(new CreditCardPayment());&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;έχεις ακόμα ένα πρόβλημα:&lt;/p&gt;

&lt;p&gt;Ο κώδικας σου γνωρίζει τις υλοποιήσεις (concrete classes)&lt;/p&gt;

&lt;p&gt;Αυτό σημαίνει:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tight coupling&lt;/li&gt;
&lt;li&gt;Δύσκολο testing (χωρίς mocking εύκολα)&lt;/li&gt;
&lt;li&gt;Παραβίαση του Dependency Inversion Principle&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;&lt;strong&gt;Τι σου δίνει το Dependency Injection&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Με DI:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Δεν δημιουργείς αντικείμενα στα δίνει το framework&lt;/li&gt;
&lt;li&gt;Δουλεύεις με interfaces&lt;/li&gt;
&lt;li&gt;Μπορείς να αλλάξεις behavior χωρίς να πειράξεις business logic&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Με απλά λόγια: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Το Strategy Pattern βρίσκει πώς να αλλάξει behavior&lt;/li&gt;
&lt;li&gt;Το DI αποφασίζει ποιο behavior θα χρησιμοποιηθεί&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;&lt;strong&gt;Παράδειγμα σε ASP.NET Core&lt;/strong&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="kt"&gt;var&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;WebApplication&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Register strategies&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;IPaymentStrategy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CreditCardPayment&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;IPaymentStrategy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;PaypalPayment&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Register context &amp;amp; service&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;PaymentContext&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;PaymentService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Service που χρησιμοποιεί το Strategy Pattern&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;PaymentContext&lt;/span&gt; &lt;span class="n"&gt;_context&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;PaymentContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_context&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&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;Pay&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;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;_context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ExecutePayment&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="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;Παράδειγμα χρήσης (π.χ. σε Controller ή minimal API)&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;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapPost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/pay"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PaymentService&lt;/span&gt; &lt;span class="n"&gt;service&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;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;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;service&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;type&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="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Payment completed"&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;Request παραδείγματα&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;POST /pay?type=credit&amp;amp;amount=100&lt;/code&gt;&lt;br&gt;
&lt;code&gt;POST /pay?type=paypal&amp;amp;amount=200&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Το DI δίνει όλες τις στρατηγικές&lt;br&gt;
Το PaymentContext επιλέγει σωστή με βάση το type&lt;br&gt;
Το PaymentService δεν ξέρει τίποτα για implementations&lt;br&gt;
Μηδέν if/else, full extensibility&lt;/p&gt;



&lt;p&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;CryptoPayment&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="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Key&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"crypto"&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="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;$"Paid &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="s"&gt; with Crypto"&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;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;IPaymentStrategy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CryptoPayment&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;Να θυμάσαι..&lt;/p&gt;

&lt;p&gt;Το Strategy Pattern είναι ένα από τα πιο χρήσιμα patterns γιατί:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Διαχωρίζει το τι κάνεις από το πώς το κάνεις&lt;/li&gt;
&lt;li&gt;Σου δίνει ελευθερία να αλλάζεις behavior χωρίς να “σπας” τον κώδικα&lt;/li&gt;
&lt;li&gt;Σε φέρνει πιο κοντά σε καθαρή αρχιτεκτονική (clean architecture)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Το Strategy Pattern μόνο του είναι καλό.&lt;/p&gt;

&lt;p&gt;Με Dependency Injection γίνεται enterprise-grade εργαλείο.&lt;/p&gt;

&lt;p&gt;Σου δίνει:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ευελιξία&lt;/li&gt;
&lt;li&gt;επεκτασιμότητα&lt;/li&gt;
&lt;li&gt;καθαρότητα&lt;/li&gt;
&lt;li&gt;testability&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;a href="mailto:nikosstit@gmail.com"&gt;nikosstit@gmail.com&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Architecture Layers, Security Boundaries και Best Practices</title>
      <dc:creator>nikosst</dc:creator>
      <pubDate>Wed, 29 Apr 2026 14:22:03 +0000</pubDate>
      <link>https://forem.com/__b63657/request-size-limits-se-apis-arkhitektonike-layers-kai-best-practices-5hbn</link>
      <guid>https://forem.com/__b63657/request-size-limits-se-apis-arkhitektonike-layers-kai-best-practices-5hbn</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvlhusoc0tawqq5p3gp08.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvlhusoc0tawqq5p3gp08.png" alt=" " width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Κάθε API που εκτίθεται στο διαδίκτυο δέχεται δεδομένα από πηγές που δεν ελέγχει πλήρως. Για παράδειγμα, ένα απλό JSON endpoint που περιμένει λίγα KB μπορεί να δεχτεί ένα payload δεκάδων MB. Αν αυτό το request φτάσει μέχρι το parsing ή το model binding, το σύστημα έχει ήδη πληρώσει κόστος σε μνήμη και CPU. Αυτό σημαίνει ότι το σύστημα πρέπει να είναι προετοιμασμένο όχι μόνο για “σωστά” requests, αλλά και για:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;λανθασμένα payloads&lt;/li&gt;
&lt;li&gt;υπερβολικά μεγάλα δεδομένα&lt;/li&gt;
&lt;li&gt;κακόβουλη χρήση&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ένα από τα βασικά εργαλεία για την αντιμετώπιση αυτών των περιπτώσεων είναι τα &lt;code&gt;request size limits&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Δεν πρόκειται για απλή ρύθμιση. Είναι μηχανισμός προστασίας που πρέπει να τοποθετηθεί σωστά στην αρχιτεκτονική.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Η ανάγκη για όρια&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Αν δεν υπάρχει κανένα όριο στο μέγεθος ενός request, το σύστημα εκτίθεται σε πραγματικά προβλήματα.&lt;/p&gt;

&lt;p&gt;Πρώτον, υπάρχει ο κίνδυνος εξάντλησης πόρων. Ένα μεγάλο request μπορεί να καταναλώσει μνήμη και CPU, ειδικά όταν πρόκειται για JSON που πρέπει να γίνει parsing ή για αρχεία που πρέπει να διαβαστούν.&lt;/p&gt;

&lt;p&gt;Δεύτερον, υπάρχει ο κίνδυνος επιθέσεων. Ένα API χωρίς limits μπορεί να δεχτεί συνεχόμενα μεγάλα requests και να οδηγηθεί σε κατάσταση όπου δεν μπορεί να εξυπηρετήσει νόμιμους χρήστες.&lt;/p&gt;

&lt;p&gt;Τρίτον, ακόμα και χωρίς κακή πρόθεση, ένας client μπορεί να στείλει λάθος δεδομένα λόγω bug. Το σύστημα πρέπει να προστατεύεται και από αυτό.&lt;/p&gt;

&lt;p&gt;Τέλος, υπάρχει και η επιχειρησιακή διάσταση. Δεν έχουν όλα τα endpoints τις ίδιες ανάγκες. Ένα endpoint για avatar δεν πρέπει να δέχεται το ίδιο μέγεθος δεδομένων με ένα endpoint για document upload.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Τι είναι το request size limit&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Είναι ένα όριο στο μέγεθος του body ενός HTTP request. Αν το request ξεπεράσει αυτό το όριο, απορρίπτεται πριν επεξεργαστεί περαιτέρω.&lt;/p&gt;

&lt;p&gt;Η απόρριψη γίνεται με HTTP status:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;413 Payload Too Large&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Το σημαντικό δεν είναι μόνο ότι απορρίπτεται, αλλά πού απορρίπτεται.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Η έννοια των layers&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Ένα request δεν φτάνει απευθείας στον controller. Περνά από πολλαπλά επίπεδα.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Client
  ↓
Reverse Proxy / Gateway (IIS, Azure, nginx)
  ↓
Kestrel (web server)
  ↓
ASP.NET pipeline (Middleware)
  ↓
API endpoint
  ↓
Controller
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Κάθε επίπεδο έχει τη δυνατότητα να εφαρμόσει limit.&lt;/p&gt;

&lt;p&gt;Η σωστή αρχιτεκτονική δεν βασίζεται σε ένα σημείο, αλλά σε διαδοχικά επίπεδα ελέγχου.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Τα επίπεδα και ο ρόλος τους&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Reverse Proxy / Gateway&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Αυτό είναι το εξωτερικό όριο του συστήματος.&lt;/p&gt;

&lt;p&gt;Σε αυτό το σημείο το request μπορεί να απορριφθεί πριν φτάσει καν στην εφαρμογή. Αυτό σημαίνει ότι δεν καταναλώνονται application resources.&lt;/p&gt;

&lt;p&gt;Είναι το πιο αποδοτικό σημείο για να απορρίπτονται υπερβολικά μεγάλα requests.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Kestrel (server level)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Ο Kestrel είναι ο web server που τρέχει την ASP.NET Core εφαρμογή.&lt;/p&gt;

&lt;p&gt;Σε αυτό το επίπεδο ορίζεται ένα γενικό όριο για όλη την εφαρμογή, λειτουργώντας ως μηχανισμός προστασίας:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;αποτρέπει μεγάλα requests πριν μπουν στο pipeline&lt;/li&gt;
&lt;li&gt;καλύπτει όλα τα endpoints&lt;/li&gt;
&lt;li&gt;λειτουργεί ως safety net&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Το όριο αυτό εκφράζει το τι μπορεί να αντέξει τεχνικά το σύστημα, όχι απαραίτητα το τι επιτρέπεται επιχειρησιακά.&lt;/p&gt;

&lt;p&gt;Ωστόσο, σε cloud περιβάλλοντα όπως το Azure, οι περιορισμοί του Kestrel δεν αποτελούν πάντα το τελικό σημείο ελέγχου. Ένα request μπορεί να έχει ήδη περάσει από upstream managed layers (π.χ. gateways, front ends) που εφαρμόζουν δικούς τους περιορισμούς, ενώ σε ορισμένα hosting μοντέλα (όπως Azure Functions in-process) το HTTP pipeline δεν ελέγχεται άμεσα από τον προγραμματιστή.&lt;/p&gt;

&lt;p&gt;Συνεπώς, τα application-level limits θα πρέπει να αντιμετωπίζονται ως ένα επιπλέον επίπεδο προστασίας και όχι ως η μοναδική γραμμή άμυνας.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;ASP.NET / API επίπεδο&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Σε αυτό το επίπεδο βρίσκονται τα endpoints.&lt;/p&gt;

&lt;p&gt;Εδώ εφαρμόζονται limits μέσω μηχανισμών όπως:&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="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;RequestSizeLimit&lt;/span&gt;&lt;span class="p"&gt;(...)]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Τα limits αυτά εκφράζουν επιχειρησιακούς κανόνες χρήσης και όχι τεχνικούς περιορισμούς του συστήματος. Δηλαδή καθορίζουν τι επιτρέπεται ανά endpoint, ανεξάρτητα από το τι μπορεί να αντέξει συνολικά η εφαρμογή.&lt;/p&gt;

&lt;p&gt;Παραδείγματα:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;avatar upload: 3 MB&lt;/li&gt;
&lt;li&gt;document upload: 25 MB&lt;/li&gt;
&lt;li&gt;JSON request: 512 KB&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Τα endpoint-level limits λειτουργούν συμπληρωματικά με τα υπόλοιπα επίπεδα (π.χ. Kestrel, gateway), επιτρέποντας πιο granular έλεγχο ανά use case.&lt;/p&gt;

&lt;p&gt;Ωστόσο, όπως και με τα application-level limits, οι περιορισμοί αυτοί δεν εγγυώνται ότι το request θα φτάσει στο endpoint, καθώς μπορεί να απορριφθεί νωρίτερα από upstream layers.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh8fzf8fkdhm62qmfjjhl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh8fzf8fkdhm62qmfjjhl.png" alt=" " width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Hosting Model &amp;amp; Azure Plan: Ποιος ελέγχει πραγματικά το pipeline;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Σε Azure περιβάλλοντα, ειδικά όταν χρησιμοποιούνται Azure Functions, το ποιος ελέγχει το HTTP request pipeline δεν είναι πάντα προφανές και εξαρτάται από δύο βασικούς παράγοντες: το hosting model και το deployment plan.&lt;/p&gt;

&lt;p&gt;Στο &lt;strong&gt;in-process model&lt;/strong&gt;, το application εκτελείται μέσα στο ίδιο process με το Functions runtime. Αυτό σημαίνει ότι το HTTP pipeline διαχειρίζεται πλήρως από την πλατφόρμα και δεν υπάρχει άμεσος έλεγχος στον underlying server (π.χ. Kestrel). Οι ρυθμίσεις που θα εφαρμόζονταν σε ένα τυπικό ASP.NET Core application δεν έχουν απαραίτητα το ίδιο αποτέλεσμα.&lt;/p&gt;

&lt;p&gt;Αντίθετα, στο &lt;strong&gt;isolated worker model&lt;/strong&gt;, το application τρέχει σε ξεχωριστό process και μπορεί να αξιοποιήσει ASP.NET Core integration. Σε αυτή την περίπτωση, υπάρχει δυνατότητα ελέγχου του request pipeline (middleware, filters, limits), προσεγγίζοντας τη συμπεριφορά ενός κλασικού Web API.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Βασικές διαφορές στην πράξη (isolated vs in-process)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Η ουσιαστική διαφορά μεταξύ των δύο μοντέλων δεν είναι μόνο το process isolation, αλλά το επίπεδο ελέγχου και παραμετροποίησης του HTTP pipeline. Στο in-process model, το request περνά αποκλειστικά μέσα από το Functions runtime, το οποίο ορίζει τη ροή εκτέλεσης και επιβάλλει περιορισμούς πριν το application code αποκτήσει έλεγχο. Αυτό σημαίνει ότι δεν υπάρχει πραγματικό ASP.NET Core pipeline, ούτε δυνατότητα χρήσης middleware, και κατά συνέπεια οι έννοιες όπως Kestrel-level limits ή request filtering δεν εφαρμόζονται με τον ίδιο τρόπο. Αντίθετα, στο isolated model, το application μπορεί να “χτίσει” το δικό του pipeline μέσω ASP.NET Core integration, επιτρέποντας την εισαγωγή middleware, custom handling και πιο granular έλεγχο στη ροή των requests. Παρ’ όλα αυτά, ακόμη και σε αυτή την περίπτωση, το pipeline δεν είναι πλήρως αυτόνομο, καθώς προηγούνται πάντα managed layers της πλατφόρμας που ενδέχεται να επιβάλλουν δικούς τους περιορισμούς.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Ωστόσο, το Azure plan επηρεάζει σημαντικά το επίπεδο ελέγχου:&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Στο Consumption plan, το request περνάει από managed infrastructure layers της πλατφόρμας πριν φτάσει στο application, επιβάλλοντας περιορισμούς ανεξάρτητα από το application configuration.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Στα Premium ή App Service plans, το περιβάλλον είναι πιο κοντά σε παραδοσιακό hosting, επιτρέποντας μεγαλύτερο έλεγχο στο HTTP pipeline και καλύτερη αξιοποίηση του ASP.NET Core stack.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Σε κάθε περίπτωση, είναι σημαντικό να σημειωθεί ότι τα upstream layers (Azure infrastructure, reverse proxies, gateways) ενδέχεται να εφαρμόζουν αυστηρότερους περιορισμούς από αυτούς που ορίζονται στο application. Αυτό σημαίνει ότι ένα request μπορεί να απορριφθεί πριν φτάσει ποτέ στο ASP.NET Core pipeline, ανεξάρτητα από τις ρυθμίσεις που έχουν οριστεί σε επίπεδο middleware ή API.&lt;/p&gt;

&lt;p&gt;Κατά συνέπεια, πριν βασιστούμε σε application-level περιορισμούς, είναι απαραίτητο να κατανοήσουμε αν το περιβάλλον εκτέλεσης μας επιτρέπει πραγματικά να τους εφαρμόσουμε και ποιο layer έχει τον τελικό έλεγχο στην αποδοχή ή απόρριψη ενός request.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Σημείωση για συμβατικά Web Applications (App Service)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Σε αντίθεση με τα serverless σενάρια (π.χ. Azure Functions), σε ένα τυπικό ASP.NET Core Web Application που φιλοξενείται στο Azure App Service, η εφαρμογή εκτελείται πάντα πάνω στον Kestrel. Ακόμη και όταν υπάρχει IIS (σε Windows environments) ή άλλο reverse proxy μπροστά, αυτά λειτουργούν ως ενδιάμεσο layer που προωθεί τα requests, ενώ ο Kestrel παραμένει ο πραγματικός web server που εκτελεί το ASP.NET Core pipeline. Συνεπώς, server-level ρυθμίσεις (όπως request size limits) εξακολουθούν να υφίστανται στο application runtime, ακόμη και αν δεν είναι πάντα άμεσα ορατές ή πλήρως ελέγξιμες από τον προγραμματιστή.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Ρόλος του Azure Front Door στο request pipeline&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Σε cloud αρχιτεκτονικές, ένα επιπλέον επίπεδο που συχνά προηγείται του application είναι το Azure Front Door. Το Front Door λειτουργεί ως global entry point και reverse proxy, δρομολογώντας τα requests προς τα backend services. Σε αυτό το επίπεδο μπορούν να εφαρμοστούν περιορισμοί όπως request size limits, rate limiting, WAF κανόνες και άλλοι μηχανισμοί προστασίας. Καθώς βρίσκεται πριν από το application και τον web server, έχει τη δυνατότητα να απορρίπτει requests πολύ νωρίς, μειώνοντας σημαντικά το load που φτάνει στο σύστημα. Ωστόσο, όπως και με κάθε upstream layer, οι περιορισμοί του Front Door υπερισχύουν των downstream ρυθμίσεων, καθώς ένα request που απορρίπτεται σε αυτό το επίπεδο δεν φτάνει ποτέ στο ASP.NET Core pipeline.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Πώς συνεργάζονται τα επίπεδα&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;            [ Internet ]
                  │
            [ CDN / WAF ]
      "Early rejection"
                  │
         [ Reverse Proxy ]
    "HTTP body/header limits"
                  │
           [ Kestrel/IIS ]
     "Transport/server limits"
                  │
            [ Middleware ]
  "Request inspection &amp;amp; logging"
                  │
          [ MVC / Controllers ]
     "Business validation"
                  │
              [ Services ]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Το request περνά διαδοχικά από τα επίπεδα και σταματά στο πρώτο που θα παραβιάσει.&lt;/p&gt;

&lt;p&gt;Αν ένα request είναι μεγαλύτερο από το όριο του proxy, δεν φτάνει ποτέ στον Kestrel.&lt;/p&gt;

&lt;p&gt;Αν περάσει τον proxy αλλά είναι μεγαλύτερο από το Kestrel limit, απορρίπτεται εκεί.&lt;/p&gt;

&lt;p&gt;Αν περάσει και τα δύο αλλά παραβιάζει το endpoint limit, απορρίπτεται στο API επίπεδο.&lt;/p&gt;

&lt;p&gt;Αυτό δημιουργεί μια αλυσίδα προστασίας.&lt;/p&gt;

&lt;p&gt;Στην πράξη, το πιο περιοριστικό upstream layer είναι αυτό που καθορίζει το τελικό αποδεκτό μέγεθος request, ανεξάρτητα από τις ρυθμίσεις των downstream επιπέδων.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Γιατί δεν αρκεί ένα μόνο επίπεδο&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Αν βάλουμε limit μόνο στο API:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;το request έχει ήδη φτάσει στον server&lt;/li&gt;
&lt;li&gt;έχουν δεσμευτεί πόροι&lt;/li&gt;
&lt;li&gt;η προστασία έρχεται αργά&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Αν βάλουμε μόνο στο Kestrel:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;προστατεύεται το σύστημα&lt;/li&gt;
&lt;li&gt;αλλά δεν εκφράζονται business rules&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Αν βάλουμε μόνο στο proxy:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;προστατεύεται η είσοδος&lt;/li&gt;
&lt;li&gt;αλλά δεν υπάρχει έλεγχος ανά endpoint&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Καμία μεμονωμένη λύση δεν καλύπτει πλήρως το πρόβλημα.&lt;/p&gt;

&lt;p&gt;Η σωστή προσέγγιση είναι ο συνδυασμός επιπέδων, όπου κάθε layer αναλαμβάνει διαφορετικό ρόλο: προστασία υποδομής, έλεγχος πόρων και εφαρμογή επιχειρησιακών κανόνων.&lt;/p&gt;

&lt;p&gt;Όσο πιο νωρίς απορριφθεί ένα υπερβολικό request, τόσο λιγότερους πόρους καταναλώνει το σύστημα.&lt;/p&gt;

&lt;p&gt;Ένα request που απορρίπτεται στο edge ή στο reverse proxy δεν φτάνει ποτέ σε parsing, model binding ή business logic.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Διαχωρισμός ευθυνών&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Η αρχιτεκτονική γίνεται πιο καθαρή όταν ξεχωρίζουμε τι ανήκει πού.&lt;/p&gt;

&lt;p&gt;Το Kestrel limit είναι infrastructure concern. Εξαρτάται από:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;το περιβάλλον εκτέλεσης&lt;/li&gt;
&lt;li&gt;το διαθέσιμο memory/CPU budget&lt;/li&gt;
&lt;li&gt;το expected load του συστήματος&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Για αυτό τον λόγο πρέπει να είναι configurable και environment-specific, συνήθως μέσω ρυθμίσεων όπως το appsettings.&lt;/p&gt;

&lt;p&gt;Αντίθετα, τα API-level limits αποτελούν business rules. Ορίζονται στο application layer και εκφράζουν τι επιτρέπεται λειτουργικά ανά endpoint ή use case, ανεξάρτητα από το υποκείμενο infrastructure.&lt;/p&gt;

&lt;p&gt;Ο σωστός διαχωρισμός είναι αυτός που επιτρέπει στο σύστημα να παραμένει σταθερό τεχνικά, ενώ οι επιχειρησιακοί κανόνες εξελίσσονται ανεξάρτητα από την υποδομή.&lt;/p&gt;

&lt;p&gt;Μην βασίζεσαι αποκλειστικά στο frontend validation.&lt;/p&gt;

&lt;p&gt;Κάθε layer πρέπει να θεωρεί το request μη έμπιστο.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Τι προστατεύουν οι περιορισμοί μεγέθους&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Οι περιορισμοί μεγέθους δεν προστατεύουν μόνο από μεγάλα uploads.&lt;/p&gt;

&lt;p&gt;Προστατεύουν επίσης από:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;memory exhaustion&lt;/li&gt;
&lt;li&gt;υπερβολικό JSON parsing&lt;/li&gt;
&lt;li&gt;επιθέσεις μέσω oversized payloads&lt;/li&gt;
&lt;li&gt;κατάχρηση file uploads&lt;/li&gt;
&lt;li&gt;αυξημένη κατανάλωση CPU και bandwidth&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Logging και Filtering: Middleware vs Controller Layer&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Η τοποθέτηση του logging και των request validations (όπως έλεγχοι μεγέθους ή απορρίψεις requests) αποτελεί κρίσιμη αρχιτεκτονική απόφαση. Σε γενικές γραμμές, οι cross-cutting concerns όπως το logging, το error handling και οι βασικοί τεχνικοί περιορισμοί (π.χ. request size limits) ανήκουν στο επίπεδο του middleware. Σε αυτό το σημείο του pipeline, το request μπορεί να καταγραφεί και να απορριφθεί νωρίς, πριν ενεργοποιηθούν μηχανισμοί όπως routing, model binding ή controllers, μειώνοντας το κόστος επεξεργασίας και βελτιώνοντας την απόδοση του συστήματος.&lt;/p&gt;

&lt;p&gt;Σε αυτό το στάδιο έχουμε ήδη πρόσβαση σε HTTP metadata όπως headers, Content-Length και stream δεδομένων, χωρίς να έχει εκτελεστεί ακόμα model binding ή business logic.&lt;/p&gt;

&lt;p&gt;Αυτό επιτρέπει την έγκαιρη απόρριψη προβληματικών requests πριν καταναλωθούν επιπλέον πόροι της εφαρμογής.&lt;/p&gt;

&lt;p&gt;Αντίθετα, η υλοποίηση αυτών των ελέγχων σε επίπεδο controller ή filter έχει νόημα όταν απαιτείται γνώση του συγκεκριμένου endpoint ή επιχειρησιακών κανόνων, όπως διαφορετικά όρια ανά use case ή domain-specific validation. Ωστόσο, σε αυτή την περίπτωση το request έχει ήδη διανύσει σημαντικό μέρος του pipeline, γεγονός που καθιστά την απόρριψη λιγότερο αποδοτική από πλευράς πόρων.&lt;/p&gt;

&lt;p&gt;Συνεπώς, η βέλτιστη προσέγγιση είναι ο διαχωρισμός ευθυνών: το middleware αναλαμβάνει τους τεχνικούς και οριζόντιους ελέγχους (logging, βασικά limits, error handling), ενώ το application layer επικεντρώνεται στους επιχειρησιακούς κανόνες. Σε περιβάλλοντα με αυξημένες απαιτήσεις (π.χ. cloud deployments με πολλαπλά ingress layers), αυτός ο διαχωρισμός συμβάλλει σε καλύτερη απόδοση, καθαρότερη αρχιτεκτονική και ευκολότερη συντήρηση.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Ένα σωστό παράδειγμα&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Reverse proxy / Azure&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;100 MB&lt;/span&gt;
&lt;span class="na"&gt;Kestrel&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;100 MB&lt;/span&gt;

&lt;span class="na"&gt;Endpoints&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;avatar&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;3 MB&lt;/span&gt;
  &lt;span class="na"&gt;document&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;25 MB&lt;/span&gt;
  &lt;span class="na"&gt;import&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;50 MB&lt;/span&gt;
  &lt;span class="na"&gt;JSON&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;512 KB&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Σε αυτή τη διάταξη:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;το σύστημα προστατεύεται συνολικά&lt;/li&gt;
&lt;li&gt;κάθε endpoint έχει σαφή όρια&lt;/li&gt;
&lt;li&gt;τα layers συνεργάζονται σωστά&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Τι επιστρέφει το API όταν απορρίπτει ένα request&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Στις περισσότερες περιπτώσεις, όταν ένα αίτημα υπερβαίνει το επιτρεπτό μέγεθος, το API ή το reverse proxy επιστρέφει:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;HTTP 413 Payload Too Large&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Σε application-level validations μπορεί να χρησιμοποιηθούν και άλλα status codes, όπως:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;400 Bad Request&lt;/li&gt;
&lt;li&gt;422 Unprocessable Entity&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ανάλογα με το είδος του περιορισμού.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Να θυμάσαι..&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Το request size δεν ελέγχεται από ένα σημείο, αλλά από μια αλυσίδα επιπέδων όπου κάθε layer μπορεί να επιβάλει τα δικά του όρια.&lt;/p&gt;

&lt;p&gt;Δεν έχει σημασία μόνο το τι έχεις ορίσει στον κώδικά σου, αλλά και το πού εκτελείται η εφαρμογή και ποια infrastructure layers προηγούνται.&lt;/p&gt;

&lt;p&gt;Στην πράξη, ο πραγματικός έλεγχος του request size είναι πάντα συνδυασμός:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;infrastructure constraints&lt;/li&gt;
&lt;li&gt;runtime behavior&lt;/li&gt;
&lt;li&gt;application-level business rules&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ο σωστός σχεδιασμός δεν είναι να επιλέξεις ένα επίπεδο, αλλά να τα ευθυγραμμίσεις ώστε να συνεργάζονται χωρίς αντιφάσεις.&lt;/p&gt;

&lt;p&gt;Δεν αρκεί να υπάρχει limit.&lt;br&gt;
Σημασία έχει σε ποιο layer εφαρμόζεται.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Πηγές&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://learn.microsoft.com/en-us/aspnet/core/fundamentals/servers/kestrel/options?view=aspnetcore-10.0&amp;amp;utm_source=chatgpt.com" rel="noopener noreferrer"&gt;Microsoft – Kestrel limits (core reference)&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://learn.microsoft.com/en-us/aspnet/core/fundamentals/servers/kestrel?view=aspnetcore-10.0&amp;amp;utm_source=chatgpt.com" rel="noopener noreferrer"&gt;Microsoft – Default limits &amp;amp; layering&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.requestsizelimitattribute?view=aspnetcore-10.0&amp;amp;utm_source=chatgpt.com" rel="noopener noreferrer"&gt;Microsoft – RequestSizeLimitAttribute (API layer)&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://sqlpey.com/c%23/aspnetcore-large-file-upload-limit-configuration/?utm_source=chatgpt.com" rel="noopener noreferrer"&gt;Multiple layers&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.textcontrol.com/blog/2024/04/12/adjusting-the-maximum-request-length-for-asp-net-core-and-asp-net-applications/?utm_source=chatgpt.com" rel="noopener noreferrer"&gt;IIS / Hosting layer (proxy level)&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://learn.microsoft.com/en-us/aspnet/core/fundamentals/servers/kestrel/options?view=aspnetcore-10.0&amp;amp;utm_source=chatgpt.com" rel="noopener noreferrer"&gt;Σημαντικό τεχνικό insight (override behavior)&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;a href="mailto:nikosstit@gmail.com"&gt;nikosstit@gmail.com&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Γιατί το parallelism δεν σώζει τα αργά queries</title>
      <dc:creator>nikosst</dc:creator>
      <pubDate>Thu, 23 Apr 2026 12:54:02 +0000</pubDate>
      <link>https://forem.com/__b63657/giati-to-baru-query-kanei-timeout-kai-giati-to-parallel-den-einai-e-prote-luse-51lj</link>
      <guid>https://forem.com/__b63657/giati-to-baru-query-kanei-timeout-kai-giati-to-parallel-den-einai-e-prote-luse-51lj</guid>
      <description>&lt;p&gt;&lt;strong&gt;Εισαγωγή&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Όταν ένα query κάνει timeout, στην ουσία το σύστημα σου λέει: «δεν μπορώ να περιμένω άλλο». Το timeout δεν είναι το πρόβλημα. Είναι ένας μηχανισμός προστασίας. Στις περισσότερες περιπτώσεις, όταν ένα query κάνει timeout, το πρόβλημα δεν είναι ότι “η βάση είναι αργή”. Είναι ότι το query κάνει περισσότερη δουλειά απ’ όση χρειάζεται. Το πραγματικό πρόβλημα είναι ότι το query είτε κάνει υπερβολική δουλειά, είτε το κάνει με λάθος τρόπο, είτε το σύστημα δεν αντέχει το φορτίο εκείνη τη στιγμή.&lt;/p&gt;

&lt;p&gt;Σε αυτό το σημείο πολλοί developers σκέφτονται: «ας το κάνω parallel για να τρέξει πιο γρήγορα». Η σκέψη είναι λογική, αλλά όχι πάντα σωστή. Για να καταλάβουμε γιατί, πρέπει πρώτα να δούμε τι σημαίνει πραγματικά parallel execution.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Τι είναι το parallel execution&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Σε έναν SQL Server, ένα query δεν εκτελείται πάντα από έναν μόνο πυρήνα CPU. Αν ο optimizer κρίνει ότι το query είναι αρκετά “βαρύ”, μπορεί να το σπάσει σε κομμάτια και να το εκτελέσει σε πολλούς πυρήνες ταυτόχρονα.&lt;/p&gt;

&lt;p&gt;Για παράδειγμα, αν έχεις έναν μεγάλο πίνακα Orders και εκτελέσεις:&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="k"&gt;SUM&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="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;Orders&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;η βάση μπορεί να χωρίσει τα δεδομένα σε τμήματα και να τα επεξεργαστεί παράλληλα. Στο τέλος, τα αποτελέσματα ενώνονται και δίνουν το τελικό άθροισμα.&lt;/p&gt;

&lt;p&gt;Αυτό είναι το parallelism. Δεν κάνει το query πιο “έξυπνο”. Το κάνει πιο “δυνατό”. Με άλλα λόγια, το parallelism προσπαθεί να ολοκληρώσει την ίδια δουλειά γρηγορότερα χρησιμοποιώντας περισσότερους πόρους CPU.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                Query
                   │
         ┌─────────┴─────────┐
         │                   │
      Thread 1           Thread 2
         │                   │
      Scan Part A        Scan Part B
         │                   │
         └─────────┬─────────┘
                   │
             Gather Streams
                   │
               Final Result

• περισσότερη CPU
• thread coordination
• CXPACKET / CXCONSUMER waits
• πιθανό faster elapsed time

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

&lt;/div&gt;






&lt;p&gt;&lt;strong&gt;Το βασικό λάθος: parallel ≠ optimization&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Tο parallel execution δεν μειώνει τη δουλειά που πρέπει να γίνει. Απλώς τη μοιράζει.&lt;/p&gt;

&lt;p&gt;Αν ένα query είναι κακοσχεδιασμένο, θα παραμείνει κακοσχεδιασμένο, απλώς θα εκτελείται σε περισσότερους πυρήνες. Το parallelism μπορεί να μειώσει τον elapsed time, αλλά δεν μειώνει τη συνολική δουλειά ούτε το συνολικό resource consumption.&lt;br&gt;
Παράδειγμα:&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="o"&gt;*&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;Name&lt;/span&gt; &lt;span class="k"&gt;LIKE&lt;/span&gt; &lt;span class="s1"&gt;'%john%'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Αυτό το query δεν μπορεί να χρησιμοποιήσει index και αναγκάζει τη βάση να σκανάρει όλο τον πίνακα. Αν το κάνεις parallel, απλώς μοιράζεις το scan σε πολλούς πυρήνες. Δεν μειώνεις το πρόβλημα.&lt;/p&gt;

&lt;p&gt;Αντίθετα:&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="o"&gt;*&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;Name&lt;/span&gt; &lt;span class="k"&gt;LIKE&lt;/span&gt; &lt;span class="s1"&gt;'john%'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;επιτρέπει τη χρήση index και μειώνει δραματικά τη δουλειά που πρέπει να γίνει. Αυτό είναι optimization.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Τι είναι το MAXDOP και τι ελέγχει&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Το MAXDOP (Maximum Degree of Parallelism) είναι το εργαλείο που σου δίνει έλεγχο στο parallel execution. Ορίζει πόσους πυρήνες μπορεί να χρησιμοποιήσει ένα query.&lt;/p&gt;

&lt;p&gt;Η σύνταξη είναι απλή:&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;OPTION&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MAXDOP&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;σημαίνει ότι το query θα εκτελεστεί σειριακά, δηλαδή σε έναν πυρήνα.&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;OPTION&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MAXDOP&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;σημαίνει ότι μπορεί να χρησιμοποιήσει μέχρι 4 πυρήνες.&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;OPTION&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MAXDOP&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;σημαίνει ότι αφήνεις τη βάση να αποφασίσει μόνη της.&lt;/p&gt;

&lt;p&gt;Το σημαντικό εδώ είναι ότι το MAXDOP δεν είναι “boost”. Είναι περιορισμός.&lt;/p&gt;

&lt;p&gt;Το MAXDOP δεν αναγκάζει ένα query να γίνει parallel. Απλώς ορίζει το μέγιστο επίπεδο παραλληλισμού που επιτρέπεται αν ο optimizer επιλέξει parallel plan.&lt;/p&gt;

&lt;p&gt;Σημαντικό: Το MAXDOP 1 δεν κάνει απαραίτητα ένα query πιο γρήγορο σε απόλυτο χρόνο. Το βασικό του όφελος εμφανίζεται κυρίως σε production περιβάλλοντα με πολλούς ταυτόχρονους χρήστες, όπου τα parallel workers ανταγωνίζονται μεταξύ τους για CPU και μπορούν να επηρεάσουν αρνητικά το συνολικό throughput του SQL Server. Σε localhost ή σε περιβάλλον με έναν μόνο χρήστη, αυτό το contention συνήθως δεν υπάρχει, οπότε το parallel execution μπορεί να είναι εξίσου γρήγορο ή και ταχύτερο.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Κριτήρια Παραλληλισμού και Σημασία του MAXDOP 1&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Πότε το SQL Server Χρησιμοποιεί Πολλαπλούς Πυρήνες;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Το SQL Server αυτόματα αποφασίζει να χρησιμοποιήσει παραλληλισμό (πολλαπλούς CPU πυρήνες) όταν το εκτιμώμενο κόστος εκτέλεσης (estimated execution cost) ενός query είναι μεγαλύτερο από 5. Αυτή η τιμή ονομάζεται "Cost Threshold for Parallelism" και είναι η default ρύθμιση του SQL Server. &lt;/p&gt;

&lt;p&gt;Το threshold αυτό δεν είναι “χρόνος εκτέλεσης” ούτε απόλυτη μονάδα μέτρησης. Είναι εσωτερικό cost metric του optimizer που χρησιμοποιείται μόνο για σύγκριση execution plans.&lt;/p&gt;

&lt;p&gt;Για queries που ξεπερνούν αυτό το όριο, ο SQL Server ενεργοποιεί παραλληλισμό χρησιμοποιώντας 4-8 threads (ανάλογα με το Max Degree of Parallelism configuration του server), προσπαθώντας να επιταχύνει την εκτέλεση μοιράζοντας τη δουλειά σε πολλαπλούς πυρήνες.&lt;/p&gt;

&lt;p&gt;Το πρόβλημα: Για τα OLTP queries της εφαρμογής μας (GetEmployeeById, GetCitiesFiltered, κλπ.), που έχουν 3-8 Includes και επιστρέφουν 1-50 rows, το estimated cost συχνά είναι 6-15 (λόγω των joins), οπότε ενεργοποιείται παραλληλισμός χωρίς να είναι αναγκαίος. Αυτό προσθέτει 20-40% overhead λόγω:&lt;/p&gt;

&lt;p&gt;• CXPACKET waits: Τα threads περιμένουν το πιο αργό thread για συγχρονισμό&lt;br&gt;
• Thread coordination: Κόστος δημιουργίας και διαχείρισης πολλαπλών threads&lt;br&gt;
• Context switching: Εναλλαγή μεταξύ threads στον CPU&lt;/p&gt;



&lt;p&gt;Μέχρι εδώ είδαμε τι κάνει το parallelism. Για να καταλάβουμε όμως γιατί ο SQL Server αποφασίζει να το χρησιμοποιήσει, πρέπει να δούμε πώς ο optimizer εκτιμά το κόστος ενός query.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Πώς Αξιολογεί πραγματικά ο SQL Server Query Optimizer το Κόστος ενός Query&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Ο SQL Server Query Optimizer χρησιμοποιεί ένα cost-based μοντέλο, αλλά όχι με τον απλοϊκό τρόπο που συχνά παρουσιάζεται. Το “estimated cost” δεν είναι πραγματικός χρόνος εκτέλεσης ούτε υπολογίζεται με σταθερό formula (π.χ. base cost + joins). Είναι ένα σχετικό metric, βασισμένο κυρίως σε εκτιμήσεις (estimates) για το πόσα rows θα επεξεργαστούν και τι πόρους (CPU/I/O) θα χρειαστούν τα διαφορετικά execution plans.&lt;/p&gt;

&lt;p&gt;Στην πράξη, ο optimizer:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;εκτιμά πόσα rows θα επεξεργαστεί&lt;/li&gt;
&lt;li&gt;επιλέγει τρόπους πρόσβασης στα δεδομένα (seek/scan)&lt;/li&gt;
&lt;li&gt;αποφασίζει join strategies&lt;/li&gt;
&lt;li&gt;και συγκρίνει διαφορετικά execution plans&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Σημαντικό: το cost χρησιμοποιείται μόνο για σύγκριση μεταξύ plans, όχι ως απόλυτη ένδειξη απόδοσης, και μπορεί να είναι λάθος αν τα statistics δεν είναι accurate.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Τι επηρεάζει πραγματικά το cost (χωρίς oversimplification)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Αν και δεν υπάρχει “επίσημο formula”, στην πράξη το cost επηρεάζεται κυρίως από:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cardinality (rows) → ο πιο σημαντικός παράγοντας (multiplicative effect)&lt;/li&gt;
&lt;li&gt;Τύπος πρόσβασης δεδομένων → seek vs scan&lt;/li&gt;
&lt;li&gt;Join complexity → αριθμός joins και είδος (nested loop, hash, merge)&lt;/li&gt;
&lt;li&gt;Execution tree depth → nested includes / subqueries&lt;/li&gt;
&lt;li&gt;Filters &amp;amp; predicates → ειδικά σε non-indexed columns&lt;/li&gt;
&lt;li&gt;Aggregations / sorting → GROUP BY, ORDER BY αυξάνουν σημαντικά το cost&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;seek vs scan&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Στις βάσεις δεδομένων όπως το SQL Server, η διαφορά μεταξύ seek και scan αφορά τον τρόπο πρόσβασης στα δεδομένα.&lt;/p&gt;

&lt;p&gt;• Index Seek&lt;br&gt;
Στοχευμένη πρόσβαση μέσω index για εύρεση συγκεκριμένων rows.&lt;/p&gt;

&lt;p&gt;Παράδειγμα:&lt;br&gt;
SELECT * FROM Users WHERE Id = 10;&lt;/p&gt;

&lt;p&gt;• Index/Table Scan&lt;br&gt;
Ανάγνωση μεγάλου μέρους ή ολόκληρου του πίνακα για εύρεση δεδομένων.&lt;/p&gt;

&lt;p&gt;Παράδειγμα:&lt;br&gt;
SELECT * FROM Users WHERE Name LIKE '%nikos%';&lt;/p&gt;

&lt;p&gt;Συνήθως τα seeks είναι πολύ πιο αποδοτικά σε μεγάλα datasets, γιατί μειώνουν δραματικά τον όγκο δεδομένων που πρέπει να διαβαστεί.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ρεαλιστικό παράδειγμα (όχι “μαγικά νούμερα”)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Ένα απλό OLTP query:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;PK seek (πολύ χαμηλό cost)&lt;/li&gt;
&lt;li&gt;3 απλά joins σε indexed FKs&lt;/li&gt;
&lt;li&gt;μικρό result set (π.χ. 1 row)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Estimated cost: ~2–3&lt;br&gt;
Δεν θα γίνει ποτέ parallel (κάτω από threshold)&lt;br&gt;
Ένα πιο σύνθετο query:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;scan ή range seek&lt;/li&gt;
&lt;li&gt;5–6 joins&lt;/li&gt;
&lt;li&gt;authorization filter (subquery)&lt;/li&gt;
&lt;li&gt;μεγαλύτερο intermediate dataset&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Estimated cost: ~8–12&lt;br&gt;
Πιθανό parallel plan&lt;/p&gt;

&lt;p&gt;Ένα reporting query:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;`full scan μεγάλου πίνακα&lt;/li&gt;
&lt;li&gt;GROUP BY + aggregations&lt;/li&gt;
&lt;li&gt;δεκάδες χιλιάδες rows
`&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Estimated cost: 100+ έως 1000+&lt;br&gt;
Parallelism είναι σχεδόν απαραίτητο&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Πού μπαίνει το Parallelism (και γιατί δεν είναι πάντα λύση)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Όταν το estimated cost ξεπεράσει το Cost Threshold for Parallelism (~5), ο optimizer μπορεί να επιλέξει parallel execution (ανάλογα με MAXDOP και server load).&lt;/p&gt;

&lt;p&gt;Όμως:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;το parallelism εισάγει coordination overhead (exchange operators, thread sync)&lt;/li&gt;
&lt;li&gt;εμφανίζονται waits όπως CXPACKET / CXCONSUMER&lt;/li&gt;
&lt;li&gt;σε μικρά OLTP queries, το overhead μπορεί να είναι μεγαλύτερο από το όφελος&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Άρα: “μεγάλο cost ⇒ βάλε parallel” είναι oversimplification.&lt;/p&gt;

&lt;p&gt;Πρακτικά κριτήρια για χρήση MAXDOP 1&lt;/p&gt;

&lt;p&gt;Για να αποφασίσεις σωστά, κοίτα:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Estimated Cost&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt; &amp;lt; 4.5 → ποτέ parallel → μην βάζεις MAXDOP&lt;/li&gt;
&lt;li&gt; 4.5–6 → borderline&lt;/li&gt;
&lt;li&gt; 6 → πιθανό parallel&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;Query shape&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;πολλά joins / nested includes&lt;/li&gt;
&lt;li&gt;subqueries / authorization filters&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;Workload type&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;OLTP (μικρά, συχνά queries) → αποφεύγεις parallelism&lt;/li&gt;
&lt;li&gt;OLAP/reporting → το θέλεις&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;Runtime signals&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;CXPACKET / CXCONSUMER waits&lt;/li&gt;
&lt;li&gt;υψηλό CPU χωρίς αντίστοιχο throughput&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Συμπέρασμα&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Το cost του SQL Server δεν είναι “μαθηματικός τύπος”, αλλά αποτέλεσμα εκτιμήσεων πάνω σε δεδομένα και statistics. Το parallelism ενεργοποιείται βάσει αυτού του cost, αλλά δεν είναι εγγύηση καλύτερης απόδοσης — ειδικά σε OLTP σενάρια. Γι’ αυτό, η χρήση του MAXDOP 1 πρέπει να είναι στοχευμένη και βασισμένη σε cost, query shape και runtime behavior, όχι γενική πρακτική.&lt;/p&gt;



&lt;p&gt;&lt;strong&gt;Γιατί Είναι Σημαντικό το MAXDOP 1;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Το &lt;strong&gt;MAXDOP 1&lt;/strong&gt; (Max Degree of Parallelism = 1) επιβάλλει single-threaded εκτέλεση, εξαλείφοντας εντελώς το overhead του παραλληλισμού. Για interactive queries που επιστρέφουν λίγα records, αυτό έχει δραματικά οφέλη:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1.Απόδοση:&lt;/strong&gt; Βελτίωση 10-25% σε CPU time και elapsed time, καθώς αφαιρείται το overhead συγχρονισμού&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2.Προβλεψιμότητα:&lt;/strong&gt; Σταθερός χρόνος απόκρισης χωρίς CXPACKET waits&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3.Scalability:&lt;/strong&gt; Σε high concurrency περιβάλλοντα, το MAXDOP 1 ελευθερώνει CPU πυρήνες για άλλα requests αντί να τους "κλειδώνει" σε παραλληλισμό ενός query&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4.Resource Efficiency:&lt;/strong&gt; Μειωμένη κατανάλωση thread pool resources&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Πότε ΔΕΝ πρέπει να χρησιμοποιείται:&lt;/strong&gt; Για reports, exports, ή queries με GROUP BY/aggregations που επεξεργάζονται χιλιάδες rows, ο παραλληλισμός είναι απαραίτητος και το MAXDOP 1 θα τα κάνει πολύ πιο αργά.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Στην περίπτωσή μας:&lt;/strong&gt; Εφαρμόζουμε MAXDOP 1 μόνο σε 44 interactive OLTP queries με ≤8 includes, όπου ο παραλληλισμός είναι περισσότερο "βάρος" παρά όφελος. Τα αποτελέσματα θα επαληθευτούν με SSMS testing συγκρίνοντας CPU time, elapsed time, και CXPACKET waits.&lt;/p&gt;



&lt;p&gt;&lt;strong&gt;Πότε χρησιμοποιείς MAXDOP 1&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Υπάρχουν περιπτώσεις όπου το να περιορίσεις το parallelism είναι πιο σημαντικό από το να κάνεις ένα query πιο γρήγορο. Αυτό συμβαίνει κυρίως σε συστήματα με πολλούς ταυτόχρονους χρήστες.&lt;/p&gt;

&lt;p&gt;Φαντάσου ένα API που κάνει αναζήτηση υπαλλήλων. Το endpoint αυτό το χτυπάνε 20 χρήστες ταυτόχρονα. Αν κάθε query χρησιμοποιεί 4 ή 8 πυρήνες, τότε πολύ γρήγορα εξαντλείται η CPU και όλα τα queries αρχίζουν να καθυστερούν.&lt;/p&gt;

&lt;p&gt;Σε αυτή την περίπτωση, αν βάλεις:&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;OPTION&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MAXDOP&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;κάθε query χρησιμοποιεί μόνο έναν πυρήνα. Μπορεί να είναι λίγο πιο αργό μεμονωμένα, αλλά το σύστημα συνολικά γίνεται πιο σταθερό και μπορεί να εξυπηρετήσει περισσότερους χρήστες ταυτόχρονα.&lt;/p&gt;

&lt;p&gt;Αυτό είναι ένα κλασικό trade-off μεταξύ individual query latency και συνολικού system throughput.&lt;/p&gt;

&lt;p&gt;Σε high concurrency συστήματα, συνήθως προτιμάς σταθερότητα και προβλεψιμότητα αντί για το να “πετάει” ένα μόνο query χρησιμοποιώντας πολλούς πυρήνες.&lt;/p&gt;

&lt;p&gt;Αυτός είναι ο λόγος που σε OLTP συστήματα (APIs, web εφαρμογές) συχνά περιορίζουμε το parallelism.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Πότε αφήνεις ή αυξάνεις το MAXDOP&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Σε αντίθεση με τα APIs, υπάρχουν workloads όπου θέλεις να εκμεταλλευτείς πλήρως την CPU. Αυτά είναι συνήθως reports, aggregations ή batch jobs που τρέχουν λιγότερο συχνά αλλά επεξεργάζονται μεγάλα datasets.&lt;/p&gt;

&lt;p&gt;Παράδειγμα:&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;Region&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;SUM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Sales&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;SalesData&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;Region&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Αυτό είναι ένα query που επωφελείται από parallel execution. Δεν έχεις πολλούς χρήστες να το χτυπάνε ταυτόχρονα, και σε νοιάζει να τελειώσει όσο πιο γρήγορα γίνεται.&lt;/p&gt;

&lt;p&gt;Εδώ το να αφήσεις το default MAXDOP ή να επιτρέψεις περισσότερους πυρήνες είναι σωστή επιλογή.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Το hidden κόστος του parallelism&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Το parallel execution έχει κόστος που δεν φαίνεται άμεσα. Όταν ένα query εκτελείται σε πολλούς πυρήνες, δημιουργείται ανάγκη για συντονισμό μεταξύ των threads. Κάποια threads μπορεί να τελειώνουν νωρίτερα και να περιμένουν τα υπόλοιπα. Αυτή η αναμονή εμφανίζεται ως waits όπως &lt;code&gt;CXPACKET&lt;/code&gt; ή &lt;code&gt;CXCONSUMER&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Στις νεότερες εκδόσεις του SQL Server, μεγάλο μέρος των parallel waits εμφανίζεται πλέον ως CXCONSUMER, ώστε να διαχωρίζεται το φυσιολογικό coordination overhead από τα πραγματικά προβληματικά waits.&lt;/p&gt;

&lt;p&gt;Γι’ αυτό, η ύπαρξη CXPACKET ή CXCONSUMER waits δεν σημαίνει αυτόματα ότι υπάρχει πρόβλημα. Η αξιολόγηση πρέπει να γίνεται μαζί με CPU usage, query duration και execution plans.&lt;/p&gt;

&lt;p&gt;Το &lt;code&gt;CXPACKET&lt;/code&gt; σημαίνει ότι ένα thread περιμένει τα υπόλοιπα threads του ίδιου query να ολοκληρώσουν τη δουλειά τους. Συνήθως δείχνει ότι η κατανομή του workload δεν είναι ισορροπημένη. Το &lt;code&gt;CXCONSUMER&lt;/code&gt; εμφανίζεται όταν ένα thread περιμένει να λάβει δεδομένα από άλλα threads μέσα στη ροή εκτέλεσης. Αυτό είναι πιο φυσιολογικό σε parallel queries.&lt;/p&gt;

&lt;p&gt;Το σημαντικό είναι ότι αυτά τα waits δεν είναι πάντα πρόβλημα, αλλά όταν κυριαρχούν, δείχνουν ότι το parallelism δεν λειτουργεί αποδοτικά.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Γιατί το parallelism μπορεί να κάνει τα πράγματα χειρότερα&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Σε περιβάλλοντα με υψηλό concurrency, το parallelism μπορεί να δημιουργήσει συμφόρηση. Αν έχεις πολλούς χρήστες και κάθε query χρησιμοποιεί πολλούς πυρήνες, το σύστημα αρχίζει να “πνίγεται”.&lt;/p&gt;

&lt;p&gt;Αυτό είναι ιδιαίτερα επικίνδυνο σε APIs με υψηλό concurrency, όπου δεκάδες requests μπορεί να εκτελούνται ταυτόχρονα. Ένα μόνο expensive parallel query μπορεί να επηρεάσει δυσανάλογα ολόκληρο το workload του server.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Πού πρέπει να εστιάσεις πραγματικά&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Το πιο σημαντικό σημείο είναι ότι τα περισσότερα performance προβλήματα δεν λύνονται με parallelism. Λύνονται με σωστό σχεδιασμό.&lt;/p&gt;

&lt;p&gt;Αν ένα query κάνει full scan, αν επιστρέφει υπερβολικά πολλά δεδομένα ή αν χρησιμοποιεί μη αποδοτικά φίλτρα, το πρόβλημα δεν είναι πόσους πυρήνες χρησιμοποιεί, αλλά πόση δουλειά κάνει.&lt;/p&gt;

&lt;p&gt;Η σωστή προσέγγιση είναι πάντα η ίδια: πρώτα μειώνεις τη δουλειά που πρέπει να γίνει και μετά, αν χρειάζεται, βελτιστοποιείς τον τρόπο που εκτελείται.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Τι είναι τα OLTP συστήματα?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Τα OLTP (Online Transaction Processing) συστήματα είναι βάσεις/συστήματα που έχουν σχεδιαστεί για να διαχειρίζονται πολλές μικρές, γρήγορες συναλλαγές σε πραγματικό χρόνο (π.χ. insert/update/delete).&lt;/p&gt;

&lt;p&gt;Με απλά λόγια:&lt;/p&gt;

&lt;p&gt;είναι τα “operational” συστήματα που τρέχουν την καθημερινή λειτουργία μιας εφαρμογής (π.χ. orders, payments, employee data).&lt;/p&gt;

&lt;p&gt;Παραδείγματα:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;σύστημα παραγγελιών (orders)&lt;/li&gt;
&lt;li&gt;τραπεζικές συναλλαγές&lt;/li&gt;
&lt;li&gt;HR systems (employees, μισθοδοσία)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Χαρακτηριστικά:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;πολλά concurrent users&lt;/li&gt;
&lt;li&gt;γρήγορα queries (συνήθως απλά)&lt;/li&gt;
&lt;li&gt;έμφαση σε consistency &amp;amp; integrity (ACID)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Σε αντίθεση με OLAP (analytics), τα OLTP είναι για run the business, όχι για reporting/analysis.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Τι είναι τα OLAP συστήματα?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Τα OLAP (Online Analytical Processing) συστήματα είναι σχεδιασμένα για ανάλυση δεδομένων και reporting, όχι για καθημερινές συναλλαγές.&lt;/p&gt;

&lt;p&gt;Με απλά λόγια:&lt;/p&gt;

&lt;p&gt;είναι τα συστήματα που χρησιμοποιείς για να βγάζεις insights από τα δεδομένα (π.χ. reports, dashboards, trends).&lt;/p&gt;

&lt;p&gt;Παραδείγματα:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;data warehouse&lt;/li&gt;
&lt;li&gt;BI tools (Power BI, Tableau)&lt;/li&gt;
&lt;li&gt;sales / finance reports&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Χαρακτηριστικά:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;δουλεύουν σε μεγάλα volumes δεδομένων&lt;/li&gt;
&lt;li&gt;queries είναι πιο “βαριά” (aggregations, joins)&lt;/li&gt;
&lt;li&gt;λιγότερα writes, κυρίως reads&lt;/li&gt;
&lt;li&gt;optimized για analysis (GROUP BY, trends, KPIs)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Σε αντίθεση με OLTP (που είναι για transactions), τα OLAP είναι για analysis &amp;amp; decision making.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Τα timeouts δεν σημαίνουν πάντα “αργό query”&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Ένα timeout μπορεί να προκληθεί από:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;blocking&lt;/li&gt;
&lt;li&gt;lock contention&lt;/li&gt;
&lt;li&gt;thread pool starvation&lt;/li&gt;
&lt;li&gt;υψηλό CPU load&lt;/li&gt;
&lt;li&gt;parameter sniffing&lt;/li&gt;
&lt;li&gt;κακό execution plan&lt;/li&gt;
&lt;li&gt;network delays&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Γι’ αυτό, πριν θεωρήσεις ότι το πρόβλημα είναι το parallelism ή το MAXDOP, πρέπει πρώτα να εξετάσεις execution plans, waits, runtime metrics και concurrency behavior.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Να θυμάσαι..&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Το parallel execution είναι ένα ισχυρό χαρακτηριστικό, αλλά δεν είναι λύση από μόνο του. Το MAXDOP δεν είναι εργαλείο επιτάχυνσης, αλλά εργαλείο ελέγχου.&lt;/p&gt;

&lt;p&gt;Σε ένα καλά σχεδιασμένο σύστημα, χρησιμοποιείς parallelism εκεί που πραγματικά προσφέρει αξία και το περιορίζεις εκεί που μπορεί να προκαλέσει αστάθεια.&lt;/p&gt;

&lt;p&gt;Η ουσία είναι απλή:&lt;/p&gt;

&lt;p&gt;Δεν προσπαθείς να κάνεις το query πιο “δυνατό”.&lt;br&gt;
Προσπαθείς να το κάνεις πιο “έξυπνο”.&lt;/p&gt;

&lt;p&gt;Το parallelism είναι εργαλείο κλιμάκωσης, όχι υποκατάστατο σωστού query design. Αν ένα query χρειάζεται περισσότερη CPU για να “σωθεί”, συνήθως αξίζει πρώτα να αναρωτηθείς γιατί κάνει τόση δουλειά εξαρχής.&lt;/p&gt;

&lt;p&gt;Και όταν αυτό δεν φτάνει, τότε — και μόνο τότε — αποφασίζεις πόση CPU αξίζει να του δώσεις.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Συχνό λάθος mindset&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Ένα συχνό λάθος είναι να αντιμετωπίζεται το parallelism σαν “θεραπεία” για κάθε αργό query.&lt;/p&gt;

&lt;p&gt;Στην πράξη, αν ένα query:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;διαβάζει υπερβολικά πολλά δεδομένα,&lt;/li&gt;
&lt;li&gt;κάνει scans αντί για seeks,&lt;/li&gt;
&lt;li&gt;επιστρέφει περισσότερα rows από όσα χρειάζονται,&lt;/li&gt;
&lt;li&gt;ή βασίζεται σε κακά execution plans,&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;τότε το parallelism συνήθως απλώς μεταφέρει το πρόβλημα σε περισσότερους CPU πυρήνες.&lt;/p&gt;

&lt;p&gt;Το πρώτο βήμα πρέπει σχεδόν πάντα να είναι:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;καλύτερα indexes,&lt;/li&gt;
&lt;li&gt;λιγότερα reads,&lt;/li&gt;
&lt;li&gt;σωστό filtering,&lt;/li&gt;
&lt;li&gt;και καλύτερο query shape.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Πρώτα μειώνεις τη δουλειά.&lt;br&gt;
Μετά αποφασίζεις πόση CPU αξίζει να της δώσεις.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Τι να ελέγξεις πρώτα όταν βλέπεις timeouts&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Πριν αλλάξεις MAXDOP ή προσπαθήσεις να αυξήσεις το parallelism, έλεγξε:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Actual execution plan&lt;/li&gt;
&lt;li&gt;Index usage (seek vs scan)&lt;/li&gt;
&lt;li&gt;Missing/inefficient indexes&lt;/li&gt;
&lt;li&gt;Query duration &amp;amp; CPU time&lt;/li&gt;
&lt;li&gt;Wait statistics&lt;/li&gt;
&lt;li&gt;Blocking / deadlocks&lt;/li&gt;
&lt;li&gt;Statistics freshness&lt;/li&gt;
&lt;li&gt;Query Store history&lt;/li&gt;
&lt;li&gt;Number of returned rows&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Στις περισσότερες περιπτώσεις, το bottleneck φαίνεται πολύ πιο καθαρά στα execution plans και στα waits παρά στο ίδιο το timeout.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Microsoft sources&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://learn.microsoft.com/en-us/troubleshoot/sql/database-engine/performance/troubleshoot-query-timeouts?utm_source=chatgpt.com" rel="noopener noreferrer"&gt;Troubleshoot query time-out errors&lt;/a&gt;&lt;br&gt;
Microsoft λέει ότι ο πρώτος στόχος είναι να κάνεις το query πιο γρήγορο και να εντοπίσεις ποιο query προκαλεί timeout με Extended Events / traces.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://learn.microsoft.com/en-us/troubleshoot/sql/database-engine/performance/troubleshoot-slow-running-queries?utm_source=chatgpt.com" rel="noopener noreferrer"&gt;Troubleshoot slow-running queries&lt;/a&gt;&lt;br&gt;
Προτείνει να ξεκινάς από elapsed time, waits, CPU time και bottleneck analysis — όχι από “βάλε parallel”.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://learn.microsoft.com/en-us/sql/relational-databases/query-processing-architecture-guide?view=sql-server-ver17&amp;amp;utm_source=chatgpt.com" rel="noopener noreferrer"&gt;Query Processing Architecture Guide&lt;/a&gt;&lt;br&gt;
Εξηγεί ότι parallel plan μπορεί να τελειώσει πιο γρήγορα, αλλά χρησιμοποιεί περισσότερους πόρους και ο optimizer το επιλέγει μόνο αν δεν επηρεάζει αρνητικά το server load.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://learn.microsoft.com/en-us/sql/database-engine/configure-windows/configure-the-max-degree-of-parallelism-server-configuration-option?view=sql-server-ver17&amp;amp;utm_source=chatgpt.com" rel="noopener noreferrer"&gt;MAXDOP configuration&lt;/a&gt;&lt;br&gt;
Το MAXDOP απλώς περιορίζει πόσους processors μπορεί να χρησιμοποιήσει ένα parallel plan· δεν είναι γενική θεραπεία για κακό query.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://learn.microsoft.com/en-us/sql/database-engine/configure-windows/configure-the-cost-threshold-for-parallelism-server-configuration-option?view=sql-server-ver17&amp;amp;utm_source=chatgpt.com" rel="noopener noreferrer"&gt;Cost Threshold for Parallelism&lt;/a&gt;&lt;br&gt;
Εξηγεί πότε ο SQL Server σκέφτεται parallel plan· άρα είναι configuration tuning, όχι αντικατάσταση query/index tuning.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://learn.microsoft.com/en-us/sql/relational-databases/performance/execution-plans?view=sql-server-ver17&amp;amp;utm_source=chatgpt.com" rel="noopener noreferrer"&gt;Execution Plans&lt;/a&gt;&lt;br&gt;
Τα execution plans είναι το βασικό εργαλείο για να δεις πώς ο optimizer αποφάσισε να τρέξει το query.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://learn.microsoft.com/en-us/sql/relational-databases/sql-server-index-design-guide?view=sql-server-ver17&amp;amp;utm_source=chatgpt.com" rel="noopener noreferrer"&gt;Index Design Guide&lt;/a&gt;&lt;br&gt;
Microsoft γράφει ξεκάθαρα ότι efficient indexes είναι κλειδί για καλή database/application performance.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://learn.microsoft.com/en-us/sql/relational-databases/statistics/statistics?view=sql-server-ver17&amp;amp;utm_source=chatgpt.com" rel="noopener noreferrer"&gt;Statistics&lt;/a&gt;&lt;br&gt;
Ο optimizer βασίζεται σε statistics για να φτιάξει καλά query plans· λάθος/παλιά stats μπορούν να οδηγήσουν σε κακό plan.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://learn.microsoft.com/en-us/sql/relational-databases/performance/monitoring-performance-by-using-the-query-store?view=sql-server-ver17&amp;amp;utm_source=chatgpt.com" rel="noopener noreferrer"&gt;Query Store&lt;/a&gt;&lt;br&gt;
Query Store κρατά ιστορικό queries, plans και runtime stats, άρα είναι σωστό εργαλείο για να βρεις regressions και top resource-consuming queries.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://learn.microsoft.com/en-us/troubleshoot/sql/database-engine/performance/understand-resolve-blocking?utm_source=chatgpt.com" rel="noopener noreferrer"&gt;Blocking problems&lt;/a&gt;&lt;br&gt;
Timeouts μπορεί να οφείλονται και σε blocking, όχι απαραίτητα σε “αργό query”.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Άλλες αξιόπιστες πηγές&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.brentozar.com/blitz/configuring-parallelism/?utm_source=chatgpt.com" rel="noopener noreferrer"&gt;Brent Ozar – Parallelism settings&lt;/a&gt;: λέει ότι τα default MAXDOP/Cost Threshold συχνά είναι κακά και ένα query μπορεί να απλωθεί σε πολλά cores και να επηρεάσει άλλους χρήστες.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.sommarskog.se/query-plan-mysteries.html?utm_source=chatgpt.com" rel="noopener noreferrer"&gt;Erland Sommarskog – Slow in the Application, Fast in SSMS?&lt;/a&gt;: εξηγεί ότι application timeouts/slow queries συχνά σχετίζονται με plans, cache, parameter sniffing κ.λπ., όχι απλά με έλλειψη parallelism.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.red-gate.com/hub/product-learning/redgate-monitor/troubleshooting-sql-server-queries-using-actual-execution-plans?utm_source=chatgpt.com" rel="noopener noreferrer"&gt;Redgate – Actual execution plans&lt;/a&gt;: τονίζει ότι για expensive/slow queries χρειάζεσαι actual execution plan με runtime statistics.&lt;/p&gt;




&lt;p&gt;&lt;a href="mailto:nikosstit@gmail.com"&gt;nikosstit@gmail.com&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>AsTracking vs AsNoTracking στο Entity Framework Core Πλήρης Ανάλυση με Παραδείγματα</title>
      <dc:creator>nikosst</dc:creator>
      <pubDate>Mon, 30 Mar 2026 13:39:07 +0000</pubDate>
      <link>https://forem.com/__b63657/astracking-vs-asnotracking-sto-entity-framework-core-pleres-analuse-me-paradeigmata-42oa</link>
      <guid>https://forem.com/__b63657/astracking-vs-asnotracking-sto-entity-framework-core-pleres-analuse-me-paradeigmata-42oa</guid>
      <description>&lt;p&gt;Όταν δουλεύουμε με Entity Framework Core, ένα από τα πιο παρεξηγημένα αλλά ταυτόχρονα και κρίσιμα για την απόδοση θέματα είναι το &lt;strong&gt;change tracking&lt;/strong&gt;. Πολλοί developers γράφουν queries χωρίς να συνειδητοποιούν ότι το EF Core παρακολουθεί (trackάρει) κάθε entity που επιστρέφεται από τη βάση.&lt;/p&gt;

&lt;p&gt;Αυτή η default συμπεριφορά μπορεί να είναι είτε εξαιρετικά χρήσιμη είτε εντελώς περιττή, ανάλογα με το σενάριο.&lt;/p&gt;

&lt;p&gt;Με τον όρο tracking εννοούμε ότι το DbContext κρατά πληροφορίες για τα entities που φόρτωσε από τη βάση, ώστε να μπορεί αργότερα να εντοπίσει αλλαγές και να εκτελέσει σωστά το SaveChanges().&lt;/p&gt;

&lt;p&gt;Με απλά λόγια:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AsTracking() → το EF Core παρακολουθεί αλλαγές στα entities&lt;/li&gt;
&lt;li&gt;AsNoTracking() → το EF Core επιστρέφει δεδομένα χωρίς να παρακολουθεί αλλαγές&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Σε αυτό το άρθρο θα δούμε τι κάνουν τα &lt;strong&gt;AsTracking()&lt;/strong&gt; και &lt;strong&gt;AsNoTracking()&lt;/strong&gt;, πώς επηρεάζουν την απόδοση και πότε πρέπει να χρησιμοποιούμε το καθένα.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Κατανόηση του Change Tracking&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Στην καρδιά του Entity Framework Core βρίσκεται ο Change Tracker, αλλά είναι πολύ σημαντικό να ξεκαθαρίσουμε κάτι από την αρχή: το EF δεν παρακολουθεί τη βάση δεδομένων, παρακολουθεί τα objects που υπάρχουν στη μνήμη.&lt;/p&gt;

&lt;p&gt;Όταν εκτελείς ένα query, το EF φέρνει δεδομένα από τη βάση και δημιουργεί αντίστοιχα C# objects. Αυτά τα objects αποθηκεύονται στο Change Tracker μαζί με την αρχική τους κατάσταση (π.χ. τις αρχικές τιμές των properties τους). Από εκεί και πέρα, το EF παρακολουθεί μόνο αυτά τα objects στη μνήμη.&lt;/p&gt;

&lt;p&gt;Αυτό σημαίνει ότι αν αλλάξεις μια τιμή σε κάποιο object, το EF μπορεί να το εντοπίσει γιατί συγκρίνει την αρχική τιμή που κράτησε με τη νέα τιμή που έχει τώρα στη μνήμη. Όταν καλέσεις SaveChanges(), το EF μετατρέπει αυτές τις αλλαγές σε SQL UPDATE και τις στέλνει στη βάση.&lt;/p&gt;

&lt;p&gt;Το workflow λοιπόν είναι το εξής:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Το EF Core φορτώνει δεδομένα από τη βάση&lt;/li&gt;
&lt;li&gt;Δημιουργεί entities στη μνήμη&lt;/li&gt;
&lt;li&gt;Αποθηκεύει την αρχική τους κατάσταση στο Change Tracker&lt;/li&gt;
&lt;li&gt;Παρακολουθεί αλλαγές στα properties&lt;/li&gt;
&lt;li&gt;Στο SaveChanges() μετατρέπει τις αλλαγές σε SQL UPDATE/INSERT/DELETE&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Το tracking του EF Core δεν είναι μηχανισμός συγχρονισμού με τη βάση δεδομένων. Είναι μηχανισμός παρακολούθησης in-memory entity state μέσα στο DbContext.&lt;/p&gt;

&lt;p&gt;Αυτό σημαίνει ότι το EF Core δεν “παρακολουθεί” εξωτερικές αλλαγές που γίνονται απευθείας στη βάση από άλλα systems ή DbContext instances.&lt;/p&gt;

&lt;p&gt;Ένα κρίσιμο σημείο που συχνά μπερδεύει είναι το εξής:&lt;br&gt;
αν κάποιος άλλος (ή άλλο σύστημα) αλλάξει τα δεδομένα απευθείας στη βάση μετά που εσύ τα έχεις φορτώσει, το EF δεν το γνωρίζει. Συνεχίζει να δουλεύει με τα δεδομένα που έχει ήδη στη μνήμη, εκτός αν ξανακάνεις query.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Παράδειγμα:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;var user = context.Users.First();&lt;/code&gt; // φορτώνεται στη μνήμη&lt;/p&gt;

&lt;p&gt;Αν στο μεταξύ αλλάξει η ίδια εγγραφή στη βάση από αλλού, το user που έχεις στη μνήμη παραμένει όπως ήταν. Το EF δεν κάνει αυτόματη ανανέωση.&lt;/p&gt;

&lt;p&gt;Με απλά λόγια:&lt;br&gt;
Το EF Core δουλεύει με ένα snapshot της κατάστασης των entities που έχει φορτώσει στη μνήμη, όχι με live σύνδεση προς τη βάση δεδομένων. Το Change Tracker γνωρίζει μόνο την κατάσταση των entities που έχουν φορτωθεί μέσα στο συγκεκριμένο DbContext instance.&lt;/p&gt;

&lt;p&gt;Το tracking υπάρχει μόνο όσο ζει το συγκεκριμένο DbContext. Όταν το DbContext γίνει disposed, χάνεται όλο το tracking state και τα entities παύουν να παρακολουθούνται.&lt;/p&gt;

&lt;p&gt;Γι’ αυτό το EF Core αντιμετωπίζει το DbContext ως unit-of-work boundary και όχι ως long-lived cache μηχανισμό.&lt;/p&gt;



&lt;p&gt;&lt;strong&gt;Τι κάνει το AsTracking();&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Στην πραγματικότητα, το AsTracking() δεν αλλάζει τη συμπεριφορά του EF Core, γιατί το tracking είναι ήδη το default behavior για queries που επιστρέφουν entities.&lt;/p&gt;

&lt;p&gt;Συνήθως χρησιμοποιείται για σαφήνεια ή όταν έχει προηγηθεί global/default απενεργοποίηση tracking.&lt;/p&gt;

&lt;p&gt;Το AsTracking() ενεργοποιεί ρητά το tracking σε ένα query. Στην πράξη, επιβεβαιώνει το default behavior και χρησιμοποιείται κυρίως για σαφήνεια ή όταν έχει προηγηθεί απενεργοποίηση tracking.&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;users&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Users&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AsTracking&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;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;0&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="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SaveChanges&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Σε αυτό το παράδειγμα:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Το entity αποθηκεύεται στο Change Tracker του DbContext&lt;/li&gt;
&lt;li&gt;Το EF Core κρατά πληροφορίες για την αρχική και την τρέχουσα κατάστασή του&lt;/li&gt;
&lt;li&gt;Εντοπίζει ότι άλλαξε το Name&lt;/li&gt;
&lt;li&gt;Στο SaveChanges() δημιουργεί το αντίστοιχο SQL UPDATE&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Τι κάνει το AsNoTracking();&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Το &lt;strong&gt;AsNoTracking()&lt;/strong&gt; απενεργοποιεί τον μηχανισμό παρακολούθησης (change tracking) του Entity Framework Core για τα entities που επιστρέφει ένα query. Αυτό σημαίνει ότι τα αντικείμενα που θα φορτωθούν από τη βάση δεδομένων δεν αποθηκεύονται στο Change Tracker του DbContext και το EF δεν κρατάει καμία πληροφορία για την αρχική τους κατάσταση.&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;users&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Users&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;ToList&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;0&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="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SaveChanges&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Σε αυτό το παράδειγμα, το EF φέρνει κανονικά τα δεδομένα από τη βάση και δημιουργεί τα αντίστοιχα objects στη μνήμη. Όταν αλλάζεις την τιμή του Name, η αλλαγή γίνεται μόνο μέσα στο object στη μνήμη δηλαδή &lt;strong&gt;στο C# instance και όχι στη βάση δεδομένων&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Το EF Core δεν παρακολουθεί το entity, άρα δεν κρατά original values ούτε πληροφορία για αλλαγές κατάστασης.&lt;/p&gt;

&lt;p&gt;Όταν εκτελεστεί το SaveChanges(), το EF Core δεν βρίσκει modified tracked entities, οπότε δεν δημιουργεί κανένα UPDATE statement.&lt;/p&gt;

&lt;p&gt;Με απλά λόγια: η αλλαγή έγινε στη μνήμη, αλλά το EF δεν την “είδε” ποτέ, άρα δεν μπορεί να τη μεταφέρει στη βάση δεδομένων.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Detached entities&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Ένα entity που φορτώθηκε με AsNoTracking() θεωρείται detached από το DbContext.&lt;/p&gt;

&lt;p&gt;Αυτό σημαίνει ότι το EF Core δεν το παρακολουθεί πλέον και δεν μπορεί να εντοπίσει αλλαγές πάνω του αυτόματα.&lt;/p&gt;

&lt;p&gt;Αν αργότερα θελήσεις να κάνεις update αυτό το entity, πρέπει είτε:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;να το κάνεις Attach(),&lt;/li&gt;
&lt;li&gt;είτε να το ξαναφορτώσεις μέσω tracking query.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Το πρόβλημα που λύνει το Identity Resolution&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Όταν χρησιμοποιούμε &lt;strong&gt;AsNoTracking()&lt;/strong&gt;, χάνουμε ένα σημαντικό χαρακτηριστικό του EF:&lt;/p&gt;

&lt;p&gt;Το ίδιο record μπορεί να εμφανιστεί ως διαφορετικά objects στη μνήμη&lt;/p&gt;

&lt;p&gt;Αυτό συμβαίνει κυρίως σε queries με joins ή includes.&lt;/p&gt;

&lt;p&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="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="n"&gt;context&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;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;Include&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;o&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;ToList&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Αν ένας πελάτης έχει πολλά orders:&lt;/p&gt;

&lt;p&gt;Το ίδιο Customer μπορεί να δημιουργηθεί πολλές φορές&lt;br&gt;
Κάθε order έχει διαφορετικό instance του ίδιου customer&lt;/p&gt;



&lt;p&gt;&lt;strong&gt;Τι κάνει το AsNoTrackingWithIdentityResolution();&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Το &lt;strong&gt;AsNoTrackingWithIdentityResolution()&lt;/strong&gt; είναι ένα ενδιάμεσο mode:&lt;/p&gt;

&lt;p&gt;Δεν κάνει tracking (άρα είναι πιο ελαφρύ από AsTracking())&lt;br&gt;
ΑΛΛΑ διατηρεί identity resolution&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="n"&gt;context&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;AsNoTrackingWithIdentityResolution&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;o&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;o&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;ToList&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Τι αλλάζει εδώ;&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Αν ο ίδιος Customer εμφανίζεται σε 10 orders:
Θα υπάρχει ένα και μόνο instance στη μνήμη&lt;/li&gt;
&lt;li&gt;Το EF κρατάει έναν προσωρινό μηχανισμό για να αποφύγει duplicates&lt;/li&gt;
&lt;li&gt;Δεν κρατάει όμως πλήρες tracking state&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Είναι σημαντικό να ξεχωρίσουμε ότι το tracking και το identity resolution δεν είναι το ίδιο πράγμα.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tracking σημαίνει ότι το EF Core παρακολουθεί αλλαγές στα entities για το SaveChanges().&lt;/li&gt;
&lt;li&gt;Identity Resolution σημαίνει ότι το EF Core επαναχρησιμοποιεί το ίδιο object instance όταν το ίδιο entity εμφανίζεται πολλές φορές στο ίδιο query result.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Το AsTracking() παρέχει και τα δύο:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;tracking&lt;/li&gt;
&lt;li&gt;identity resolution&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Το AsNoTracking() δεν παρέχει κανένα από τα δύο.&lt;/p&gt;

&lt;p&gt;Το AsNoTrackingWithIdentityResolution() παρέχει μόνο identity resolution χωρίς change tracking.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Τι είναι το Identity Resolution&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Το Identity Resolution εξασφαλίζει ότι αν το ίδιο entity εμφανιστεί πολλές φορές μέσα στο ίδιο query result, το EF Core θα χρησιμοποιήσει το ίδιο object instance στη μνήμη αντί να δημιουργήσει duplicates.&lt;/p&gt;

&lt;p&gt;Αυτό είναι ιδιαίτερα σημαντικό σε queries με Include ή joins, όπου το ίδιο entity μπορεί να εμφανιστεί πολλές φορές μέσα στο result set.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Database
    │
    ▼
 DbContext
    │
    ▼
Change Tracker
    │
 ┌───────────────┬────────────────────┐
 │               │                    │
AsTracking   AsNoTracking   AsNoTrackingWithIdentityResolution
 │               │                    │
tracking       no tracking        identity resolution only
identity res   no identity res    no change tracking
SaveChanges ✔  SaveChanges ✘      SaveChanges ✘

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

&lt;/div&gt;






&lt;p&gt;&lt;strong&gt;Πρακτικό Παράδειγμα&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Χωρίς Identity Resolution&lt;/strong&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="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="n"&gt;context&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;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;Include&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;o&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;ToList&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;sameCustomer&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;ReferenceEquals&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="m"&gt;0&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;orders&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;1&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;sameCustomer = false&lt;/code&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Με Identity Resolution&lt;/strong&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="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="n"&gt;context&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;AsNoTrackingWithIdentityResolution&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;o&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;o&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;ToList&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;sameCustomer&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;ReferenceEquals&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="m"&gt;0&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;orders&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;1&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;sameCustomer = true&lt;/code&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Πότε έχει νόημα να το χρησιμοποιήσεις;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Το &lt;strong&gt;AsNoTrackingWithIdentityResolution()&lt;/strong&gt; έχει νόημα όταν:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;το ίδιο entity μπορεί να εμφανιστεί πολλές φορές μέσα στο ίδιο result set&lt;/li&gt;
&lt;li&gt;χρησιμοποιείς Include ή joins&lt;/li&gt;
&lt;li&gt;φορτώνεις nested relationships&lt;/li&gt;
&lt;li&gt;δεν χρειάζεσαι SaveChanges()&lt;/li&gt;
&lt;li&gt;αλλά θέλεις να αποφύγεις duplicate object instances στη μνήμη&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Πότε να το αποφύγεις&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Μην το χρησιμοποιείς όταν:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Κάνεις απλά flat queries&lt;/li&gt;
&lt;li&gt;Δεν έχεις relationships&lt;/li&gt;
&lt;li&gt;Δεν σε νοιάζουν duplicate instances&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Σε αυτές τις περιπτώσεις:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;AsNoTracking()&lt;/code&gt; είναι αρκετό και πιο γρήγορο&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Συχνό λάθος σε senior επίπεδο&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Πολλοί developers χρησιμοποιούν AsNoTracking() παντού για performance, αλλά:&lt;/p&gt;

&lt;p&gt;Σε complex graphs μπορεί να δημιουργήσεις:&lt;/p&gt;

&lt;p&gt;duplicate objects&lt;br&gt;
bugs σε reference comparisons&lt;br&gt;
περίεργη συμπεριφορά σε mapping&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Στην πράξη, ένα συνηθισμένο guideline είναι:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CRUD/update scenarios → AsTracking()&lt;/li&gt;
&lt;li&gt;Read-only simple queries → AsNoTracking()&lt;/li&gt;
&lt;li&gt;Read-only queries με Includes ή repeated entities → AsNoTrackingWithIdentityResolution()&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Η σωστή επιλογή εξαρτάται πάντα από:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;το shape των δεδομένων,&lt;/li&gt;
&lt;li&gt;το query size,&lt;/li&gt;
&lt;li&gt;τα relationships,&lt;/li&gt;
&lt;li&gt;και το αν χρειάζεται persistence μέσω SaveChanges().&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Γιατί το AsNoTracking() είναι πιο αποδοτικό&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Σημαντικό: όταν χρησιμοποιείς projections (Select σε DTOs ή anonymous types), το tracking συνήθως δεν έχει νόημα, γιατί τα αποτελέσματα δεν είναι tracked entity instances.&lt;/p&gt;

&lt;p&gt;Το AsNoTracking() είναι συνήθως πιο αποδοτικό σε read-only queries, γιατί το EF Core δεν χρειάζεται να αποθηκεύσει τα entities στο Change Tracker.&lt;/p&gt;

&lt;p&gt;Αυτό σημαίνει λιγότερο overhead σε:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;memory usage&lt;/li&gt;
&lt;li&gt;change detection&lt;/li&gt;
&lt;li&gt;object state management&lt;/li&gt;
&lt;li&gt;DbContext internal bookkeeping&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Το όφελος γίνεται πιο εμφανές όταν το query επιστρέφει πολλά rows ή όταν το DbContext εκτελεί πολλά read-only queries.&lt;/p&gt;

&lt;p&gt;Το σημαντικότερο είναι να καταλάβεις ότι το tracking δεν είναι “καλό” ή “κακό”. Είναι μηχανισμός με συγκεκριμένο κόστος και συγκεκριμένο σκοπό.&lt;/p&gt;

&lt;p&gt;Το σωστό ερώτημα δεν είναι:&lt;br&gt;
“να βάλω AsNoTracking παντού;”&lt;/p&gt;

&lt;p&gt;Αλλά:&lt;br&gt;
“χρειάζομαι πραγματικά change tracking σε αυτό το query;”&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Συμπέρασμα&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Το &lt;strong&gt;AsNoTrackingWithIdentityResolution()&lt;/strong&gt; είναι ένα advanced εργαλείο που γεφυρώνει το κενό ανάμεσα σε performance και συνέπεια δεδομένων στη μνήμη.&lt;/p&gt;

&lt;p&gt;Δεν είναι τόσο γνωστό όσο τα άλλα δύο modes, αλλά σε πραγματικά production συστήματα μπορεί να κάνει τεράστια διαφορά, ειδικά όταν δουλεύεις με σύνθετα object graphs.&lt;/p&gt;

&lt;p&gt;Ένας έμπειρος developer δεν επιλέγει απλά tracking ή όχι. Καταλαβαίνει το shape των δεδομένων του και επιλέγει το κατάλληλο εργαλείο για το συγκεκριμένο πρόβλημα.&lt;/p&gt;

&lt;p&gt;Και αυτό είναι που ξεχωρίζει τον καλό κώδικα από τον production-grade κώδικα.&lt;/p&gt;

&lt;p&gt;Το σωστό mode δεν επιλέγεται με βάση το “τι είναι πιο γρήγορο”, αλλά με βάση το τι lifecycle και behavior χρειάζονται πραγματικά τα δεδομένα σου.&lt;/p&gt;




&lt;p&gt;&lt;a href="mailto:nikosstit@gmail.com"&gt;nikosstit@gmail.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>database</category>
      <category>dotnet</category>
      <category>performance</category>
    </item>
    <item>
      <title>Specification Pattern υπό το πρίσμα του SOLID και της Clean Architecture</title>
      <dc:creator>nikosst</dc:creator>
      <pubDate>Wed, 25 Mar 2026 18:24:21 +0000</pubDate>
      <link>https://forem.com/__b63657/specification-pattern-upo-to-prisma-tou-solid-kai-tes-clean-architecture-8oh</link>
      <guid>https://forem.com/__b63657/specification-pattern-upo-to-prisma-tou-solid-kai-tes-clean-architecture-8oh</guid>
      <description>&lt;p&gt;Η εξέλιξη της μηχανικής λογισμικού τις τελευταίες δεκαετίες έχει καταδείξει ότι το βασικό πρόβλημα δεν είναι η υλοποίηση μιας λύσης, αλλά η διατήρησή της στο χρόνο. Συστήματα που αρχικά φαίνονται απλά, καταλήγουν να γίνονται εύθραυστα καθώς αυξάνεται η πολυπλοκότητα και οι απαιτήσεις μεταβάλλονται. Σε αυτό το πλαίσιο, αρχές όπως το SOLID και προσεγγίσεις όπως η Clean Architecture δεν αποτελούν θεωρητικές πολυτέλειες, αλλά θεμέλια για βιώσιμο λογισμικό.&lt;/p&gt;

&lt;p&gt;Η χρήση design patterns εντάσσεται ακριβώς σε αυτή τη φιλοσοφία. Τα patterns δεν είναι έτοιμες λύσεις προς μηχανική εφαρμογή, αλλά αφαιρετικά εργαλεία που ενσωματώνουν δοκιμασμένες αρχές σχεδίασης. Ένα από τα patterns που συνδέονται άμεσα με τις αρχές του SOLID και ενσωματώνονται φυσικά σε μια Clean Architecture προσέγγιση είναι το &lt;strong&gt;Specification Pattern&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Η &lt;strong&gt;βασική ιδέα&lt;/strong&gt; του &lt;strong&gt;Specification Pattern&lt;/strong&gt; είναι η απομόνωση της επιχειρησιακής λογικής που αφορά &lt;strong&gt;κανόνες&lt;/strong&gt; και &lt;strong&gt;φίλτρα&lt;/strong&gt; σε ανεξάρτητα, συνθέσιμα αντικείμενα. Στο πλαίσιο της Clean Architecture, αυτή η λογική ανήκει στον πυρήνα του domain και δεν πρέπει να εξαρτάται από εξωτερικές υποδομές, όπως βάσεις δεδομένων ή frameworks. Με άλλα λόγια, οι προδιαγραφές αποτελούν μέρος της “καρδιάς” του συστήματος.&lt;/p&gt;

&lt;p&gt;Αν εξετάσουμε το πρόβλημα χωρίς τη χρήση του pattern, συχνά παρατηρούμε repositories ή services να περιέχουν πολύπλοκα φίλτρα, ενσωματωμένα είτε σε queries είτε σε αλγοριθμική λογική. Αυτό οδηγεί σε παραβίαση του Single Responsibility Principle, καθώς οι ίδιες κλάσεις αναλαμβάνουν τόσο την πρόσβαση στα δεδομένα όσο και την επιχειρησιακή λογική των φίλτρων. Επιπλέον, κάθε νέα απαίτηση οδηγεί σε τροποποίηση υπαρχόντων μεθόδων, παραβιάζοντας το Open/Closed Principle.&lt;/p&gt;

&lt;p&gt;Το Specification Pattern επαναφέρει τη δομή. Ξεκινάμε από έναν αφηρημένο ορισμό:&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;ISpecification&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;IsSatisfiedBy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="n"&gt;entity&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;strong&gt;Clean Architecture&lt;/strong&gt;, όπου τα domain components είναι ανεξάρτητα από εξωτερικές ανησυχίες.&lt;/p&gt;

&lt;p&gt;Ας θεωρήσουμε ένα domain μοντέλο Product:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;public class Product&lt;br&gt;
{&lt;br&gt;
    public decimal Price { get; set; }&lt;br&gt;
    public bool IsActive { get; set; }&lt;br&gt;
}&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Αντί να ενσωματώσουμε τη λογική φίλτρων σε repositories, δημιουργούμε ανεξάρτητες προδιαγραφές:&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;ActiveProductSpecification&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ISpecification&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Product&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="kt"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;IsSatisfiedBy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Product&lt;/span&gt; &lt;span class="n"&gt;product&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;product&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="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;PriceSpecification&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ISpecification&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Product&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;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="kt"&gt;decimal&lt;/span&gt; &lt;span class="n"&gt;_maxPrice&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;PriceSpecification&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;maxPrice&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_maxPrice&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;maxPrice&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;bool&lt;/span&gt; &lt;span class="nf"&gt;IsSatisfiedBy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Product&lt;/span&gt; &lt;span class="n"&gt;product&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;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;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;_maxPrice&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;Σε αυτό το σημείο, είναι εμφανής η εφαρμογή του Single Responsibility Principle. Κάθε κλάση εκφράζει έναν και μόνο κανόνα. Παράλληλα, η προσθήκη νέων κανόνων δεν απαιτεί τροποποίηση των υπαρχόντων, αλλά μόνο επέκταση του συστήματος με νέες υλοποιήσεις, ικανοποιώντας το Open/Closed Principle.&lt;/p&gt;

&lt;p&gt;Η πραγματική δύναμη του pattern, ωστόσο, αναδεικνύεται μέσω της σύνθεσης. Σε ένα σύστημα που ακολουθεί Clean Architecture, η σύνθεση της επιχειρησιακής λογικής είναι κρίσιμη για την αποφυγή επανάληψης και τη διατήρηση καθαρών ορίων μεταξύ των layers.&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;AndSpecification&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&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;ISpecification&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&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;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;ISpecification&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_left&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;ISpecification&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_right&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;AndSpecification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ISpecification&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ISpecification&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_left&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;_right&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;right&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;bool&lt;/span&gt; &lt;span class="nf"&gt;IsSatisfiedBy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="n"&gt;entity&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;_left&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsSatisfiedBy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;_right&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsSatisfiedBy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;entity&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;Η παραπάνω υλοποίηση εισάγει μια σημαντική ιδιότητα, τη δυνατότητα σύνθεσης συμπεριφορών χωρίς τροποποίηση υπαρχόντων κλάσεων. Αυτό συνδέεται άμεσα με το Liskov Substitution Principle, καθώς κάθε σύνθετη προδιαγραφή μπορεί να χρησιμοποιηθεί όπου αναμένεται μια βασική ISpecification, και με το Dependency Inversion Principle, αφού η εξάρτηση γίνεται από αφαιρέσεις και όχι από συγκεκριμένες υλοποιήσεις.&lt;/p&gt;

&lt;p&gt;Σε επίπεδο εφαρμογής, η χρήση των specifications επιτρέπει την αποσύνδεση του domain από το infrastructure. Ένα repository μπορεί να δεχθεί μια ISpecification ως παράμετρο, χωρίς να γνωρίζει τις λεπτομέρειες της:&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="n"&gt;IEnumerable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;GetProducts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ISpecification&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;specification&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;_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;p&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;specification&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsSatisfiedBy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&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;Με αυτόν τον τρόπο, το repository παραμένει απλό και επικεντρωμένο στην ευθύνη του, ενώ η επιχειρησιακή λογική μεταφέρεται πλήρως στο domain layer. Σε πιο εξελιγμένες υλοποιήσεις, η ISpecification μπορεί να επεκταθεί ώστε να εκφράζει και expression trees, επιτρέποντας τη μεταφορά της ίδιας λογικής σε επίπεδο βάσης δεδομένων, χωρίς παραβίαση των αρχών της αρχιτεκτονικής.&lt;/p&gt;

&lt;p&gt;Αξίζει να σημειωθεί ότι το Specification Pattern ενισχύει και τη δοκιμασιμότητα του συστήματος. Καθώς κάθε κανόνας είναι απομονωμένος, μπορεί να ελεγχθεί ανεξάρτητα με unit tests, χωρίς την ανάγκη για mocking πολύπλοκων εξαρτήσεων. Αυτό συνάδει με τη φιλοσοφία της Clean Architecture, όπου ο πυρήνας του συστήματος πρέπει να είναι πλήρως ελέγξιμος.&lt;/p&gt;

&lt;p&gt;Ωστόσο, όπως κάθε αφαιρετική τεχνική, απαιτεί μέτρο. Σε απλά σενάρια, η εισαγωγή πολλών specifications μπορεί να οδηγήσει σε περιττή πολυπλοκότητα. Η αξία του pattern αναδεικνύεται σε συστήματα με πλούσια domain λογική, όπου οι κανόνες μεταβάλλονται συχνά και απαιτείται υψηλός βαθμός επαναχρησιμοποίησης.&lt;/p&gt;

&lt;p&gt;Συνοψίζοντας, το Specification Pattern δεν είναι απλώς ένας τρόπος να γράφουμε φίλτρα. Είναι μια αρχιτεκτονική επιλογή που εναρμονίζεται με τις αρχές του SOLID και ενσωματώνεται οργανικά στη Clean Architecture. Επιτρέπει τη σαφή οριοθέτηση της επιχειρησιακής λογικής, ενισχύει την επεκτασιμότητα και καθιστά το σύστημα πιο ανθεκτικό στις αλλαγές. Σε ένα περιβάλλον όπου η πολυπλοκότητα είναι αναπόφευκτη, τέτοιες προσεγγίσεις αποτελούν βασικά εργαλεία για τη δημιουργία ποιοτικού και διαχρονικού λογισμικού.&lt;/p&gt;

&lt;p&gt;&lt;a href="mailto:nikosstit@gmail.com"&gt;nikosstit@gmail.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>programming</category>
      <category>softwaredevelopment</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>Asynchronous Programming στην C#: Θεμελιώδεις Αρχές, Κανόνες και Βαθιά Κατανόηση</title>
      <dc:creator>nikosst</dc:creator>
      <pubDate>Mon, 23 Mar 2026 23:36:40 +0000</pubDate>
      <link>https://forem.com/__b63657/asynchronous-programming-sten-c-themeliodeis-arkhes-kanones-kai-bathia-katanoese-2nd5</link>
      <guid>https://forem.com/__b63657/asynchronous-programming-sten-c-themeliodeis-arkhes-kanones-kai-bathia-katanoese-2nd5</guid>
      <description>&lt;p&gt;&lt;strong&gt;Εισαγωγή&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Ο ασύγχρονος προγραμματισμός στην C# αποτελεί ένα από τα πιο ισχυρά εργαλεία για την ανάπτυξη σύγχρονων εφαρμογών, ιδιαίτερα σε περιβάλλοντα όπου η απόδοση, η κλιμάκωση και η αποδοτική χρήση των πόρων είναι κρίσιμες απαιτήσεις. Παρ’ όλα αυτά, η ευκολία με την οποία εισάγεται το async και το await στη σύνταξη της γλώσσας δημιουργεί συχνά μια ψευδαίσθηση απλότητας. Πολλοί προγραμματιστές χρησιμοποιούν τα εργαλεία αυτά χωρίς να έχουν κατανοήσει πλήρως τη λειτουργία τους, με αποτέλεσμα να εισάγουν σφάλματα που είναι δύσκολο να εντοπιστούν και ακόμη πιο δύσκολο να διορθωθούν.&lt;/p&gt;

&lt;p&gt;Στην πραγματικότητα, το asynchronous programming δεν είναι απλώς ένα διαφορετικό στυλ γραφής κώδικα, αλλά μια διαφορετική φιλοσοφία εκτέλεσης. Δεν στοχεύει απαραίτητα στο να κάνει τον κώδικα πιο γρήγορο με την έννοια της μείωσης του χρόνου εκτέλεσης, αλλά στο να επιτρέπει στο σύστημα να εκμεταλλεύεται καλύτερα τους διαθέσιμους πόρους του, αποφεύγοντας την άσκοπη δέσμευση νημάτων (threads). Η κατανόηση αυτής της διάκρισης είναι θεμελιώδης για την ορθή χρήση των μηχανισμών async/await.&lt;/p&gt;

&lt;p&gt;Στο παρόν κείμενο θα αναλυθούν δέκα βασικοί κανόνες, οι οποίοι έχουν προκύψει μέσα από πραγματική εμπειρία ανάπτυξης λογισμικού σε παραγωγικά συστήματα. Για κάθε κανόνα θα παρουσιαστεί ένα αντιπαράδειγμα, η ορθή προσέγγιση και, κυρίως, η ερμηνεία του γιατί η σωστή πρακτική είναι αναγκαία.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;1. Αποφυγή μπλοκαρίσματος ασύγχρονου κώδικα&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Ένα από τα πιο συνηθισμένα λάθη είναι η χρήση των ιδιοτήτων .Result ή .Wait() σε ασύγχρονες μεθόδους. Εξετάζοντας το παρακάτω παράδειγμα:&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;string&lt;/span&gt; &lt;span class="nf"&gt;GetData&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="nf"&gt;GetDataAsync&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;Result&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;παρατηρούμε ότι η μέθοδος GetData καλεί μια ασύγχρονη λειτουργία, αλλά επιλέγει να περιμένει συγχρονισμένα το αποτέλεσμά της. Το πρόβλημα δεν είναι απλώς αισθητικό· αφορά τον ίδιο τον τρόπο λειτουργίας του runtime. Όταν η GetDataAsync φτάσει σε ένα await, προσπαθεί να συνεχίσει την εκτέλεσή της στο ίδιο thread. Ωστόσο, το thread αυτό είναι ήδη δεσμευμένο από την αναμονή της .Result. Δημιουργείται έτσι μια κατάσταση αδιεξόδου (deadlock), όπου καμία από τις δύο πλευρές δεν μπορεί να προχωρήσει.&lt;/p&gt;

&lt;p&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;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;string&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;GetData&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="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;GetDataAsync&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;Η σημασία αυτού του κανόνα είναι ιδιαίτερα εμφανής σε περιβάλλοντα όπως το ASP.NET, όπου ένα μπλοκαρισμένο thread μπορεί να οδηγήσει σε εξάντληση των διαθέσιμων πόρων και, τελικά, σε μη ανταποκρινόμενες εφαρμογές.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;2. Αποφυγή της χρήσης async void&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Η χρήση της επιστροφής async void πρέπει να αποφεύγεται σχεδόν καθολικά. Ένα παράδειγμα:&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;async&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="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;SaveToDatabase&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;μπορεί να φαίνεται ακίνδυνο, αλλά κρύβει σοβαρούς κινδύνους. Μια μέθοδος που επιστρέφει void δεν επιτρέπει στον καλούντα να περιμένει την ολοκλήρωσή της ούτε να διαχειριστεί εξαιρέσεις που μπορεί να προκύψουν. Σε περίπτωση αποτυχίας, η εξαίρεση δεν μεταφέρεται με ελεγχόμενο τρόπο, αλλά διαχέεται στο runtime.&lt;/p&gt;

&lt;p&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;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;Save&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="nf"&gt;SaveToDatabase&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;Η επιστροφή Task λειτουργεί ως συμβόλαιο που επιτρέπει στον καλούντα να ελέγξει τη ροή εκτέλεσης και να διαχειριστεί πιθανά σφάλματα.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;3. Εκτέλεση ανεξάρτητων εργασιών παράλληλα&lt;/strong&gt;&lt;/p&gt;

&lt;p&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="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;GetUser&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;orders&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;GetOrders&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&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="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;userTask&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;GetUser&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;ordersTask&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;GetOrders&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;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WhenAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userTask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ordersTask&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Με αυτόν τον τρόπο, το συνολικό χρονικό κόστος μειώνεται σημαντικά. Η διαφορά αυτή γίνεται κρίσιμη σε εφαρμογές που εκτελούν πολλαπλά εξωτερικά αιτήματα, όπως API calls ή database queries.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;4. Αποφυγή χρήσης Task.Run για I/O εργασίες&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Η χρήση του Task.Run για την εκτέλεση ασύγχρονων λειτουργιών εισόδου/εξόδου αποτελεί μια παρανόηση της φύσης του async programming. Για παράδειγμα:&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;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ReadAllTextAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"file.txt"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;επιβαρύνει το σύστημα δημιουργώντας ένα νέο thread για μια εργασία που ήδη είναι μη μπλοκαριστική. Οι I/O λειτουργίες δεν απαιτούν dedicated thread κατά την αναμονή τους, καθώς βασίζονται σε μηχανισμούς του λειτουργικού συστήματος.&lt;/p&gt;

&lt;p&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;await&lt;/span&gt; &lt;span class="n"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ReadAllTextAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"file.txt"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Η αποφυγή άσκοπης δημιουργίας threads συμβάλλει στην αποδοτικότητα και στη σταθερότητα του συστήματος.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;5. Χρήση του ConfigureAwait(false) σε βιβλιοθήκες&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Η μέθοδος ConfigureAwait(false) επηρεάζει τον τρόπο με τον οποίο συνεχίζεται η εκτέλεση μετά από ένα await. Συγκεκριμένα, αποτρέπει την επιστροφή στο αρχικό synchronization context. Σε βιβλιοθήκες, όπου δεν υπάρχει ανάγκη επιστροφής σε συγκεκριμένο thread, η χρήση του:&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="nf"&gt;SomeOperation&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;ConfigureAwait&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;βελτιώνει την απόδοση και μειώνει την πιθανότητα deadlocks. Η κατανόηση αυτού του μηχανισμού είναι κρίσιμη για την ανάπτυξη επαναχρησιμοποιήσιμων και αποδοτικών components.&lt;/p&gt;

&lt;p&gt;Η μέθοδος ConfigureAwait(false) επηρεάζει άμεσα τον τρόπο με τον οποίο συνεχίζεται η εκτέλεση μιας ασύγχρονης μεθόδου μετά από ένα await. Για να κατανοηθεί πλήρως η σημασία της, χρειάζεται πρώτα να δούμε τι συμβαίνει “πίσω από τα φώτα” όταν χρησιμοποιούμε await. Από προεπιλογή, κάθε await καταγράφει το τρέχον Synchronization Context (δηλαδή το περιβάλλον εκτέλεσης, όπως το UI thread ή το request context) και προσπαθεί να επαναφέρει την εκτέλεση σε αυτό μόλις ολοκληρωθεί η ασύγχρονη εργασία. Αυτή η συμπεριφορά είναι ιδιαίτερα χρήσιμη σε εφαρμογές με γραφικό περιβάλλον ή σε περιβάλλοντα όπου η συνέχεια της εκτέλεσης πρέπει να γίνει σε συγκεκριμένο thread.&lt;/p&gt;

&lt;p&gt;Ωστόσο, αυτή η “επιστροφή στο αρχικό context” δεν είναι πάντα απαραίτητη. Σε περιπτώσεις όπως οι βιβλιοθήκες (libraries) ή τα backend components, όπου δεν υπάρχει εξάρτηση από συγκεκριμένο thread ή περιβάλλον εκτέλεσης, η διατήρηση του context προσθέτει περιττό κόστος. Συγκεκριμένα, απαιτείται επιπλέον μηχανισμός scheduling για να επανέλθει η εκτέλεση στο αρχικό thread, κάτι που μπορεί να επηρεάσει αρνητικά την απόδοση, ιδιαίτερα σε σενάρια υψηλής κλιμάκωσης.&lt;/p&gt;

&lt;p&gt;Με τη χρήση του ConfigureAwait(false), όπως στο παράδειγμα await SomeOperation().ConfigureAwait(false);, δηλώνουμε ρητά ότι δεν μας ενδιαφέρει η επιστροφή στο αρχικό synchronization context. Αυτό επιτρέπει στο runtime να συνεχίσει την εκτέλεση σε οποιοδήποτε διαθέσιμο thread, συνήθως από το thread pool, μειώνοντας έτσι το overhead και βελτιώνοντας τη συνολική αποδοτικότητα της εφαρμογής.&lt;/p&gt;

&lt;p&gt;Επιπλέον, η χρήση του ConfigureAwait(false) συμβάλλει στην αποφυγή πιθανών deadlocks. Σε περιβάλλοντα όπου γίνεται συγχρονισμένη αναμονή (π.χ. με .Result ή .Wait()), μπορεί να προκύψει κατάσταση όπου το thread που περιμένει την ολοκλήρωση μιας ασύγχρονης μεθόδου είναι το ίδιο που απαιτείται για να συνεχιστεί η εκτέλεση μετά το await. Αν η συνέχεια προσπαθεί να επιστρέψει σε αυτό το δεσμευμένο thread, δημιουργείται αδιέξοδο. Με το ConfigureAwait(false), η συνέχεια δεν εξαρτάται από το αρχικό context, με αποτέλεσμα να αποφεύγεται αυτό το σενάριο.&lt;/p&gt;

&lt;p&gt;Ένα απλό αλλά χαρακτηριστικό παράδειγμα μπορεί να αναδείξει τη διαφορά. Ας υποθέσουμε ότι έχουμε μια μέθοδο σε μια βιβλιοθήκη που καλεί ένα εξωτερικό API:&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;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;string&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;GetDataAsync&lt;/span&gt;&lt;span class="p"&gt;()&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;response&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;httpClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetStringAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"https://api.example.com/data"&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;response&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;Σε αυτή τη μορφή, μετά το await, η εκτέλεση θα προσπαθήσει να επιστρέψει στο αρχικό context. Αν αυτή η μέθοδος κληθεί από ένα UI thread ή από κάποιο περιβάλλον με synchronization context, τότε δεσμεύεται άσκοπα σε αυτό.&lt;/p&gt;

&lt;p&gt;Η βελτιωμένη εκδοχή για χρήση σε library είναι:&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;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;string&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;GetDataAsync&lt;/span&gt;&lt;span class="p"&gt;()&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;response&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;httpClient&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetStringAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"https://api.example.com/data"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ConfigureAwait&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;return&lt;/span&gt; &lt;span class="n"&gt;response&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;Εδώ, η μέθοδος δεν ενδιαφέρεται για το πού θα συνεχιστεί η εκτέλεση, καθώς απλώς επιστρέφει δεδομένα χωρίς να αλληλεπιδρά με UI ή context-specific στοιχεία. Αυτό την καθιστά πιο αποδοτική και ασφαλή για επαναχρησιμοποίηση.&lt;/p&gt;

&lt;p&gt;Αντίθετα, σε ένα UI παράδειγμα:&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;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;LoadDataAsync&lt;/span&gt;&lt;span class="p"&gt;()&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;data&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;GetDataAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;myLabel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Text&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// χρειάζεται UI thread&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Σε αυτή την περίπτωση, δεν πρέπει να χρησιμοποιηθεί ConfigureAwait(false) μέσα στη μέθοδο που ενημερώνει το UI, γιατί η συνέχεια πρέπει να εκτελεστεί στο UI thread.&lt;/p&gt;

&lt;p&gt;Το βασικό συμπέρασμα είναι ότι το ConfigureAwait(false) ανήκει κυρίως σε επίπεδο βιβλιοθηκών και υποδομής (infrastructure code), όπου δεν υπάρχει ανάγκη επιστροφής σε συγκεκριμένο context. Με αυτόν τον τρόπο, ο κώδικας γίνεται πιο αποδοτικός, πιο ασφαλής ως προς deadlocks και πιο κατάλληλος για χρήση σε διαφορετικά περιβάλλοντα.&lt;/p&gt;

&lt;p&gt;Συνοψίζοντας, η κατανόηση και η σωστή χρήση του ConfigureAwait(false) είναι κρίσιμη για την ανάπτυξη αποδοτικών και επαναχρησιμοποιήσιμων components. Επιτρέπει καλύτερο έλεγχο της εκτέλεσης, μειώνει το περιττό κόστος διαχείρισης του context και προστατεύει από δύσκολα εντοπίσιμα προβλήματα συγχρονισμού, καθιστώντας τον κώδικα πιο αξιόπιστο και scalable.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;6. Διατήρηση της ασύγχρονης ροής σε όλη την αλυσίδα&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Η ανάμιξη συγχρονικού και ασύγχρονου κώδικα οδηγεί συχνά σε προβλήματα. Όταν μια ασύγχρονη μέθοδος καλείται από συγχρονική, η ανάγκη για αναμονή δημιουργεί πίεση προς τη χρήση .Result ή .Wait(), με τα προβλήματα που ήδη αναφέρθηκαν. Η σωστή πρακτική είναι η διατήρηση της ασύγχρονης φύσης σε όλη την αλυσίδα κλήσεων.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;7. Ορθή διαχείριση εξαιρέσεων&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Η εκτέλεση μιας ασύγχρονης μεθόδου χωρίς await οδηγεί σε μη παρατηρήσιμες εξαιρέσεις:&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="nf"&gt;DoWorkAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Σε αυτή την περίπτωση, αν η μέθοδος αποτύχει, η εξαίρεση δεν θα διαχειριστεί στο σημείο που αναμένεται. Αντίθετα, η χρήση του await εξασφαλίζει ότι η εξαίρεση θα προκύψει στο σωστό σημείο της ροής και θα μπορεί να αντιμετωπιστεί κατάλληλα.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;8. Αποφυγή άσκοπης χρήσης του async&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Η δήλωση μιας μεθόδου ως async χωρίς την ύπαρξη await προκαλεί περιττό overhead. Ο compiler δημιουργεί έναν state machine που δεν είναι απαραίτητος. Σε τέτοιες περιπτώσεις, η επιστροφή ενός ήδη ολοκληρωμένου Task είναι προτιμότερη.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;9. Υποστήριξη ακύρωσης μέσω CancellationToken&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Η δυνατότητα ακύρωσης είναι κρίσιμη σε πραγματικά συστήματα. Ένας χρήστης μπορεί να εγκαταλείψει μια ενέργεια ή ένα request μπορεί να λήξει. Η ενσωμάτωση ενός CancellationToken επιτρέπει τον έλεγχο της εκτέλεσης και την αποφυγή άσκοπης κατανάλωσης πόρων.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;10. Αποφυγή αναμονής μέσα σε επαναλήψεις&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Η χρήση του await μέσα σε loops οδηγεί σε σειριακή εκτέλεση:&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="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;Process&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Όταν οι εργασίες είναι ανεξάρτητες, η μετατροπή τους σε συλλογή από tasks και η χρήση του Task.WhenAll επιτρέπει την παράλληλη εκτέλεση, βελτιώνοντας σημαντικά την απόδοση.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Συμπέρασμα&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Ο ασύγχρονος προγραμματισμός στην C# δεν αποτελεί απλώς μια τεχνική βελτιστοποίησης, αλλά έναν θεμελιώδη τρόπο σχεδίασης συστημάτων. Η ορθή χρήση του απαιτεί κατανόηση του τρόπου με τον οποίο διαχειρίζεται το runtime τα threads, τα tasks και τη ροή εκτέλεσης.&lt;/p&gt;

&lt;p&gt;Η διαφορά μεταξύ ενός αρχάριου και ενός έμπειρου προγραμματιστή δεν έγκειται στη γνώση της σύνταξης, αλλά στην ικανότητα πρόβλεψης της συμπεριφοράς του συστήματος. Ένας έμπειρος developer αναρωτιέται συνεχώς αν ένας πόρος δεσμεύεται άσκοπα, αν μια εργασία μπορεί να εκτελεστεί παράλληλα ή αν μια εξαίρεση μπορεί να χαθεί.&lt;/p&gt;

&lt;p&gt;Η εμβάθυνση στους παραπάνω κανόνες οδηγεί όχι μόνο σε πιο αποδοτικό κώδικα, αλλά και σε πιο αξιόπιστα και επεκτάσιμα συστήματα. Τελικά, ο στόχος δεν είναι απλώς η δημιουργία κώδικα που λειτουργεί, αλλά η ανάπτυξη λύσεων που παραμένουν σταθερές, κατανοητές και αποδοτικές σε βάθος χρόνου.&lt;/p&gt;




&lt;p&gt;&lt;a href="mailto:nikosstit@gmail.com"&gt;nikosstit@gmail.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>dotnet</category>
      <category>performance</category>
      <category>softwaredevelopment</category>
    </item>
    <item>
      <title>Helper Classes vs Extension Methods</title>
      <dc:creator>nikosst</dc:creator>
      <pubDate>Thu, 19 Mar 2026 21:31:55 +0000</pubDate>
      <link>https://forem.com/__b63657/helper-classes-vs-extension-methods-3f8f</link>
      <guid>https://forem.com/__b63657/helper-classes-vs-extension-methods-3f8f</guid>
      <description>&lt;p&gt;&lt;strong&gt;Ποια είναι η διαφορά και πότε χρησιμοποιείς το καθένα&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Στην καθημερινή ανάπτυξη λογισμικού, πολύ συχνά θα βρεθείς να γράφεις “βοηθητικό” κώδικα. Δηλαδή μικρές λειτουργίες που δεν ανήκουν ξεκάθαρα σε ένα domain object, αλλά χρειάζονται ξανά και ξανά.&lt;/p&gt;

&lt;p&gt;Εδώ εμφανίζονται δύο βασικά patterns:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Helper Classes&lt;/li&gt;
&lt;li&gt;Extension Methods&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Πολλοί τα μπερδεύουν ή τα χρησιμοποιούν τυχαία. Όμως η διαφορά τους δεν είναι τεχνική λεπτομέρεια, είναι θέμα καθαρότητας, αναγνωσιμότητας και επικοινωνίας μέσω κώδικα.&lt;/p&gt;

&lt;p&gt;Και αυτό είναι το πιο σημαντικό.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Helper Classes&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Τι είναι&lt;/p&gt;

&lt;p&gt;Μια helper class είναι απλά μια static class που περιέχει βοηθητικές μεθόδους.&lt;/p&gt;

&lt;p&gt;Δεν “ανήκει” σε κάποιο object. Είναι ένα εξωτερικό εργαλείο.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Παράδειγμα 1: String Helper&lt;/strong&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;static&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;StringHelper&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;static&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;IsNullOrShort&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;text&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="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsNullOrEmpty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;||&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Length&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="m"&gt;5&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;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;string&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;"Nikos"&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;StringHelper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsNullOrShort&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="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;"Invalid 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;&lt;strong&gt;Τι συμβαίνει εδώ&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Έχω ένα string&lt;/li&gt;
&lt;li&gt;Θέλω να κάνω κάτι με αυτό&lt;/li&gt;
&lt;li&gt;Πάω σε μια άλλη class για να το κάνω&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Δηλαδή: φεύγεις από το object&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Παράδειγμα 2: Date Helper&lt;/strong&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;static&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DateHelper&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;static&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;IsWeekend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DateTime&lt;/span&gt; &lt;span class="n"&gt;date&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;date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DayOfWeek&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="n"&gt;DayOfWeek&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Saturday&lt;/span&gt; &lt;span class="p"&gt;||&lt;/span&gt;
               &lt;span class="n"&gt;date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DayOfWeek&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="n"&gt;DayOfWeek&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sunday&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;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;DateHelper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsWeekend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Now&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;"Relax!"&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;strong&gt;Πλεονεκτήματα&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Απλό και ξεκάθαρο&lt;/li&gt;
&lt;li&gt;Μαζεύεις utilities σε ένα μέρος&lt;/li&gt;
&lt;li&gt;Δεν “πειράζεις” υπάρχουσες classes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Μειονεκτήματα&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Δεν είναι intuitive&lt;/li&gt;
&lt;li&gt;Δεν διαβάζεται φυσικά&lt;/li&gt;
&lt;li&gt;Μεγαλώνει εύκολα και γίνεται “dumping ground”&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Extension Methods&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Τι είναι&lt;/p&gt;

&lt;p&gt;Extension methods σου επιτρέπουν να “προσθέσεις” μεθόδους σε υπάρχουσες classes χωρίς να τις αλλάξεις.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Παράδειγμα 1: String Extension&lt;/strong&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;static&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;StringExtensions&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;static&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;IsNullOrShort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;text&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="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsNullOrEmpty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;||&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Length&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="m"&gt;5&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;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;string&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;"Nikos"&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;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsNullOrShort&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;"Invalid 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;Τι αλλάζει εδώ;&lt;/p&gt;

&lt;p&gt;Αντί να λες:&lt;/p&gt;

&lt;p&gt;“πάω στο helper”&lt;/p&gt;

&lt;p&gt;λες:&lt;/p&gt;

&lt;p&gt;“το ίδιο το string ξέρει να το κάνει”&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Παράδειγμα 2: Date Extension&lt;/strong&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;static&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DateExtensions&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;static&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;IsWeekend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="n"&gt;DateTime&lt;/span&gt; &lt;span class="n"&gt;date&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;date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DayOfWeek&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="n"&gt;DayOfWeek&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Saturday&lt;/span&gt; &lt;span class="p"&gt;||&lt;/span&gt;
               &lt;span class="n"&gt;date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DayOfWeek&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="n"&gt;DayOfWeek&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sunday&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;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;DateTime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Now&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsWeekend&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;"Relax!"&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;strong&gt;Πώς το “βρίσκει” ο compiler&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Εδώ βρίσκεται όλη η “μαγεία”, που στην πραγματικότητα δεν είναι μαγεία αλλά ένας πολύ συγκεκριμένος μηχανισμός του compiler.&lt;/p&gt;

&lt;p&gt;Όταν γράφεις name.IsNullOrShort(), ο compiler ΔΕΝ ψάχνει μόνο μέσα στο string για τη μέθοδο. Αν δεν τη βρει εκεί, κάνει ένα δεύτερο βήμα: &lt;strong&gt;κοιτάει όλα τα extension methods που είναι διαθέσιμα μέσω των using statements στο αρχείο σου&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Δηλαδή, &lt;strong&gt;σκανάρει τα static classes στα namespaces που έχεις κάνει import και ψάχνει για μεθόδους που έχουν this string ως πρώτο parameter&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Μόλις βρει μια που ταιριάζει, τη “μεταφράζει” εσωτερικά σε κανονική static κλήση&lt;/strong&gt;, π.χ. StringExtensions.IsNullOrShort(name). &lt;/p&gt;

&lt;p&gt;Αν δεν υπάρχει το σωστό using, η μέθοδος απλά δεν υπάρχει για τον compiler γι’ αυτό και πολλές φορές “ξαφνικά” δουλεύει μόλις προσθέσεις ένα namespace.&lt;/p&gt;

&lt;p&gt;Με λίγα λόγια, &lt;strong&gt;ο compiler δεν αλλάζει το string· απλά σου δίνει έναν πιο φυσικό τρόπο να καλέσεις μια κανονική static μέθοδο&lt;/strong&gt;.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Παράδειγμα: Πώς ο compiler βρίσκει (ή δεν βρίσκει) το extension&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Ορίζεις το extension&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;namespace&lt;/span&gt; &lt;span class="nn"&gt;MyProject.Extensions&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;static&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;StringExtensions&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;static&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;IsNullOrShort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;text&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="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsNullOrEmpty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;||&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Length&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="m"&gt;5&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;Περίπτωση Α: ΧΩΡΙΣ using&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;string&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;"Nikos"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;bool&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;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsNullOrShort&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;//Compile error&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Error:&lt;br&gt;
&lt;code&gt;string does not contain a definition for 'IsNullOrShort'&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Γιατί;&lt;br&gt;
&lt;code&gt;Ο compiler δεν βλέπει το extension, γιατί δεν έχεις κάνει import το namespace.&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Περίπτωση Β: ΜΕ using&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;using&lt;/span&gt; &lt;span class="nn"&gt;MyProject.Extensions&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;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Nikos"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;bool&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;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsNullOrShort&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Works&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Τι έγινε εδώ;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ο compiler δεν βρίσκει τη μέθοδο στο string&lt;/li&gt;
&lt;li&gt;Κοιτάει στα extensions του namespace&lt;/li&gt;
&lt;li&gt;Βρίσκει:
&lt;/li&gt;
&lt;/ul&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;static&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;IsNullOrShort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Και το μετατρέπει σε:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;StringExtensions.IsNullOrShort(name);&lt;/code&gt;&lt;/p&gt;

&lt;p&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;using&lt;/span&gt; &lt;span class="nn"&gt;MyProject.Extensions&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;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Nikos"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;a&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;IsNullOrShort&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;StringExtensions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsNullOrShort&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;Τα a και b είναι ακριβώς το ίδιο πράγμα&lt;/p&gt;

&lt;p&gt;Το extension method:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;δεν ανήκει πραγματικά στο string&lt;/li&gt;
&lt;li&gt;δεν αλλάζει την class&lt;/li&gt;
&lt;li&gt;απλά γίνεται “ορατό” μέσω using&lt;/li&gt;
&lt;li&gt;και ο compiler το μετατρέπει σε static call&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Η Πραγματική Διαφορά&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Δεν είναι θέμα “τι κάνει”, αλλά πώς διαβάζεται.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Σύγκριση&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Helper:&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;StringHelper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsNullOrShort&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;Extension:&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;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsNullOrShort&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;&lt;strong&gt;Πότε χρησιμοποιείς τι&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Χρησιμοποίησε Extension Methods όταν:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Η λειτουργία σχετίζεται ξεκάθαρα με το object&lt;/li&gt;
&lt;li&gt;Θες readable / fluent code&lt;/li&gt;
&lt;li&gt;Θες να γράφεις “σαν πρόταση”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Παραδείγματα:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;string.IsValidEmail()&lt;/li&gt;
&lt;li&gt;date.IsWeekend()&lt;/li&gt;
&lt;li&gt;list.IsEmpty()&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Χρησιμοποίησε Helper Class όταν:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Η λειτουργία είναι γενική&lt;/li&gt;
&lt;li&gt;Δεν ανήκει σε ένα object&lt;/li&gt;
&lt;li&gt;Έχεις logic που χρησιμοποιεί πολλά types&lt;/li&gt;
&lt;/ul&gt;

&lt;p&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;static&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MathHelper&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;static&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;Add&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;a&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;b&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;a&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="n"&gt;b&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;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;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;&lt;strong&gt;Να θυμάσαι..&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Η διαφορά δεν είναι τεχνική, είναι νοητική.&lt;/p&gt;

&lt;p&gt;Helper Class&lt;br&gt;
 εργαλείο έξω από το object&lt;/p&gt;

&lt;p&gt;Extension Method&lt;br&gt;
 συμπεριφορά πάνω στο object&lt;/p&gt;




&lt;p&gt;&lt;a href="mailto:nikosstit@gmail.com"&gt;nikosstit@gmail.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>codequality</category>
      <category>csharp</category>
      <category>dotnet</category>
      <category>softwaredevelopment</category>
    </item>
    <item>
      <title>Από Static Classes σε Services με Dependency Injection Με οπτική Clean Architecture (layers &amp; boundaries)</title>
      <dc:creator>nikosst</dc:creator>
      <pubDate>Wed, 18 Mar 2026 16:19:37 +0000</pubDate>
      <link>https://forem.com/__b63657/apo-static-classes-se-services-me-dependency-injection-me-optike-clean-architecture-layers--cf7</link>
      <guid>https://forem.com/__b63657/apo-static-classes-se-services-me-dependency-injection-me-optike-clean-architecture-layers--cf7</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhqgpc9yo964q59yl8nra.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhqgpc9yo964q59yl8nra.png" alt=" " width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Εισαγωγή&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Οι static classes είναι από τα πρώτα εργαλεία που χρησιμοποιεί ένας developer. Είναι απλές, άμεσες και δεν απαιτούν ιδιαίτερη υποδομή. Για μικρές λειτουργίες ή βοηθητικό κώδικα, συχνά φαίνονται η πιο σωστή επιλογή.&lt;/p&gt;

&lt;p&gt;Όσο όμως ένα σύστημα μεγαλώνει, οι ίδιες αυτές επιλογές αρχίζουν να δημιουργούν περιορισμούς. Ο κώδικας γίνεται πιο δύσκολος να δοκιμαστεί, οι εξαρτήσεις κρύβονται και η ευελιξία μειώνεται.&lt;/p&gt;

&lt;p&gt;Σε αυτό το σημείο μπαίνουν τα services και το dependency injection. Όχι απλά ως τεχνική, αλλά ως τρόπος να οργανώσεις τον κώδικά σου με τρόπο που να αντέχει στον χρόνο.&lt;/p&gt;

&lt;p&gt;Το πραγματικό τους value όμως δεν φαίνεται μόνο σε επίπεδο κλάσης. Φαίνεται όταν τα εντάξεις σωστά μέσα σε αρχιτεκτονικά layers. Εκεί είναι που η διαφορά μεταξύ “κώδικας που απλά δουλεύει” και “κώδικας που μπορεί να εξελιχθεί” γίνεται ξεκάθαρη.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Τι αλλάζει όταν μπαίνουμε σε Clean Architecture&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Στην Clean Architecture δεν μας ενδιαφέρει απλά να γράψουμε services. Μας ενδιαφέρει πού ανήκουν και ποιος εξαρτάται από ποιον.&lt;/p&gt;

&lt;p&gt;Η βασική ιδέα είναι ότι το σύστημα χωρίζεται σε layers με ξεκάθαρα boundaries. Το domain βρίσκεται στο κέντρο και δεν εξαρτάται από τίποτα. Τα εξωτερικά layers εξαρτώνται από το domain, όχι το αντίστροφο.&lt;/p&gt;

&lt;p&gt;Αυτό σημαίνει ότι οι αποφάσεις που παίρνεις για static ή service δεν είναι πλέον τοπικές. Είναι αρχιτεκτονικές.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Πού “σπάνε” οι static classes μέσα σε layers&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Αν χρησιμοποιήσεις static classes μέσα σε ένα layered σύστημα, αρχίζεις να παραβιάζεις boundaries χωρίς να το καταλάβεις.&lt;/p&gt;

&lt;p&gt;Φαντάσου ότι έχεις μια static class που καλεί database ή κάνει HTTP calls. Αν αυτή χρησιμοποιείται μέσα στο domain layer, τότε το domain σου εξαρτάται άμεσα από infrastructure. Έχεις σπάσει την αρχιτεκτονική χωρίς να φαίνεται ξεκάθαρα στο code.&lt;/p&gt;

&lt;p&gt;Αυτό είναι επικίνδυνο γιατί το dependency είναι κρυφό. Δεν φαίνεται από constructor ή interface. Είναι “σκληρά γραμμένο” μέσα στη static κλήση.&lt;/p&gt;

&lt;p&gt;Με services και interfaces, αυτή η εξάρτηση γίνεται explicit και μπορείς να τη μετακινήσεις στο σωστό layer.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Πώς τοποθετούνται τα services στα layers&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Σε ένα Clean Architecture setup, τα services δεν είναι όλα ίδια. Παίζουν διαφορετικούς ρόλους ανά layer.&lt;/p&gt;

&lt;p&gt;Στο domain layer έχεις business rules. Εκεί δεν θέλεις concrete implementations. Θέλεις abstractions. Αν χρειάζεται κάτι εξωτερικό, το εκφράζεις ως interface.&lt;/p&gt;

&lt;p&gt;Στο application layer orchestrate τη ροή. Εκεί χρησιμοποιείς services για να εκτελέσεις use cases. Τα dependencies έρχονται μέσω interfaces.&lt;/p&gt;

&lt;p&gt;Στο infrastructure layer υλοποιείς τα interfaces. Εκεί μπαίνουν database, API clients, email senders και οτιδήποτε εξωτερικό.&lt;/p&gt;

&lt;p&gt;Αν είχες static classes, δεν θα μπορούσες να κάνεις αυτόν τον διαχωρισμό. Θα είχες άμεσες κλήσεις από παντού προς παντού.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Παράδειγμα μέσα σε Clean Architecture&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Ας πούμε ότι έχεις logic για αποστολή email.&lt;/p&gt;

&lt;p&gt;Με static approach:&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;static&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;EmailHelper&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;static&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;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;// SMTP logic εδώ&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;Αν αυτό χρησιμοποιηθεί μέσα στο domain ή στο application layer, έχεις ήδη δέσει το σύστημα σου με συγκεκριμένη υλοποίηση.&lt;/p&gt;

&lt;p&gt;Με Clean Architecture προσέγγιση:&lt;/p&gt;

&lt;p&gt;Στο domain ή application layer:&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;IEmailService&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;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;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;Στο infrastructure layer:&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;SmtpEmailService&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IEmailService&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;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;// SMTP logic εδώ&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;Και το injection γίνεται από έξω. Το domain δεν γνωρίζει τίποτα για SMTP, ούτε για implementation details.&lt;/p&gt;

&lt;p&gt;Αυτό είναι το κλειδί. Δεν σε νοιάζει πώς στέλνεται το email. Σε νοιάζει ότι στέλνεται.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Πώς το lifecycle συνδέεται με τα layers&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Το lifecycle των services αποκτά μεγαλύτερη σημασία μέσα σε Clean Architecture.&lt;/p&gt;

&lt;p&gt;Τα infrastructure services συχνά σχετίζονται με resources. Database connections, HTTP clients, caches. Εκεί πρέπει να είσαι προσεκτικός με το αν θα είναι scoped ή singleton.&lt;/p&gt;

&lt;p&gt;Τα application services είναι συνήθως stateless και μπορούν να είναι transient ή scoped.&lt;/p&gt;

&lt;p&gt;Το domain δεν πρέπει να γνωρίζει τίποτα για lifecycle. Αυτό είναι ευθύνη του outer layer.&lt;/p&gt;

&lt;p&gt;Αυτός ο διαχωρισμός είναι αδύνατος με static classes, γιατί δεν υπάρχει lifecycle. Όλα είναι global και πάντα διαθέσιμα.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Πώς καταλαβαίνεις ότι η static class “παραβιάζει” την αρχιτεκτονική&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Σε ένα Clean Architecture σύστημα, το πιο σημαντικό σήμα είναι το εξής:&lt;/p&gt;

&lt;p&gt;Αν μια static class σε αναγκάζει να κάνεις import κάτι από infrastructure μέσα σε domain ή application, τότε έχεις ήδη πρόβλημα.&lt;/p&gt;

&lt;p&gt;Ένα άλλο σημάδι είναι όταν δεν μπορείς να αλλάξεις implementation χωρίς να πειράξεις πολλά σημεία στο σύστημα. Αυτό σημαίνει ότι δεν έχεις abstraction, αλλά direct dependency.&lt;/p&gt;

&lt;p&gt;Επίσης, αν δεις ότι η static class αρχίζει να μεγαλώνει και να συγκεντρώνει logic από διαφορετικά concerns, τότε έχεις χάσει το separation of concerns.&lt;/p&gt;




&lt;p&gt;Τα θετικά και τα αρνητικά μέσα σε Clean Architecture&lt;/p&gt;

&lt;p&gt;Μέσα σε ένα layered σύστημα, τα services γίνονται σχεδόν απαραίτητα. Δεν είναι απλά πιο “καλά”. Είναι ο μόνος τρόπος να κρατήσεις τα boundaries καθαρά.&lt;/p&gt;

&lt;p&gt;Το μεγάλο πλεονέκτημα είναι ότι μπορείς να αλλάξεις implementation χωρίς να επηρεάσεις το core. Μπορείς να κάνεις testing χωρίς εξωτερικά dependencies. Μπορείς να εξελίξεις το σύστημα χωρίς να “σπάσεις” υπάρχουσα λειτουργικότητα.&lt;/p&gt;

&lt;p&gt;Το κόστος είναι ότι αυξάνεται η πολυπλοκότητα. Έχεις περισσότερα abstractions, περισσότερα αρχεία και περισσότερη “έμμεση” ροή. Αυτό όμως είναι ελεγχόμενη πολυπλοκότητα. Δεν είναι χάος, είναι δομή.&lt;/p&gt;

&lt;p&gt;Οι static classes σε αυτό το περιβάλλον φαίνονται απλές, αλλά στην πράξη δημιουργούν αόρατες συνδέσεις μεταξύ layers. Αυτές οι συνδέσεις είναι που τελικά κάνουν ένα σύστημα δύσκολο να συντηρηθεί.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Πότε πρέπει να μετατρέψεις static σε service μέσα σε Clean Architecture&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Η πιο κρίσιμη στιγμή για refactor έρχεται όταν η static class αρχίζει να “τραβάει” προς τα έξω layers. Αν βλέπεις ότι μέσα σε static logic μπαίνει database access, HTTP calls ή configuration, τότε έχεις ήδη περάσει το όριο.&lt;/p&gt;

&lt;p&gt;Ένα δεύτερο σημείο είναι όταν θέλεις να αλλάξεις implementation χωρίς να επηρεάσεις τον πυρήνα της εφαρμογής. Αν αυτό δεν γίνεται εύκολα, τότε σημαίνει ότι δεν έχεις abstraction και η static class σε κρατάει πίσω.&lt;/p&gt;

&lt;p&gt;Ένα τρίτο, πιο ώριμο κριτήριο, είναι όταν το domain σου αρχίζει να “ξέρει πολλά”. Αν το domain γνωρίζει λεπτομέρειες για το πώς δουλεύουν εξωτερικά συστήματα, τότε η αρχιτεκτονική έχει διαρρεύσει. Εκεί η λύση δεν είναι απλά refactor. Είναι επανατοποθέτηση της ευθύνης μέσω services και interfaces.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Πότε παραμένει σωστή επιλογή η static class&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Ακόμα και μέσα σε Clean Architecture, οι static classes έχουν θέση. Δεν είναι κάτι που πρέπει να εξαφανιστεί.&lt;/p&gt;

&lt;p&gt;Όταν έχεις καθαρή, deterministic λογική, χωρίς καμία εξάρτηση και χωρίς καμία πιθανότητα να αλλάξει, η static class είναι η πιο καθαρή λύση. Μαθηματικοί υπολογισμοί, απλά transformations, pure functions είναι ιδανικές περιπτώσεις.&lt;/p&gt;

&lt;p&gt;Η διαφορά είναι ότι αυτές οι static κλάσεις δεν “αγγίζουν” τα layers. Δεν δημιουργούν dependencies. Είναι απομονωμένες και ασφαλείς.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Το τελικό mental model&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Αν το δεις συνολικά, η απόφαση δεν είναι τεχνική αλλά αρχιτεκτονική.&lt;/p&gt;

&lt;p&gt;Ό,τι ανήκει στον πυρήνα του συστήματος και μπορεί να αλλάξει, πρέπει να προστατεύεται πίσω από abstractions. Εκεί χρησιμοποιείς services και dependency injection.&lt;/p&gt;

&lt;p&gt;Ό,τι είναι απλό, σταθερό και χωρίς εξαρτήσεις, μπορεί να παραμείνει static χωρίς κανένα πρόβλημα.&lt;/p&gt;

&lt;p&gt;Η Clean Architecture δεν σου λέει “μην χρησιμοποιείς static”. Σου λέει “πρόσεχε τα boundaries”. Και τα services είναι ο μηχανισμός που σου επιτρέπει να τα τηρήσεις.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Συμπέρασμα&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Η μετάβαση από static classes σε services είναι στην ουσία μετάβαση από ad-hoc κώδικα σε σχεδιασμένο σύστημα. Όταν τη δεις μέσα από το πρίσμα της Clean Architecture, γίνεται ξεκάθαρο ότι δεν είναι απλά θέμα καλής πρακτικής, αλλά θέμα επιβίωσης του codebase όσο μεγαλώνει.&lt;/p&gt;

&lt;p&gt;Οι static classes είναι χρήσιμες όταν ο κόσμος σου είναι μικρός και ελεγχόμενος. Τα services είναι απαραίτητα όταν ο κόσμος σου αρχίζει να μεγαλώνει και να αποκτά πολυπλοκότητα.&lt;/p&gt;

&lt;p&gt;Ο στόχος δεν είναι να αποφύγεις τα static ή να γεμίσεις το σύστημα με services. Ο στόχος είναι να ξέρεις πού ανήκει το κάθε πράγμα και να το τοποθετείς σωστά.&lt;/p&gt;

&lt;p&gt;Και αυτή είναι, τελικά, η διαφορά ανάμεσα στο να γράφεις κώδικα και στο να σχεδιάζεις συστήματα.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>codequality</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>A Simple Clean Architecture Structure for ASP.NET Core Projects</title>
      <dc:creator>nikosst</dc:creator>
      <pubDate>Tue, 17 Mar 2026 22:29:40 +0000</pubDate>
      <link>https://forem.com/__b63657/a-simple-clean-architecture-structure-for-aspnet-core-projects-33g1</link>
      <guid>https://forem.com/__b63657/a-simple-clean-architecture-structure-for-aspnet-core-projects-33g1</guid>
      <description>&lt;p&gt;Many developers hear about Clean Architecture but struggle with one simple question:&lt;/p&gt;

&lt;p&gt;How should I structure my projects?&lt;/p&gt;

&lt;p&gt;In this article we will look at a simple and practical project structure for ASP.NET Core using the ideas from Clean Architecture, introduced by software engineer Robert C. Martin (Uncle Bob).&lt;/p&gt;

&lt;p&gt;The goal is simple:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;keep business logic independent&lt;/li&gt;
&lt;li&gt;keep infrastructure replaceable&lt;/li&gt;
&lt;li&gt;keep the system easy to maintain&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s break it down.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;The 4 Project Structure&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A very clean approach is to split the solution into four projects.&lt;/p&gt;

&lt;p&gt;MyProject&lt;/p&gt;

&lt;p&gt;MyProject.Domain&lt;br&gt;
MyProject.Application&lt;br&gt;
MyProject.Infrastructure&lt;br&gt;
MyProject.API&lt;/p&gt;

&lt;p&gt;Each project has a very specific responsibility.&lt;/p&gt;



&lt;p&gt;&lt;strong&gt;1. Domain Layer (The Core of the System)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The Domain layer contains the business rules.&lt;/p&gt;

&lt;p&gt;Nothing here should depend on frameworks, databases, or external services.&lt;/p&gt;

&lt;p&gt;Typical folders:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Domain&lt;br&gt;
 ├── Entities&lt;br&gt;
 ├── ValueObjects&lt;br&gt;
 ├── Enums&lt;br&gt;
 └── Exceptions&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Example 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;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Order&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;Guid&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;private&lt;/span&gt; &lt;span class="k"&gt;set&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;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;get&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;set&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;Pay&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// business rule&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;Important rule:&lt;/p&gt;

&lt;p&gt;The Domain layer should have zero dependencies.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;2. Application Layer&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The Application layer orchestrates the use cases of the system.&lt;/p&gt;

&lt;p&gt;This is where we define interfaces for external dependencies such as repositories or services.&lt;/p&gt;

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

&lt;p&gt;&lt;code&gt;Application&lt;br&gt;
 ├── Interfaces&lt;br&gt;
 ├── Services&lt;br&gt;
 ├── DTOs&lt;br&gt;
 ├── Commands&lt;br&gt;
 └── Queries&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Example 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;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="n"&gt;Guid&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;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;p&gt;Application services use these interfaces without knowing how they are implemented.&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;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;PayOrder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Guid&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="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;_repository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetByIdAsync&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;order&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="k"&gt;await&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;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="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;strong&gt;3. Infrastructure Layer&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The Infrastructure layer contains technical implementations.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;database access&lt;/li&gt;
&lt;li&gt;external APIs&lt;/li&gt;
&lt;li&gt;email services&lt;/li&gt;
&lt;li&gt;file storage&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Infrastructure&lt;br&gt;
 ├── Persistence&lt;br&gt;
 ├── Services&lt;br&gt;
 └── External&lt;/p&gt;

&lt;p&gt;Repository 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;OrderRepository&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;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;AppDbContext&lt;/span&gt; &lt;span class="n"&gt;_context&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;OrderRepository&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AppDbContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_context&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&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="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="n"&gt;Guid&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="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_context&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;FindAsync&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="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;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;span class="n"&gt;_context&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;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;_context&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;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice something important:&lt;/p&gt;

&lt;p&gt;The Infrastructure layer depends on the Application layer, not the other way around.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;4. API Layer&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The API layer is the entry point of the system.&lt;/p&gt;

&lt;p&gt;Typical structure:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;API&lt;br&gt;
 ├── Controllers&lt;br&gt;
 └── Program.cs&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Example controller:&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="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ApiController&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"orders"&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;OrdersController&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ControllerBase&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;OrderService&lt;/span&gt; &lt;span class="n"&gt;_service&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;OrdersController&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;OrderService&lt;/span&gt; &lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_service&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;service&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;HttpPost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{id}/pay"&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="n"&gt;IActionResult&lt;/span&gt;&lt;span class="p"&gt;&amp;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;Guid&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="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;PayOrder&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="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;Ok&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;strong&gt;Dependency Direction (The Most Important Rule)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The direction of dependencies should look like this:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;API&lt;br&gt;
 ↓&lt;br&gt;
Application&lt;br&gt;
 ↓&lt;br&gt;
Domain&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Infrastructure → Application&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This rule ensures that business logic remains independent from frameworks and databases.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Dependency Injection&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The concrete implementations are wired up in the API project:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;builder.Services.AddScoped&amp;lt;IOrderRepository, OrderRepository&amp;gt;();&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This allows the Application layer to stay clean and unaware of infrastructure details.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;A Senior Developer Tip&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;As systems grow, many teams move from simple services to patterns like CQRS (Command Query Responsibility Segregation).&lt;/p&gt;

&lt;p&gt;Instead of this:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Application&lt;br&gt;
 └── Services&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;you may see something like this:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Application&lt;br&gt;
 ├── Commands&lt;br&gt;
 ├── Queries&lt;br&gt;
 ├── Handlers&lt;br&gt;
 └── Behaviors&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This approach scales better in larger systems.&lt;/p&gt;




&lt;p&gt;Final Thoughts&lt;/p&gt;

&lt;p&gt;Clean Architecture is not about adding complexity.&lt;/p&gt;

&lt;p&gt;It is about separating responsibilities so your system becomes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;easier to test&lt;/li&gt;
&lt;li&gt;easier to maintain&lt;/li&gt;
&lt;li&gt;easier to evolve&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Start simple, keep the layers clean, and your architecture will scale naturally as your application grows.&lt;/p&gt;




&lt;p&gt;&lt;a href="mailto:nikosstit@gmail.com"&gt;nikosstit@gmail.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>csharp</category>
      <category>dotnet</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Middleware στο ASP.NET Core (C#)</title>
      <dc:creator>nikosst</dc:creator>
      <pubDate>Wed, 11 Mar 2026 21:19:56 +0000</pubDate>
      <link>https://forem.com/__b63657/middleware-sto-aspnet-core-c-55b9</link>
      <guid>https://forem.com/__b63657/middleware-sto-aspnet-core-c-55b9</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftsucea5xr8lcxp5v7qkh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftsucea5xr8lcxp5v7qkh.png" alt=" " width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Από τη θεωρία μέχρι τα production patterns&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Στην αρχιτεκτονική του ASP.NET Core, το middleware αποτελεί έναν από τους πιο θεμελιώδεις μηχανισμούς του framework. Στην πράξη, σχεδόν κάθε request που φτάνει σε μια εφαρμογή ASP.NET Core περνά μέσα από μια αλληλουχία middleware components πριν φτάσει στον τελικό προορισμό του, δηλαδή σε έναν controller, ένα endpoint ή ένα minimal API.&lt;/p&gt;

&lt;p&gt;Η έννοια του middleware δεν είναι απλώς μια τεχνική λεπτομέρεια. Είναι στην ουσία το κεντρικό μοτίβο επεξεργασίας HTTP requests, το οποίο επιτρέπει την υλοποίηση πολλών κρίσιμων λειτουργιών μιας εφαρμογής όπως authentication, logging, exception handling, rate limiting, caching και security.&lt;/p&gt;

&lt;p&gt;Σκοπός αυτού του άρθρου είναι να παρουσιάσει το middleware σε βάθος, με τρόπο που να είναι κατανοητός τόσο από αρχάριους όσο και από πιο προχωρημένους developers, ενώ παράλληλα να εξετάζει και πρακτικές που χρησιμοποιούνται σε enterprise επίπεδο εφαρμογών.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Η βασική ιδέα του Middleware&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Ένα middleware είναι ένα component που συμμετέχει στην επεξεργασία ενός HTTP request. Μπορεί να εκτελέσει λογική πριν και μετά από την εκτέλεση του επόμενου component στο pipeline.&lt;/p&gt;

&lt;p&gt;Το ASP.NET Core βασίζεται σε μια αλυσίδα middleware components, τα οποία εκτελούνται διαδοχικά.&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;Client Request
      │
      ▼
Middleware 1
      │
      ▼
Middleware 2
      │
      ▼
Middleware 3
      │
      ▼
Endpoint (Controller / Minimal API)
      │
      ▼
Response επιστρέφει προς τα πίσω

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

&lt;/div&gt;



&lt;p&gt;Η σημαντική λεπτομέρεια εδώ είναι ότι κάθε middleware έχει τη δυνατότητα να:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;εκτελέσει λογική πριν την επεξεργασία του request&lt;/li&gt;
&lt;li&gt;καλέσει το επόμενο middleware&lt;/li&gt;
&lt;li&gt;επεξεργαστεί το response αφού επιστρέψει&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Η συμπεριφορά αυτή θυμίζει έντονα το Chain of Responsibility pattern, όπου κάθε component μπορεί να επεξεργαστεί ένα αίτημα ή να το προωθήσει στον επόμενο.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Το HTTP Pipeline στο ASP.NET Core&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Το σύνολο των middleware components ονομάζεται HTTP pipeline.&lt;/p&gt;

&lt;p&gt;Όταν μια εφαρμογή ξεκινά, ο developer ορίζει ποια middleware θα συμμετέχουν σε αυτό το pipeline και με ποια σειρά.&lt;/p&gt;

&lt;p&gt;Αυτό γίνεται συνήθως στο αρχείο Program.cs.&lt;/p&gt;

&lt;p&gt;Ένα απλό παράδειγμα pipeline:&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;builder&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;WebApplication&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&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;app&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseHttpsRedirection&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseAuthentication&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseAuthorization&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapControllers&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Σε αυτό το pipeline συμβαίνουν τα εξής:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Το request μετατρέπεται σε HTTPS αν χρειάζεται&lt;/li&gt;
&lt;li&gt;Ελέγχεται η ταυτότητα του χρήστη&lt;/li&gt;
&lt;li&gt;Ελέγχονται τα permissions&lt;/li&gt;
&lt;li&gt;Εκτελείται ο controller&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Η σειρά είναι εξαιρετικά σημαντική. Αν αλλάξει, μπορεί να δημιουργηθούν σφάλματα ή κενά ασφαλείας.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Δημιουργία απλού Middleware&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Το πιο απλό middleware μπορεί να δημιουργηθεί inline μέσα στο pipeline.&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;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&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;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;"Incoming request"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;next&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;"Outgoing response"&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;Το context αντιπροσωπεύει το HTTP context, δηλαδή όλα τα δεδομένα του request και του response.&lt;/p&gt;

&lt;p&gt;Το next είναι ένας delegate που καλεί το επόμενο middleware.&lt;/p&gt;

&lt;p&gt;Η εκτέλεση γίνεται ως εξής:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Εκτελείται η πρώτη Console.WriteLine&lt;/li&gt;
&lt;li&gt;Εκτελείται το επόμενο middleware&lt;/li&gt;
&lt;li&gt;Επιστρέφει ο έλεγχος και εκτελείται η δεύτερη Console.WriteLine&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Δημιουργία Custom Middleware Class&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Σε πραγματικές εφαρμογές είναι καλύτερο να δημιουργούμε ξεχωριστές κλάσεις middleware.&lt;/p&gt;

&lt;p&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;LoggingMiddleware&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;RequestDelegate&lt;/span&gt; &lt;span class="n"&gt;_next&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;LoggingMiddleware&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;RequestDelegate&lt;/span&gt; &lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_next&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;next&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;InvokeAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HttpContext&lt;/span&gt; &lt;span class="n"&gt;context&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;$"Request: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&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;Path&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;_next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&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;$"Response: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StatusCode&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&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;Και προσθήκη στο pipeline:&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;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UseMiddleware&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;LoggingMiddleware&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;Αυτός είναι ο πιο καθαρός και επεκτάσιμος τρόπος δημιουργίας middleware.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Middleware και SOLID Principles&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Το middleware architecture είναι ένα εξαιρετικό παράδειγμα εφαρμογής των αρχών SOLID.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Single Responsibility Principle&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Κάθε middleware πρέπει να έχει μία ευθύνη.&lt;/p&gt;

&lt;p&gt;Για παράδειγμα:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;LoggingMiddleware&lt;/li&gt;
&lt;li&gt;AuthenticationMiddleware&lt;/li&gt;
&lt;li&gt;ExceptionMiddleware&lt;/li&gt;
&lt;li&gt;RateLimitMiddleware&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Δεν πρέπει να υπάρχει middleware που να κάνει πολλά πράγματα ταυτόχρονα.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Open / Closed Principle&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Το pipeline μπορεί να επεκταθεί χωρίς να αλλάξουμε υπάρχον κώδικα.&lt;/p&gt;

&lt;p&gt;Απλά προσθέτουμε νέο middleware.&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;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UseMiddleware&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;LoggingMiddleware&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UseMiddleware&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;MetricsMiddleware&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UseMiddleware&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;RateLimitMiddleware&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;&lt;strong&gt;Dependency Injection&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Τα middleware μπορούν να χρησιμοποιούν dependency injection.&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;LoggingMiddleware&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;RequestDelegate&lt;/span&gt; &lt;span class="n"&gt;_next&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;ILogger&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;LoggingMiddleware&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_logger&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;LoggingMiddleware&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;RequestDelegate&lt;/span&gt; &lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;ILogger&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;LoggingMiddleware&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_next&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;_logger&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;logger&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;InvokeAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HttpContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LogInformation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Request started"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;_next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="n"&gt;_logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LogInformation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Request finished"&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;Αυτό επιτρέπει εύκολη ενσωμάτωση με services της εφαρμογής.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Κύριες Χρήσεις Middleware&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Το middleware χρησιμοποιείται σε πολλές κρίσιμες λειτουργίες μιας web εφαρμογής.&lt;/p&gt;

&lt;p&gt;Οι πιο συνηθισμένες περιπτώσεις είναι:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Logging requests&lt;/li&gt;
&lt;li&gt;Authentication&lt;/li&gt;
&lt;li&gt;Authorization&lt;/li&gt;
&lt;li&gt;Exception handling&lt;/li&gt;
&lt;li&gt;Request validation&lt;/li&gt;
&lt;li&gt;Security headers&lt;/li&gt;
&lt;li&gt;Rate limiting&lt;/li&gt;
&lt;li&gt;Response caching&lt;/li&gt;
&lt;li&gt;Metrics και monitoring&lt;/li&gt;
&lt;li&gt;Localization&lt;/li&gt;
&lt;li&gt;Multi-tenancy&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Παρακάτω αναλύουμε τις πιο σημαντικές.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Exception Handling Middleware&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Σε production εφαρμογές δεν θέλουμε τα exceptions να εμφανίζονται απευθείας στον χρήστη.&lt;/p&gt;

&lt;p&gt;Ένα middleware μπορεί να τα διαχειρίζεται κεντρικά.&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;ExceptionMiddleware&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;RequestDelegate&lt;/span&gt; &lt;span class="n"&gt;_next&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;ExceptionMiddleware&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;RequestDelegate&lt;/span&gt; &lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_next&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;next&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;InvokeAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HttpContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;_next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StatusCode&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;500&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;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="s"&gt;"An internal server error occurred"&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;Αυτό δημιουργεί centralized error handling.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Logging Middleware&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Το logging middleware βοηθά στη διάγνωση προβλημάτων και στο monitoring.&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;RequestLoggingMiddleware&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;RequestDelegate&lt;/span&gt; &lt;span class="n"&gt;_next&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;RequestLoggingMiddleware&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;RequestDelegate&lt;/span&gt; &lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_next&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;next&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;InvokeAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HttpContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&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;start&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UtcNow&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;_next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&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;duration&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UtcNow&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="n"&gt;start&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;$"&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&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;Method&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&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;Path&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt; took &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TotalMilliseconds&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;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;/p&gt;

&lt;ul&gt;
&lt;li&gt;structured logging&lt;/li&gt;
&lt;li&gt;distributed tracing&lt;/li&gt;
&lt;li&gt;application monitoring&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Rate Limiting Middleware&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Σε public APIs είναι απαραίτητο να περιορίζουμε τον αριθμό των requests.&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;RateLimitMiddleware&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;RequestDelegate&lt;/span&gt; &lt;span class="n"&gt;_next&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;RateLimitMiddleware&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;RequestDelegate&lt;/span&gt; &lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_next&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;next&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;InvokeAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HttpContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&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;ip&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RemoteIpAddress&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;ToString&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="c1"&gt;// pseudo logic rate limit&lt;/span&gt;

        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;_next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&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;/p&gt;

&lt;ul&gt;
&lt;li&gt;abuse&lt;/li&gt;
&lt;li&gt;bot traffic&lt;/li&gt;
&lt;li&gt;denial-of-service attacks&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Security Headers Middleware&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Τα security headers είναι σημαντικά για την προστασία της εφαρμογής.&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;SecurityHeadersMiddleware&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;RequestDelegate&lt;/span&gt; &lt;span class="n"&gt;_next&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;SecurityHeadersMiddleware&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;RequestDelegate&lt;/span&gt; &lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_next&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;next&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;InvokeAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HttpContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"X-Frame-Options"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"DENY"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"X-Content-Type-Options"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"nosniff"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"X-XSS-Protection"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"1; mode=block"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;_next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&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;strong&gt;JWT Authentication Middleware&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Σε APIs χρησιμοποιούμε συχνά JWT tokens.&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;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseAuthentication&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseAuthorization&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Το authentication middleware:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;διαβάζει το token&lt;/li&gt;
&lt;li&gt;το επικυρώνει&lt;/li&gt;
&lt;li&gt;δημιουργεί το User Identity&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Correlation ID Middleware (Production Pattern)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Σε distributed systems είναι σημαντικό να υπάρχει correlation id για tracing.&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;CorrelationIdMiddleware&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;RequestDelegate&lt;/span&gt; &lt;span class="n"&gt;_next&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;CorrelationIdMiddleware&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;RequestDelegate&lt;/span&gt; &lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_next&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;next&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;InvokeAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HttpContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&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;correlationId&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Guid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;NewGuid&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;ToString&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"X-Correlation-ID"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;correlationId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;_next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&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;Αυτό βοηθά στο debugging microservices.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Multi-Tenancy Middleware&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Σε SaaS εφαρμογές χρησιμοποιούμε middleware για να εντοπίσουμε τον tenant.&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;TenantMiddleware&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;RequestDelegate&lt;/span&gt; &lt;span class="n"&gt;_next&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;TenantMiddleware&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;RequestDelegate&lt;/span&gt; &lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_next&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;next&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;InvokeAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HttpContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&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;tenant&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&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;Headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"X-Tenant"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

        &lt;span class="n"&gt;context&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="s"&gt;"Tenant"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tenant&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;_next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&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;strong&gt;Use vs Run vs Map&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Το ASP.NET Core προσφέρει διαφορετικούς τρόπους προσθήκης middleware.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Συνεχίζει το pipeline.&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;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&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;await&lt;/span&gt; &lt;span class="nf"&gt;next&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;strong&gt;Run&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Τερματίζει το pipeline.&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;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;context&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;await&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"End"&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;strong&gt;Map&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Δημιουργεί branch.&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;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/api"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;apiApp&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;apiApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UseMiddleware&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ApiMiddleware&lt;/span&gt;&lt;span class="p"&gt;&amp;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;strong&gt;Middleware Order&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Η σειρά είναι κρίσιμη.&lt;/p&gt;

&lt;p&gt;Ένα συνηθισμένο production pipeline:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Exception Middleware
Logging Middleware
Security Middleware
Routing
Authentication
Authorization
Endpoints

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

&lt;/div&gt;



&lt;p&gt;Αν αλλάξει η σειρά μπορεί να προκύψουν προβλήματα ασφαλείας ή λειτουργίας.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Best Practices για Middleware&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Σε production συστήματα πρέπει να ακολουθούνται μερικές βασικές αρχές.&lt;/p&gt;

&lt;p&gt;Τα middleware πρέπει να είναι:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;stateless&lt;/li&gt;
&lt;li&gt;γρήγορα&lt;/li&gt;
&lt;li&gt;ελαφριά&lt;/li&gt;
&lt;li&gt;εύκολα επεκτάσιμα&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Stateless&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Ένα middleware πρέπει να είναι stateless, δηλαδή να μην αποθηκεύει κατάσταση (state) μεταξύ διαφορετικών HTTP requests. Κάθε request πρέπει να αντιμετωπίζεται ανεξάρτητα από τα προηγούμενα.&lt;/p&gt;

&lt;p&gt;Αυτό σημαίνει ότι το middleware δεν πρέπει να κρατά δεδομένα σε fields που αλλάζουν ανά request, γιατί η ίδια instance μπορεί να χρησιμοποιείται ταυτόχρονα από πολλούς χρήστες.&lt;/p&gt;

&lt;p&gt;Για παράδειγμα, δεν πρέπει να αποθηκεύουμε στοιχεία χρήστη σε μεταβλητές της κλάσης. Αν χρειάζεται προσωρινή πληροφορία, χρησιμοποιούμε το HttpContext, το οποίο είναι μοναδικό για κάθε request.&lt;/p&gt;

&lt;p&gt;Η stateless σχεδίαση κάνει το middleware thread-safe και scalable, κάτι ιδιαίτερα σημαντικό για εφαρμογές με πολλούς ταυτόχρονους χρήστες.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Γρήγορα&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Τα middleware πρέπει να εκτελούνται όσο το δυνατόν πιο γρήγορα, γιατί κάθε HTTP request περνά από αυτά.&lt;/p&gt;

&lt;p&gt;Αν ένα middleware εκτελεί βαριές λειτουργίες, όπως μεγάλα queries στη βάση ή σύνθετους υπολογισμούς, τότε επηρεάζεται η συνολική απόδοση της εφαρμογής.&lt;/p&gt;

&lt;p&gt;Για αυτό το λόγο συνήθως τα middleware περιορίζονται σε λειτουργίες όπως logging, validation ή routing και αποφεύγουν βαριά business logic.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Ελαφριά&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Ένα middleware πρέπει να είναι ελαφρύ σε πόρους (CPU και μνήμη). Η βασική του ευθύνη είναι να επεξεργαστεί το request και να το προωθήσει γρήγορα στο επόμενο component του pipeline.&lt;/p&gt;

&lt;p&gt;Αν ένα middleware περιέχει πολύπλοκη λογική ή πολλές εξαρτήσεις, τότε το pipeline γίνεται πιο αργό και δύσκολο στη συντήρηση.&lt;/p&gt;

&lt;p&gt;Η καλή πρακτική είναι το middleware να λειτουργεί ως ενδιάμεσος μηχανισμός διαχείρισης του request, ενώ η κύρια λογική να υλοποιείται σε services.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Εύκολα επεκτάσιμα&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Τα middleware πρέπει να είναι σχεδιασμένα έτσι ώστε να μπορούν να επεκταθούν ή να αντικατασταθούν εύκολα χωρίς να επηρεάζεται η υπόλοιπη εφαρμογή.&lt;/p&gt;

&lt;p&gt;Αυτό επιτυγχάνεται με:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;καθαρό διαχωρισμό ευθυνών&lt;/li&gt;
&lt;li&gt;χρήση dependency injection&lt;/li&gt;
&lt;li&gt;μικρά και ανεξάρτητα middleware components&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Με αυτόν τον τρόπο μπορούμε να προσθέσουμε νέα λειτουργικότητα στο pipeline απλά προσθέτοντας ένα νέο middleware, χωρίς να χρειαστεί να τροποποιήσουμε τον υπάρχοντα κώδικα.&lt;/p&gt;

&lt;p&gt;Επίσης δεν πρέπει να περιέχουν business logic. Η επιχειρησιακή λογική ανήκει στο domain ή στα services.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Να θυμάσαι..&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Το middleware αποτελεί τον βασικό μηχανισμό επεξεργασίας HTTP requests στο ASP.NET Core. Μέσα από το middleware pipeline μπορούμε να υλοποιήσουμε μια μεγάλη ποικιλία λειτουργιών, από logging και authentication μέχρι security και distributed tracing.&lt;/p&gt;

&lt;p&gt;Η σωστή σχεδίαση middleware επιτρέπει τη δημιουργία εφαρμογών που είναι modular, επεκτάσιμες και εύκολες στη συντήρηση. Συνδυάζοντας τις αρχές του SOLID, το dependency injection και το σωστό pipeline ordering, το ASP.NET Core παρέχει μια ιδιαίτερα ισχυρή αρχιτεκτονική για την ανάπτυξη σύγχρονων web εφαρμογών και APIs.&lt;/p&gt;




&lt;p&gt;&lt;a href="mailto:nikosstit@gmail.com"&gt;nikosstit@gmail.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>backend</category>
      <category>csharp</category>
      <category>dotnet</category>
    </item>
    <item>
      <title>Async / Await, Task και Thread Pool στο C# Μια πλήρης αλλά απλή εξήγηση</title>
      <dc:creator>nikosst</dc:creator>
      <pubDate>Tue, 10 Mar 2026 16:12:48 +0000</pubDate>
      <link>https://forem.com/__b63657/async-await-task-kai-thread-pool-sto-c-mia-pleres-alla-aple-exegese-2k71</link>
      <guid>https://forem.com/__b63657/async-await-task-kai-thread-pool-sto-c-mia-pleres-alla-aple-exegese-2k71</guid>
      <description>&lt;p&gt;Πολλοί προγραμματιστές χρησιμοποιούν async και await καθημερινά χωρίς να γνωρίζουν πραγματικά τι συμβαίνει από κάτω.&lt;br&gt;
Σε αυτό το άρθρο θα εξηγήσουμε από την αρχή και χωρίς κενά:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;τι είναι thread&lt;/li&gt;
&lt;li&gt;τι είναι thread pool&lt;/li&gt;
&lt;li&gt;τι είναι task&lt;/li&gt;
&lt;li&gt;τι κάνουν οι λέξεις async και await&lt;/li&gt;
&lt;li&gt;τι σημαίνει "syntactic sugar"&lt;/li&gt;
&lt;li&gt;τι πραγματικά κάνει ο compiler&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Το άρθρο βασίζεται στη συμπεριφορά της πλατφόρμας .NET και της γλώσσας C#.&lt;/p&gt;



&lt;p&gt;&lt;strong&gt;1. Τι είναι ένα Thread&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Ένα thread (νήμα εκτέλεσης) είναι ο μικρότερος τρόπος με τον οποίο μπορεί να εκτελεστεί κώδικας σε ένα πρόγραμμα.&lt;/p&gt;

&lt;p&gt;Σκέψου ένα πρόγραμμα σαν ένα εργοστάσιο.&lt;/p&gt;

&lt;p&gt;Το εργοστάσιο είναι η εφαρμογή.&lt;br&gt;
Οι εργαζόμενοι είναι τα threads.&lt;/p&gt;

&lt;p&gt;Κάθε thread εκτελεί εντολές του προγράμματος.&lt;/p&gt;

&lt;p&gt;Παράδειγμα:&lt;/p&gt;

&lt;p&gt;Ένα πρόγραμμα μπορεί να έχει:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ένα thread για το UI&lt;/li&gt;
&lt;li&gt;ένα thread για υπολογισμούς&lt;/li&gt;
&lt;li&gt;ένα thread για δίκτυο&lt;/li&gt;
&lt;li&gt;Κάθε thread εκτελεί κώδικα ανεξάρτητα.&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;&lt;strong&gt;2. Γιατί δεν δημιουργούμε συνεχώς Threads&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Η δημιουργία ενός νέου thread είναι ακριβή διαδικασία για το λειτουργικό σύστημα.&lt;/p&gt;

&lt;p&gt;Υπάρχουν αρκετοί λόγοι.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Memory allocation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Όταν δημιουργείται ένα thread, το λειτουργικό σύστημα πρέπει να δεσμεύσει μνήμη για αυτό.&lt;/p&gt;

&lt;p&gt;Η μνήμη αυτή χρησιμοποιείται για:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;το stack του thread&lt;/li&gt;
&lt;li&gt;μεταβλητές που χρειάζεται το thread&lt;/li&gt;
&lt;li&gt;εσωτερικά δεδομένα του λειτουργικού συστήματος&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Η διαδικασία αυτή λέγεται memory allocation, δηλαδή:&lt;/p&gt;

&lt;p&gt;δέσμευση ενός τμήματος μνήμης για χρήση από ένα thread.&lt;/p&gt;



&lt;p&gt;&lt;strong&gt;Kernel scheduling&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Το λειτουργικό σύστημα πρέπει να αποφασίσει ποιο thread θα εκτελεστεί κάθε στιγμή στον επεξεργαστή.&lt;/p&gt;

&lt;p&gt;Αυτό το κάνει ένα κομμάτι του λειτουργικού συστήματος που λέγεται &lt;strong&gt;kernel&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Η διαδικασία με την οποία ο kernel αποφασίζει ποιο thread θα τρέξει λέγεται:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;kernel scheduling&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Δηλαδή:&lt;/p&gt;

&lt;p&gt;ο προγραμματισμός εκτέλεσης των threads από το λειτουργικό σύστημα.&lt;/p&gt;



&lt;p&gt;&lt;strong&gt;Context switch&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Ο επεξεργαστής μπορεί να εκτελεί ένα thread τη φορά ανά πυρήνα.&lt;/p&gt;

&lt;p&gt;Όταν το λειτουργικό σύστημα αλλάζει thread, πρέπει να:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;αποθηκεύσει την κατάσταση του τρέχοντος thread&lt;/li&gt;
&lt;li&gt;φορτώσει την κατάσταση ενός άλλου thread&lt;/li&gt;
&lt;li&gt;συνεχίσει την εκτέλεση&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Αυτή η αλλαγή λέγεται:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;context switch&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Δηλαδή:&lt;/p&gt;

&lt;p&gt;αλλαγή από ένα thread σε ένα άλλο.&lt;/p&gt;

&lt;p&gt;Η διαδικασία αυτή κοστίζει χρόνο.&lt;/p&gt;



&lt;p&gt;&lt;strong&gt;3. Τι είναι το Thread Pool&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Για να αποφύγουμε τη συνεχή δημιουργία threads, το runtime της πλατφόρμας .NET χρησιμοποιεί κάτι που λέγεται:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Thread Pool&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Το Thread Pool είναι μια συλλογή από threads που έχουν δημιουργηθεί ήδη και περιμένουν δουλειά.&lt;/p&gt;

&lt;p&gt;Αντί να δημιουργούμε νέο thread κάθε φορά:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;το πρόγραμμα στέλνει μια εργασία&lt;/li&gt;
&lt;li&gt;η εργασία μπαίνει σε μια ουρά &lt;/li&gt;
&lt;li&gt;ένα διαθέσιμο thread από το pool την εκτελεί&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Έτσι:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;δεν δημιουργούνται συνεχώς νέα threads&lt;/li&gt;
&lt;li&gt;μειώνεται το κόστος&lt;/li&gt;
&lt;li&gt;αυξάνεται η απόδοση&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;&lt;strong&gt;4. Τι είναι το Task&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Ένα Task είναι μια αναπαράσταση μιας εργασίας που θα ολοκληρωθεί στο μέλλον.&lt;/p&gt;

&lt;p&gt;Πολύ σημαντικό:&lt;/p&gt;

&lt;p&gt;Task δεν είναι thread.&lt;/p&gt;

&lt;p&gt;Ένα Task είναι απλά ένα αντικείμενο που λέει:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Υπάρχει μια εργασία που θα ολοκληρωθεί αργότερα.&lt;/em&gt;&lt;/p&gt;

&lt;p&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="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="n"&gt;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;CalculateAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Αυτό σημαίνει:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;η εργασία εκτελείται&lt;/li&gt;
&lt;li&gt;κάποια στιγμή θα επιστρέψει έναν αριθμό&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Το Task απλά κρατά πληροφορίες για την κατάσταση της εργασίας.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;5. Τι σημαίνει Async&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Η λέξη async μπαίνει πριν από μια μέθοδο.&lt;/p&gt;

&lt;p&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;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;string&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;GetData&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;Η λέξη async δηλώνει ότι:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;η μέθοδος μπορεί να περιέχει await.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Δεν δημιουργεί νέο thread.&lt;/p&gt;

&lt;p&gt;Δεν κάνει την μέθοδο αυτόματα ασύγχρονη.&lt;/p&gt;

&lt;p&gt;Απλά επιτρέπει τη χρήση του await.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;6. Τι κάνει το Await&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Το await χρησιμοποιείται για να περιμένουμε την ολοκλήρωση ενός Task.&lt;/p&gt;

&lt;p&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="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;GetDataAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Η διαφορά από την κανονική αναμονή είναι σημαντική.&lt;/p&gt;

&lt;p&gt;Με απλή αναμονή το thread μπλοκάρει.&lt;/p&gt;

&lt;p&gt;Με await το thread ελευθερώνεται για να κάνει άλλη δουλειά.&lt;/p&gt;

&lt;p&gt;Η διαδικασία είναι η εξής:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;ξεκινά το Task&lt;/li&gt;
&lt;li&gt;αν δεν έχει ολοκληρωθεί&lt;/li&gt;
&lt;li&gt;η μέθοδος "παγώνει"&lt;/li&gt;
&lt;li&gt;το thread επιστρέφει στο Thread Pool&lt;/li&gt;
&lt;li&gt;όταν ολοκληρωθεί το Task&lt;/li&gt;
&lt;li&gt;η μέθοδος συνεχίζει από το σημείο που σταμάτησε&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;&lt;strong&gt;7. Παράδειγμα Async με καθυστέρηση&lt;/strong&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;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;Example&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;"Start"&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;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;2000&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;"End"&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;/p&gt;

&lt;ol&gt;
&lt;li&gt;εκτελείται το Start&lt;/li&gt;
&lt;li&gt;ξεκινά το Delay&lt;/li&gt;
&lt;li&gt;η μέθοδος σταματά προσωρινά&lt;/li&gt;
&lt;li&gt;το thread ελευθερώνεται&lt;/li&gt;
&lt;li&gt;μετά από 2 δευτερόλεπτα&lt;/li&gt;
&lt;li&gt;η μέθοδος συνεχίζει&lt;/li&gt;
&lt;li&gt;εκτελείται το End&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;&lt;strong&gt;8. Async δεν σημαίνει νέο Thread&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Ένα πολύ συχνό λάθος είναι να πιστεύουμε ότι:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;async = νέο thread&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Αυτό δεν ισχύει.&lt;/p&gt;

&lt;p&gt;Στις περισσότερες περιπτώσεις async χρησιμοποιείται για I/O εργασίες.&lt;/p&gt;

&lt;p&gt;Παραδείγματα:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;HTTP requests&lt;/li&gt;
&lt;li&gt;database queries&lt;/li&gt;
&lt;li&gt;file system&lt;/li&gt;
&lt;li&gt;network calls&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Σε αυτές τις περιπτώσεις:&lt;/p&gt;

&lt;p&gt;το πρόγραμμα στέλνει το αίτημα και απλά περιμένει απάντηση.&lt;/p&gt;

&lt;p&gt;Κατά τη διάρκεια της αναμονής δεν χρειάζεται thread να δουλεύει συνεχώς.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;9. Τι σημαίνει Syntactic Sugar&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Οι λέξεις async και await είναι κάτι που λέγεται:&lt;/p&gt;

&lt;p&gt;syntactic sugar&lt;/p&gt;

&lt;p&gt;Αυτό σημαίνει:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;ένας πιο απλός τρόπος γραφής για κάτι που θα μπορούσε να γραφτεί πιο δύσκολα.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Πριν υπάρξει το async/await γράφαμε κώδικα με callbacks.&lt;/p&gt;

&lt;p&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="nf"&gt;GetDataAsync&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;ContinueWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&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;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;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Result&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;Με async/await:&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;data&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;GetDataAsync&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;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Ο δεύτερος τρόπος είναι πολύ πιο κατανοητός.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;10. Τι κάνει ο Compiler&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Όταν γράφουμε async μέθοδο, ο compiler της C# δεν την αφήνει έτσι.&lt;/p&gt;

&lt;p&gt;Την μετατρέπει σε κάτι πιο σύνθετο που λέγεται:&lt;/p&gt;

&lt;p&gt;state machine&lt;/p&gt;

&lt;p&gt;Μια state machine είναι ένας μηχανισμός που:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;θυμάται σε ποιο σημείο βρισκόταν η μέθοδος&lt;/li&gt;
&lt;li&gt;ξέρει από πού να συνεχίσει μετά το await&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Έτσι η μέθοδος μπορεί να:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;σταματήσει&lt;/li&gt;
&lt;li&gt;αποθηκεύσει την κατάσταση της&lt;/li&gt;
&lt;li&gt;συνεχίσει αργότερα&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;11. Πραγματικό παράδειγμα με HTTP&lt;/strong&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;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;string&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;Download&lt;/span&gt;&lt;span class="p"&gt;()&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;client&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;HttpClient&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetStringAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"https://example.com"&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;/p&gt;

&lt;ul&gt;
&lt;li&gt;στέλνεται το HTTP request&lt;/li&gt;
&lt;li&gt;το πρόγραμμα περιμένει απάντηση&lt;/li&gt;
&lt;li&gt;το thread ελευθερώνεται&lt;/li&gt;
&lt;li&gt;όταν έρθει η απάντηση&lt;/li&gt;
&lt;li&gt;η μέθοδος συνεχίζει&lt;/li&gt;
&lt;li&gt;επιστρέφεται το αποτέλεσμα&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;12. Πότε πρέπει να χρησιμοποιούμε Async&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Το async είναι πολύ χρήσιμο σε εφαρμογές όπως:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;web APIs&lt;/li&gt;
&lt;li&gt;servers&lt;/li&gt;
&lt;li&gt;εφαρμογές με UI&lt;/li&gt;
&lt;li&gt;εφαρμογές που κάνουν πολλά network calls&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Παράδειγμα framework:&lt;/p&gt;

&lt;p&gt;ASP.NET Core&lt;/p&gt;

&lt;p&gt;Εκεί το async επιτρέπει σε έναν server να εξυπηρετεί πολλούς χρήστες ταυτόχρονα χωρίς να χρειάζεται πολλά threads.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;13. Πότε δεν είναι χρήσιμο&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Το async δεν βοηθά σε εργασίες που χρησιμοποιούν έντονα τον επεξεργαστή.&lt;/p&gt;

&lt;p&gt;Παραδείγματα:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;επεξεργασία εικόνας&lt;/li&gt;
&lt;li&gt;πολύπλοκοι μαθηματικοί υπολογισμοί&lt;/li&gt;
&lt;li&gt;machine learning&lt;/li&gt;
&lt;li&gt;συμπίεση δεδομένων&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Σε αυτές τις περιπτώσεις χρειαζόμαστε πραγματικά threads ή παράλληλη εκτέλεση.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Συμπέρασμα&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Ας συνοψίσουμε τις βασικές έννοιες.&lt;/p&gt;

&lt;p&gt;Thread&lt;br&gt;
είναι ένα νήμα εκτέλεσης κώδικα.&lt;/p&gt;

&lt;p&gt;Thread Pool&lt;br&gt;
είναι μια συλλογή από έτοιμα threads που επαναχρησιμοποιούνται.&lt;/p&gt;

&lt;p&gt;Task&lt;br&gt;
είναι μια αναπαράσταση μιας εργασίας που θα ολοκληρωθεί στο μέλλον.&lt;/p&gt;

&lt;p&gt;async&lt;br&gt;
επιτρέπει την χρήση του await.&lt;/p&gt;

&lt;p&gt;await&lt;br&gt;
περιμένει την ολοκλήρωση μιας εργασίας χωρίς να μπλοκάρει το thread.&lt;/p&gt;

&lt;p&gt;syntactic sugar&lt;br&gt;
είναι ένας πιο απλός τρόπος γραφής για κάτι πιο σύνθετο.&lt;/p&gt;




&lt;p&gt;&lt;a href="mailto:nikosstit@gmail.com"&gt;nikosstit@gmail.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>csharp</category>
      <category>dotnet</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Async/Await in C#: The Feature You Think You Understand (But Probably Use Wrong)</title>
      <dc:creator>nikosst</dc:creator>
      <pubDate>Sun, 08 Mar 2026 17:34:02 +0000</pubDate>
      <link>https://forem.com/__b63657/asyncawait-in-c-the-feature-you-think-you-understand-but-probably-use-wrong-4963</link>
      <guid>https://forem.com/__b63657/asyncawait-in-c-the-feature-you-think-you-understand-but-probably-use-wrong-4963</guid>
      <description>&lt;p&gt;If you work with C# and .NET, chances are you use async and await every day.&lt;/p&gt;

&lt;p&gt;Yet many developers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;don't fully understand what await actually does&lt;/li&gt;
&lt;li&gt;accidentally introduce performance bottlenecks&lt;/li&gt;
&lt;li&gt;block threads without realizing it&lt;/li&gt;
&lt;li&gt;write async code that isn’t truly asynchronous&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This article is written primarily for Senior Developers, but structured so Junior Developers can learn from it step by step.&lt;/p&gt;

&lt;p&gt;The goal is simple:&lt;/p&gt;

&lt;p&gt;Build a correct mental model of asynchronous programming.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;The Biggest Misconception&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;One of the most common misconceptions in .NET development is the following:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;async/await = new thread&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This is not true.&lt;/p&gt;

&lt;p&gt;Using async and await does not automatically create a new thread.&lt;/p&gt;

&lt;p&gt;Instead, something much more efficient happens.&lt;/p&gt;

&lt;p&gt;When an asynchronous operation starts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the current thread initiates the operation&lt;/li&gt;
&lt;li&gt;the thread returns to the thread pool&lt;/li&gt;
&lt;li&gt;the application does not block while waiting&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;when the operation completes, execution continues later from the awaited point&lt;/p&gt;

&lt;p&gt;This mechanism allows the runtime to reuse threads efficiently instead of keeping them idle while waiting for I/O operations such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;database queries&lt;/li&gt;
&lt;li&gt;HTTP requests&lt;/li&gt;
&lt;li&gt;file access&lt;/li&gt;
&lt;li&gt;external API calls&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because threads are not blocked, the server can handle many more concurrent requests with the same resources.&lt;/p&gt;

&lt;p&gt;And this is exactly what makes modern .NET web APIs highly scalable.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;The Real Problem Async Solves&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Imagine this API endpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public string GetData()
{
    var client = new HttpClient();
    var result = client.GetStringAsync("https://api.example.com").Result;
    return result;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What happens here?&lt;/p&gt;

&lt;p&gt;The thread:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;waits for the network response&lt;/li&gt;
&lt;li&gt;does nothing meanwhile&lt;/li&gt;
&lt;li&gt;remains blocked&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In a high-traffic server this leads to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;thread pool exhaustion&lt;/li&gt;
&lt;li&gt;lower throughput&lt;/li&gt;
&lt;li&gt;higher latency&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;The Correct Async Version&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;public async Task&amp;lt;string&amp;gt; GetDataAsync()
{
    var client = new HttpClient();
    return await client.GetStringAsync("https://api.example.com");
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the thread:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;starts the request&lt;/li&gt;
&lt;li&gt;returns to the pool&lt;/li&gt;
&lt;li&gt;resumes when the response arrives&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is the key to scalability.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;The Mistake Even Senior Developers Make&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;One of the most common mistakes developers make when working with asynchronous code is forcing it to run synchronously.&lt;/p&gt;

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

&lt;p&gt;&lt;code&gt;var result = GetDataAsync().Result;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;or&lt;/p&gt;

&lt;p&gt;&lt;code&gt;GetDataAsync().Wait();&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;At first glance this might seem harmless. You call an async method and simply wait for the result.&lt;/p&gt;

&lt;p&gt;But what actually happens is more problematic.&lt;/p&gt;

&lt;p&gt;Even though GetDataAsync() is asynchronous, using .Result or .Wait() blocks the current thread while waiting for the operation to complete.&lt;/p&gt;

&lt;p&gt;Instead of allowing the thread to return to the thread pool and continue other work, the thread just sits there doing nothing, waiting for the result.&lt;/p&gt;

&lt;p&gt;This defeats the entire purpose of asynchronous programming.&lt;/p&gt;

&lt;p&gt;Because of this blocking behavior, several problems can occur:&lt;/p&gt;

&lt;p&gt;Deadlocks especially in environments with synchronization contexts (such as UI frameworks)&lt;/p&gt;

&lt;p&gt;Thread starvation, threads remain blocked instead of serving other requests&lt;/p&gt;

&lt;p&gt;Performance issues, the application handles fewer concurrent operations&lt;/p&gt;

&lt;p&gt;Golden Rule&lt;/p&gt;

&lt;p&gt;Async all the way down&lt;/p&gt;

&lt;p&gt;Once a method becomes asynchronous, the entire call chain should remain asynchronous.&lt;/p&gt;

&lt;p&gt;In practice, this means:&lt;/p&gt;

&lt;p&gt;var result = await GetDataAsync();&lt;/p&gt;

&lt;p&gt;instead of forcing the async code to behave synchronously.&lt;/p&gt;

&lt;p&gt;Following this rule ensures that threads are not blocked and that your application can fully benefit from asynchronous execution.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Sequential vs Parallel Async&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A key difference between mid-level and senior developers is parallel async execution.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sequential&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;var user = await GetUserAsync();
var orders = await GetOrdersAsync();
var payments = await GetPaymentsAsync();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each operation waits for the previous one.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Parallel&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;
var userTask = GetUserAsync();
var ordersTask = GetOrdersAsync();
var paymentsTask = GetPaymentsAsync();

await Task.WhenAll(userTask, ordersTask, paymentsTask);

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

&lt;/div&gt;



&lt;p&gt;Now everything runs concurrently.&lt;/p&gt;

&lt;p&gt;In microservices architectures this can reduce response time from 900ms to 300ms.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Another Common Mistake: async void&lt;/strong&gt;&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public async void Save()
{
    await SaveToDatabase();
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public async Task Save()
{
    await SaveToDatabase();
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;async void means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;you can't await it&lt;/li&gt;
&lt;li&gt;you can't catch exceptions&lt;/li&gt;
&lt;li&gt;It should only be used for event handlers.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Cancellation Tokens (Production Must-Have)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Real production systems need cancellation support.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public async Task&amp;lt;string&amp;gt; DownloadAsync(
    string url,
    CancellationToken token)
{
    var client = new HttpClient();
    return await client.GetStringAsync(url, token);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why it matters:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;aborted HTTP requests&lt;/li&gt;
&lt;li&gt;timeouts&lt;/li&gt;
&lt;li&gt;graceful shutdowns&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;A Tip That Separates Senior Developers&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Don't write async code just because you can.&lt;/p&gt;

&lt;p&gt;Use async when you have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I/O operations&lt;/li&gt;
&lt;li&gt;network calls&lt;/li&gt;
&lt;li&gt;database queries&lt;/li&gt;
&lt;li&gt;file access&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For CPU-bound work, async often doesn't help.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;A Simple Mental Model&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Every time you write async code ask yourself:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Is this I/O bound?&lt;/li&gt;
&lt;li&gt;Am I blocking a thread?&lt;/li&gt;
&lt;li&gt;Can operations run in parallel?&lt;/li&gt;
&lt;li&gt;Is cancellation supported?&lt;/li&gt;
&lt;li&gt;Will this scale in production?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is how senior engineers think.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Final Thoughts&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;async/await is one of the most important concepts in modern .NET development.&lt;/p&gt;

&lt;p&gt;It is not just syntax.&lt;/p&gt;

&lt;p&gt;It is an architectural tool.&lt;/p&gt;

&lt;p&gt;Senior developers don't just write async code.&lt;/p&gt;

&lt;p&gt;They design systems that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;leverage asynchronous execution&lt;/li&gt;
&lt;li&gt;avoid concurrency pitfalls&lt;/li&gt;
&lt;li&gt;scale under real production load&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And that difference becomes obvious the moment your system starts receiving real traffic.&lt;/p&gt;




&lt;p&gt;&lt;a href="mailto:nikosstit@gmail.com"&gt;nikosstit@gmail.com&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
