<?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>Future: nikosst</title>
    <description>The latest articles on Future by nikosst (@__b63657).</description>
    <link>https://future.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>Future: nikosst</title>
      <link>https://future.forem.com/__b63657</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://future.forem.com/feed/__b63657"/>
    <language>en</language>
    <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://future.forem.com/__b63657/astracking-vs-asnotracking-sto-entity-framework-core-pleres-analuse-me-paradeigmata-42oa</link>
      <guid>https://future.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;Σε αυτό το άρθρο θα δούμε τι κάνουν τα &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;ul&gt;
&lt;li&gt;Φέρνεις δεδομένα από τη βάση (δημιουργούνται objects στη μνήμη)&lt;/li&gt;
&lt;li&gt;Το EF κρατάει snapshot της αρχικής κατάστασης&lt;/li&gt;
&lt;li&gt;Τροποποιείς τα objects στη μνήμη&lt;/li&gt;
&lt;li&gt;Το EF εντοπίζει τις αλλαγές συγκρίνοντας τιμές στη μνήμη&lt;/li&gt;
&lt;li&gt;Με SaveChanges() στέλνει τις αλλαγές στη βάση&lt;/li&gt;
&lt;/ul&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 δουλεύει με ένα “snapshot” της βάσης μέσα στη μνήμη και παρακολουθεί αλλαγές πάνω σε αυτό, όχι απευθείας στη βάση δεδομένων.&lt;/p&gt;



&lt;p&gt;&lt;strong&gt;Τι κάνει το AsTracking();&lt;/strong&gt;&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;Το EF Core παρακολουθεί το entity&lt;/li&gt;
&lt;li&gt;Εντοπίζει την αλλαγή στο Name&lt;/li&gt;
&lt;li&gt;Εκτελεί αυτόματα 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;Το κρίσιμο σημείο είναι ότι το &lt;strong&gt;Entity Framework&lt;/strong&gt; δεν γνωρίζει ότι έγινε αυτή η αλλαγή, γιατί δεν παρακολουθεί το συγκεκριμένο object. Δεν έχει καταγράψει την αρχική του κατάσταση, ούτε παρακολουθεί τις μεταβολές του.&lt;/p&gt;

&lt;p&gt;Όταν στη συνέχεια καλείται η SaveChanges(), το EF ελέγχει ποια entities έχουν αλλαγές. Επειδή όμως δεν υπάρχει κανένα tracked entity, θεωρεί ότι δεν υπάρχει τίποτα προς αποθήκευση και έτσι δεν εκτελεί κανένα UPDATE query στη βάση.&lt;/p&gt;

&lt;p&gt;Με απλά λόγια: η αλλαγή έγινε στη μνήμη, αλλά το EF δεν την “είδε” ποτέ, άρα δεν μπορεί να τη μεταφέρει στη βάση δεδομένων.&lt;/p&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;&lt;strong&gt;Τι είναι το Identity Resolution&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Το Identity Resolution είναι ένας μηχανισμός του Entity Framework Core που εξασφαλίζει ότι κάθε εγγραφή (record) από τη βάση δεδομένων αντιστοιχεί σε ένα και μοναδικό object instance στη μνήμη κατά την εκτέλεση ενός query. Με άλλα λόγια, αν το ίδιο entity εμφανιστεί πολλές φορές μέσα στο αποτέλεσμα — κάτι πολύ συνηθισμένο σε queries με JOIN ή Include — το EF Core δεν δημιουργεί νέα αντικείμενα κάθε φορά, αλλά επαναχρησιμοποιεί το ίδιο instance.&lt;/p&gt;

&lt;p&gt;Αυτό έχει μεγάλη σημασία για τη συνέπεια των δεδομένων στη μνήμη. Επιτρέπει σωστή σύγκριση με reference equality (π.χ. ReferenceEquals), αποτρέπει duplicates και διασφαλίζει ότι οποιαδήποτε αλλαγή σε ένα object αντανακλάται παντού όπου αυτό χρησιμοποιείται μέσα στο ίδιο result set.&lt;/p&gt;

&lt;p&gt;Χωρίς Identity Resolution, το ίδιο entity μπορεί να φορτωθεί πολλές φορές ως διαφορετικά objects, κάτι που μπορεί να οδηγήσει σε subtle bugs, αυξημένη κατανάλωση μνήμης και απρόβλεπτη συμπεριφορά σε mapping, serialization ή business logic.&lt;/p&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;Έχεις complex graphs (π.χ. Orders → Customers → Addresses)&lt;/li&gt;
&lt;li&gt;Χρησιμοποιείς Include&lt;/li&gt;
&lt;li&gt;Δεν θέλεις tracking αλλά:&lt;/li&gt;
&lt;li&gt;Θέλεις consistency στα object references&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;Κανόνας στην πράξη&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CRUD → AsTracking()&lt;/li&gt;
&lt;li&gt;Read simple → AsNoTracking()&lt;/li&gt;
&lt;li&gt;Read complex graph → AsNoTrackingWithIdentityResolution()&lt;/li&gt;
&lt;/ul&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;&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://future.forem.com/__b63657/specification-pattern-upo-to-prisma-tou-solid-kai-tes-clean-architecture-8oh</link>
      <guid>https://future.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://future.forem.com/__b63657/asynchronous-programming-sten-c-themeliodeis-arkhes-kanones-kai-bathia-katanoese-2nd5</link>
      <guid>https://future.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://future.forem.com/__b63657/helper-classes-vs-extension-methods-3f8f</link>
      <guid>https://future.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://future.forem.com/__b63657/apo-static-classes-se-services-me-dependency-injection-me-optike-clean-architecture-layers--cf7</link>
      <guid>https://future.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://future.forem.com/__b63657/a-simple-clean-architecture-structure-for-aspnet-core-projects-33g1</link>
      <guid>https://future.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://future.forem.com/__b63657/middleware-sto-aspnet-core-c-55b9</link>
      <guid>https://future.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://future.forem.com/__b63657/async-await-task-kai-thread-pool-sto-c-mia-pleres-alla-aple-exegese-2k71</link>
      <guid>https://future.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://future.forem.com/__b63657/asyncawait-in-c-the-feature-you-think-you-understand-but-probably-use-wrong-4963</link>
      <guid>https://future.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>
    <item>
      <title>Το Ταξίδι του JWT Όταν η Εμπιστοσύνη Γίνεται Token</title>
      <dc:creator>nikosst</dc:creator>
      <pubDate>Sun, 08 Mar 2026 16:44:37 +0000</pubDate>
      <link>https://future.forem.com/__b63657/to-taxidi-tou-jwt-otan-e-empistosune-ginetai-token-3e0g</link>
      <guid>https://future.forem.com/__b63657/to-taxidi-tou-jwt-otan-e-empistosune-ginetai-token-3e0g</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%2Fzypziemyqgvy29edjifi.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%2Fzypziemyqgvy29edjifi.png" alt=" " width="800" height="1200"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;Η διαχείριση της σύνδεσης των χρηστών σε εφαρμογές υπήρξε πάντα μια δύσκολη υπόθεση. Παραδοσιακά, χρησιμοποιούνται cookies, sessions και διάφορες μορφές state management για να θυμάται ο server ποιος είναι συνδεδεμένος. Τα cookies αποθηκεύουν μικρά δεδομένα στον browser του χρήστη και στέλνονται αυτόματα με κάθε αίτημα. Τα sessions κρατούν πληροφορίες στη μνήμη ή στη βάση του server, συνδέοντας κάθε χρήστη με ένα μοναδικό session ID. Το state management βοηθά την εφαρμογή να θυμάται σε ποιο σημείο βρίσκεται ο χρήστης, αλλά η συντήρηση όλων αυτών των μηχανισμών μπορεί να γίνει σύνθετη και εύθραυστη, ειδικά όταν υπάρχουν πολλά endpoints, microservices ή περιβάλλοντα που αλλάζουν συχνά.&lt;/p&gt;

&lt;p&gt;Το JWT (JSON Web Token) ήρθε να απλοποιήσει αυτή τη διαδικασία, προσφέροντας έναν διαφορετικό τρόπο να εκφράζεται η εμπιστοσύνη ανάμεσα σε client και server. Αντί ο server να χρειάζεται να “θυμάται” κάθε χρήστη, εκδίδει ένα token που περιέχει όλες τις απαραίτητες πληροφορίες μέσα του, με τρόπο ασφαλή και επαληθεύσιμο.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Το σώμα του token και πώς λειτουργεί&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Κάθε JWT αποτελείται από τρία μέρη, το Header, το Payload και την Signature. Το Header δηλώνει τον τύπο του token και τον αλγόριθμο υπογραφής που χρησιμοποιείται. Το Payload περιέχει τα claims, δηλαδή τα δεδομένα που περιγράφουν την ταυτότητα και τα δικαιώματα του χρήστη, για παράδειγμα ποιος είναι ο χρήστης (Subject), ποιος εξέδωσε το token (Issuer), ποιοι είναι οι παραλήπτες του (Audience), πότε δημιουργήθηκε (Issued At) και πότε λήγει (Expiration Time). Η Signature είναι η ψηφιακή υπογραφή που εξασφαλίζει ότι το token δεν έχει τροποποιηθεί μετά την έκδοσή του.&lt;/p&gt;

&lt;p&gt;Όταν ο χρήστης κάνει login, ο server επαληθεύει τα στοιχεία του, όπως όνομα χρήστη και κωδικό πρόσβασης. Αν αυτά είναι σωστά, δημιουργεί το JWT, παίρνει τα δεδομένα που θέλει να ενσωματώσει στο Payload, τα κωδικοποιεί σε Base64URL, προσθέτει το Header και στη συνέχεια υπογράφει το αποτέλεσμα με ένα μυστικό ή ιδιωτικό κλειδί. Το αποτέλεσμα είναι ένα ενιαίο string που μοιάζει κάπως έτσι:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNjg4MDAwMDAwfQ
.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Αυτό το token αποστέλλεται πίσω στον client, ο οποίος το αποθηκεύει — συνήθως στη μνήμη της εφαρμογής ή σε ένα ασφαλές cookie. Από εκεί και πέρα, κάθε φορά που ο client θέλει να καλέσει ένα προστατευμένο endpoint, στέλνει το token στο HTTP header με τη μορφή Authorization: Bearer . Η λέξη Bearer σημαίνει “κάτοχος” δηλαδή όποιος κρατάει αυτό το token, θεωρείται ότι έχει δικαίωμα να το χρησιμοποιήσει. Για αυτόν τον λόγο είναι κρίσιμο να προστατεύεται από διαρροές, καθώς δίνει πρόσβαση στο σύστημα όσο παραμένει έγκυρο.&lt;/p&gt;

&lt;p&gt;Όταν ο server λάβει ένα αίτημα με ένα JWT (JSON Web Token), μπορεί να το επαληθεύσει χωρίς να χρειάζεται να κοιτάξει σε βάση ή cache. Αυτό σημαίνει ότι δεν κρατά καμία αποθηκευμένη πληροφορία για το ποιος είναι συνδεδεμένος όλες οι απαραίτητες πληροφορίες βρίσκονται ήδη μέσα στο token, όπως το user ID, οι ρόλοι του χρήστη και η ημερομηνία λήξης του token.&lt;/p&gt;

&lt;p&gt;Το μυστικό ή το δημόσιο κλειδί που χρειάζεται για να επαληθευτεί η υπογραφή του token πρέπει να είναι ήδη γνωστό στον server. Αν χρησιμοποιούμε symmetric signing (π.χ. HS256), το server γνωρίζει το secret key. Αν χρησιμοποιούμε asymmetric signing (π.χ. RS256, που είναι το συνηθισμένο με Auth0), ο server γνωρίζει το public key που παρέχει ο πάροχος ταυτότητας (όπως το Auth0). Με αυτόν τον τρόπο, ο server μπορεί να ελέγξει ότι το token δεν έχει αλλοιωθεί και ότι είναι έγκυρο, χωρίς να χρειάζεται να ανατρέξει στη βάση δεδομένων για να βρει τον χρήστη.&lt;/p&gt;

&lt;p&gt;Αν οι έλεγχοι υπογραφής και ημερομηνίας λήξης περάσουν, το αίτημα γίνεται αποδεκτό. Αυτή η λογική ονομάζεται stateless authentication, επειδή δεν απαιτεί καμία αποθήκευση κατάστασης στον server — όλα τα στοιχεία για την ταυτοποίηση του χρήστη περιέχονται ήδη μέσα στο token.&lt;/p&gt;

&lt;p&gt;Η προσέγγιση αυτή ταιριάζει ιδανικά σε περιβάλλοντα serverless, όπου δεν υπάρχει μόνιμος server που τρέχει και διατηρεί μνήμη. Σε ένα serverless περιβάλλον, κάθε αίτημα μπορεί να εξυπηρετείται από μια ανεξάρτητη συνάρτηση ή μικροϋπηρεσία που ενεργοποιείται προσωρινά. Το JWT λειτουργεί σαν διαβατήριο, η συνάρτηση μπορεί να επαληθεύσει το token επιτόπου, χωρίς να χρειάζεται να συνδεθεί σε βάση ή να “θυμηθεί” προηγούμενες συνεδρίες.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Η λεπτή ισορροπία ασφάλειας&lt;/strong&gt;&lt;/p&gt;

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

&lt;p&gt;Επιπλέον, τα access tokens πρέπει να έχουν μικρή διάρκεια ζωής. Όταν λήγουν, χρησιμοποιούνται refresh tokens για την έκδοση νέων. Τα refresh tokens έχουν μεγαλύτερη διάρκεια, αλλά πρέπει να φυλάσσονται με αυστηρότητα και να ανανεώνονται περιοδικά, μια διαδικασία που ονομάζεται token rotation. Αυτή η πρακτική μειώνει τον κίνδυνο επαναχρησιμοποίησης σε περίπτωση παραβίασης.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Από τα sessions στα tokens μια αλλαγή φιλοσοφίας&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Με τα sessions, η αυθεντικοποίηση βασίζεται στη διατήρηση κατάστασης στον server κάτι που γίνεται περίπλοκο όταν υπάρχουν πολλοί servers ή microservices. Με τα tokens, η ευθύνη μεταφέρεται στον client, ο χρήστης κουβαλάει μαζί του την ταυτότητά του, υπογεγραμμένη από τον server. Κάθε υπηρεσία που γνωρίζει το δημόσιο κλειδί του εκδότη μπορεί να επαληθεύσει το JWT χωρίς να εξαρτάται από κάποια κοινή βάση δεδομένων. Το αποτέλεσμα είναι ένα αποκεντρωμένο, καθαρό και επεκτάσιμο σύστημα που στηρίζεται στην κρυπτογραφική εμπιστοσύνη αντί στη μνήμη του server.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Το τέλος του ταξιδιού&lt;/strong&gt;&lt;/p&gt;

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

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

&lt;p&gt;Μπορούμε να το φανταστούμε σαν μια ψηφιακή ταυτότητα που κρατάς στα χέρια σου. Όπως ένας άνθρωπος δείχνει την ταυτότητά του για να εισέλθει σε ένα συνέδριο πληροφορικής, να αποδείξει την ηλικία του σε ένα μπαρ ή να περάσει από έναν έλεγχο ασφαλείας, έτσι και το JWT λειτουργεί ως απόδειξη ταυτότητας για τις εφαρμογές. Δεν χρειάζεται κάποιος να καλέσει μια αρχή για να επιβεβαιώσει την εγκυρότητα η υπογραφή πάνω του το αποδεικνύει. Το μόνο που μένει είναι να το δείξεις εκεί που χρειάζεται. Είναι η ίδια πράξη εμπιστοσύνης, απλώς σε ψηφιακή μορφή, μια ταυτότητα που δεν την κρατάει το κράτος, αλλά η εφαρμογή σου.&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>backend</category>
      <category>beginners</category>
      <category>security</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Πότε και πώς να μετατρέψεις ένα pattern σε NuGet Package</title>
      <dc:creator>nikosst</dc:creator>
      <pubDate>Sun, 08 Mar 2026 16:34:18 +0000</pubDate>
      <link>https://future.forem.com/__b63657/pote-kai-pos-na-metatrepseis-ena-pattern-se-nuget-package-2am7</link>
      <guid>https://future.forem.com/__b63657/pote-kai-pos-na-metatrepseis-ena-pattern-se-nuget-package-2am7</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%2Ftct8mbjqp0dgbwbwiz0t.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%2Ftct8mbjqp0dgbwbwiz0t.png" alt=" " width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;Στον κόσμο της ανάπτυξης λογισμικού, η επαναχρησιμοποίηση κώδικα δεν είναι απλώς καλή πρακτική, είναι απαραίτητη για να διατηρείς καθαρά, συντηρήσιμα και ευανάγνωστα projects. Κάθε φορά που ανακαλύπτεις ότι αντιγράφεις και επικολλάς κομμάτια κώδικα σε πολλά projects ή modules, υπάρχει ένδειξη ότι κάτι μπορεί να γίνει πιο DRY.&lt;br&gt;
Το DRY (Don't Repeat Yourself) είναι μια βασική αρχή που λέει ότι κάθε γνώση ή λειτουργικότητα πρέπει να έχει μία, μοναδική και authoritative αναπαράσταση μέσα στο σύστημα. Αν σπάσεις αυτήν την αρχή, δημιουργείς κώδικα δύσκολο στη συντήρηση, γεμάτο bugs και αντιφάσεις.&lt;/p&gt;



&lt;p&gt;&lt;strong&gt;Πότε χρειάζεται να φτιάξεις NuGet package&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Υπάρχουν αρκετές περιπτώσεις που η δημιουργία ενός NuGet package αξίζει&lt;br&gt;
επαναχρησιμοποίηση σε πολλά projects&lt;/p&gt;

&lt;p&gt;1.Αν ένα pattern, μια helper κλάση ή component χρησιμοποιείται σε περισσότερα από ένα project, η μετατροπή του σε package το καθιστά εύκολα προσβάσιμο.&lt;/p&gt;

&lt;p&gt;2.Καθαρή αρχιτεκτονική&lt;br&gt;
Διαχωρίζεις το core logic από τα projects που το χρησιμοποιούν. Αυτό είναι χρήσιμο για Clean Architecture ή layered architectures, όπου η επαναχρησιμοποίηση είναι κρίσιμη.&lt;/p&gt;

&lt;p&gt;3.Διαχείριση εκδόσεων&lt;br&gt;
Με NuGet μπορείς να ενημερώνεις μόνο το package αντί για κάθε project ξεχωριστά.&lt;/p&gt;

&lt;p&gt;4.Ομαδική δουλειά&lt;br&gt;
Κάθε μέλος της ομάδας μπορεί να χρησιμοποιεί το ίδιο package χωρίς να χρειάζεται να κατανοήσει κάθε λεπτομέρεια της υλοποίησης.&lt;/p&gt;



&lt;p&gt;&lt;strong&gt;Πότε να μην το κάνεις&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Αν ο κώδικας είναι πολύ μικρός ή μοναδικός για ένα project.&lt;/li&gt;
&lt;li&gt;Αν η βιβλιοθήκη έχει ισχυρές εξαρτήσεις από συγκεκριμένα projects.&lt;/li&gt;
&lt;li&gt;Αν το project δεν θα επαναχρησιμοποιηθεί, τότε το NuGet προσθέτει μόνο περιττή πολυπλοκότητα.&lt;/li&gt;
&lt;/ol&gt;



&lt;p&gt;&lt;strong&gt;Πώς προέκυψε η ιδέα&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Η ανάγκη για NuGet συνήθως εμφανίζεται όταν αντιλαμβάνεσαι ότι κάθε νέο project ή module επαναλαμβάνει την ίδια λογική.&lt;/p&gt;

&lt;p&gt;Για παράδειγμα, σε ένα school management system μπορεί να έχεις ένα pattern για διαχείριση JWT tokens, logging ή validation. Αν το αντιγράψεις σε κάθε project, δημιουργείται γρήγορα redundant κώδικας.&lt;/p&gt;

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



&lt;p&gt;&lt;strong&gt;Τι είναι και γιατί το DRY (Don't repeat your code) έχει σχήμα&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Το DRY δεν είναι μόνο θεωρία. Έχει σχήμα όταν το εφαρμόζεις:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Κάθε κομμάτι λογικής υπάρχει μόνο μία φορά.&lt;/li&gt;
&lt;li&gt;Όλοι οι developers ανατρέχουν στην ίδια πηγή.&lt;/li&gt;
&lt;li&gt;Οτιδήποτε αλλάξει, ενημερώνεται κεντρικά, χωρίς να χρειάζεται να ψάχνεις και να αλλάζεις σε 5 - 10 projects.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Το NuGet είναι ένα εργαλείο που «υλοποιεί» το DRY σε επίπεδο πολλαπλών projects, προσφέροντας έλεγχο εκδόσεων, αυτοματισμό και επαναχρησιμοποίηση.&lt;/p&gt;



&lt;p&gt;Βήματα για να μετατρέψεις ένα pattern σε NuGet&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1.Δημιουργία Class Library&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Δημιουργείς μια Class Library για να βάλεις όλο τον επαναχρησιμοποιήσιμο κώδικα.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dotnet new classlib -n MyPatternLibrary
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2.Υλοποίηση του pattern&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Γράφεις τις κλάσεις, interfaces και helpers με clear API και XML documentation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3.Προσθήκη μεταδεδομένων στο .csproj&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;&amp;lt;PropertyGroup&amp;gt;
    &amp;lt;PackageId&amp;gt;MyPatternLibrary&amp;lt;/PackageId&amp;gt;
    &amp;lt;Version&amp;gt;1.0.0&amp;lt;/Version&amp;gt;
    &amp;lt;Authors&amp;gt;ΌνομαΣου&amp;lt;/Authors&amp;gt;
    &amp;lt;Description&amp;gt;Library για επαναχρησιμοποίηση singleton pattern&amp;lt;/Description&amp;gt;
    &amp;lt;PackageTags&amp;gt;singleton;utility;pattern&amp;lt;/PackageTags&amp;gt;
&amp;lt;/PropertyGroup&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Το αρχείο .csproj ενός project που πρόκειται να μετατραπεί σε NuGet package περιέχει βασικές πληροφορίες που καθορίζουν την ταυτότητα και τη διανομή του πακέτου. Το πεδίο PackageId δηλώνει το μοναδικό όνομα του πακέτου στο NuGet, ενώ το Version ορίζει την τρέχουσα έκδοση για λόγους ενημερώσεων και συμβατότητας. Το Authors αναφέρει τον δημιουργό ή την ομάδα ανάπτυξης, το Description δίνει μια σύντομη περιγραφή της λειτουργίας του πακέτου και το PackageTags περιλαμβάνει λέξεις-κλειδιά που διευκολύνουν την αναζήτησή του στο NuGet Gallery. Όλα αυτά τα στοιχεία εμφανίζονται δημόσια όταν το πακέτο ανέβει στο nuget.org και συμβάλλουν στο να κατανοήσει ο χρήστης τι κάνει και πώς μπορεί να το χρησιμοποιήσει.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4.Build και τοπικός έλεγχος&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;dotnet pack -c Release
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Αφού ρυθμιστεί σωστά το .csproj, το επόμενο βήμα είναι η δημιουργία του πακέτου μέσω της εντολής dotnet pack, η οποία δημιουργεί ένα αρχείο .nupkg. Πριν δημοσιευθεί, είναι σημαντικό να γίνει τοπικός έλεγχος με την εντολή dotnet nuget locals ή εγκαθιστώντας το πακέτο σε ένα δοκιμαστικό project για να διαπιστωθεί ότι λειτουργεί σωστά. Έτσι διασφαλίζεται ότι το NuGet package είναι σταθερό και έτοιμο για διανομή χωρίς απρόοπτα.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5.Δημοσίευση στο NuGet&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Φτιάχνεις λογαριασμό στο nuget.org&lt;br&gt;
Παίρνεις API key&lt;br&gt;
Στέλνεις το package&lt;/p&gt;

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

&lt;p&gt;Δημιούργησε πρώτα έναν λογαριασμό στο &lt;a href="https://www.nuget.org/" rel="noopener noreferrer"&gt;https://www.nuget.org/&lt;/a&gt; και έπειτα δημιούργησε ένα API Key από το προφίλ σου (θα χρειαστεί για να πιστοποιήσει το ανέβασμα). Στη συνέχεια, εκτέλεσε την εντολή&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dotnet nuget push MyPatternLibrary.1.0.0.nupkg --api-key &amp;lt;API_KEY&amp;gt; --source https://api.nuget.org/v3/index.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;&lt;strong&gt;6.Διαχείριση εκδόσεων και updates&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Κράτα semantic versioning: MAJOR.MINOR.PATCH. Κράτα CHANGELOG και ενημέρωνε το package μόνο όταν αλλάζει κάτι σημαντικό.&lt;/p&gt;

&lt;p&gt;Η σωστή διαχείριση εκδόσεων είναι κρίσιμη για τη συντήρηση ενός αξιόπιστου NuGet package. Χρησιμοποίησε semantic versioning (MAJOR.MINOR.PATCH) ώστε οι χρήστες να γνωρίζουν πότε υπάρχουν σημαντικές αλλαγές ή διορθώσεις. &lt;/p&gt;

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




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

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




&lt;p&gt;&lt;a href="https://learn.microsoft.com/en-us/nuget/quickstart/create-and-publish-a-package-using-visual-studio?tabs=netcore-cli" rel="noopener noreferrer"&gt;Create and publish a NuGet package using Visual Studio (Windows only)&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>
      <category>codequality</category>
      <category>dotnet</category>
      <category>softwaredevelopment</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>FluentValidation σε Clean Architecture</title>
      <dc:creator>nikosst</dc:creator>
      <pubDate>Wed, 04 Mar 2026 16:15:54 +0000</pubDate>
      <link>https://future.forem.com/__b63657/fluentvalidation-se-clean-architecture-4koh</link>
      <guid>https://future.forem.com/__b63657/fluentvalidation-se-clean-architecture-4koh</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%2F0zkuyz0l6q7n31rrgblx.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%2F0zkuyz0l6q7n31rrgblx.png" alt=" " width="800" height="1200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Πώς στήνουμε σωστά τα validations και γιατί δεν πρέπει να βάζουμε business logic στους validators&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Σε πολλές εφαρμογές που χρησιμοποιούν Clean Architecture μαζί με MediatR και FluentValidation, εμφανίζεται συχνά ένα συγκεκριμένο αρχιτεκτονικό λάθος. Οι validators αρχίζουν να περιέχουν μεγάλο μέρος της επιχειρησιακής λογικής της εφαρμογής, γεμίζουν με if συνθήκες, permission checks ή ακόμη και κλήσεις σε services και repositories.&lt;/p&gt;

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




&lt;p&gt;&lt;strong&gt;Ο ρόλος του validation στην Clean Architecture&lt;/strong&gt;&lt;/p&gt;

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

&lt;p&gt;Το πρώτο επίπεδο αφορά το validation της μορφής του request. Το δεύτερο επίπεδο αφορά τους επιχειρησιακούς κανόνες της εφαρμογής. Το τρίτο επίπεδο αφορά τους κανόνες ακεραιότητας του domain model.&lt;/p&gt;

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




&lt;p&gt;&lt;strong&gt;Input validation με FluentValidation&lt;/strong&gt;&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%2Flskvq651d6bxmfbkcs88.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%2Flskvq651d6bxmfbkcs88.png" alt=" " width="513" height="260"&gt;&lt;/a&gt;&lt;/p&gt;

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

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

&lt;p&gt;Ένας τυπικός validator μπορεί να μοιάζει με τον παρακάτω:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class SaveCartItemCommandValidator 
    : AbstractValidator&amp;lt;SaveCartItemCommand&amp;gt;
{
    public SaveCartItemCommandValidator()
    {
        RuleFor(x =&amp;gt; x.EmployeeId)
            .NotEmpty();

        RuleFor(x =&amp;gt; x.Actions)
            .NotEmpty();

        RuleForEach(x =&amp;gt; x.Actions)
            .ChildRules(action =&amp;gt;
            {
                action.RuleFor(x =&amp;gt; x.ItemId)
                    .NotEmpty();

                action.RuleFor(x =&amp;gt; x.Quantity)
                    .GreaterThan(0);
            });
    }
}

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

&lt;/div&gt;



&lt;p&gt;Ο validator εδώ ελέγχει μόνο τη δομή του request. Δεν γνωρίζει τίποτα για permissions, business rules ή κατάσταση της βάσης δεδομένων.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Application validation και business rules&lt;/strong&gt;&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%2F4zxdqsrrz6q608xgl6lx.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%2F4zxdqsrrz6q608xgl6lx.png" alt=" " width="487" height="232"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Αφού περάσει το input validation, η εφαρμογή προχωρά στον έλεγχο των επιχειρησιακών κανόνων. Αυτή η λογική ανήκει στο Application Layer, συνήθως μέσα στον handler ή σε services που καλεί ο handler.&lt;/p&gt;

&lt;p&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 plaintext"&gt;&lt;code&gt;var employee = await employeeService.GetEmployeeAsync(request.EmployeeId);

if (!employee.CanReceiveItems())
{
    return Result.Fail("Employee cannot receive items");
}

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

&lt;/div&gt;



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




&lt;p&gt;&lt;strong&gt;Domain validation&lt;/strong&gt;&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%2Fl25kfblm8mcgz3100dok.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%2Fl25kfblm8mcgz3100dok.png" alt=" " width="483" height="208"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Το τρίτο επίπεδο βρίσκεται στο Domain Layer. Εκεί βρίσκονται οι κανόνες που προστατεύουν την ακεραιότητα των entities του συστήματος.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public void AssignItem(Item item)
{
    if(IsTerminated)
        throw new DomainException("Employee is inactive");

    _items.Add(item);
}

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

&lt;/div&gt;



&lt;p&gt;Ακόμα και αν η εφαρμογή παραλείψει κάποιον έλεγχο στο application layer, το domain model εξακολουθεί να προστατεύει την κατάσταση του συστήματος.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Γιατί δεν πρέπει να βάζουμε business logic στους validators&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Όταν ένας validator αρχίζει να περιέχει επιχειρησιακή λογική, παραβιάζεται ο βασικός κανόνας της Clean Architecture: κάθε component πρέπει να έχει μια σαφή ευθύνη.&lt;/p&gt;

&lt;p&gt;Ο validator πρέπει να γνωρίζει μόνο τη μορφή του request. Αν αρχίσει να καλεί services, repositories ή να ελέγχει permissions, τότε αποκτά εξαρτήσεις που δεν θα έπρεπε να έχει. Αυτό οδηγεί σε coupling ανάμεσα στα layers και κάνει την αρχιτεκτονική πιο δύσκολη στη συντήρηση.&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;RuleFor(x =&amp;gt; x)
    .MustAsync(async (request, ct) =&amp;gt;
    {
        var employee = await employeeService.GetEmployeeAsync(request.EmployeeId);

        if(employee.IsTerminated)
            return false;

        if(employee.Role == "Manager")
            return true;

        return false;
    });

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

&lt;/div&gt;



&lt;p&gt;Σε αυτή την περίπτωση ο validator αρχίζει να εκτελεί business logic που κανονικά θα έπρεπε να βρίσκεται στον handler ή σε κάποιο application service.&lt;/p&gt;

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




&lt;p&gt;&lt;strong&gt;Πώς διαχωρίζουμε σωστά τα validations&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Η βασική αρχή είναι απλή. Το FluentValidation ελέγχει μόνο αν το request είναι σωστά διαμορφωμένο. Οι επιχειρησιακοί κανόνες εφαρμόζονται στο application layer, ενώ οι κανόνες ακεραιότητας προστατεύονται από το domain model.&lt;/p&gt;

&lt;p&gt;Με αυτό τον διαχωρισμό κάθε κομμάτι της εφαρμογής έχει ξεκάθαρη ευθύνη. Οι validators παραμένουν μικροί και ευανάγνωστοι, οι handlers περιέχουν την επιχειρησιακή λογική και το domain model προστατεύει την κατάσταση των entities.&lt;/p&gt;

&lt;p&gt;Έτσι η εφαρμογή παραμένει ευέλικτη, εύκολη στη συντήρηση και ευκολότερη στον έλεγχο μέσω tests.&lt;/p&gt;




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

&lt;p&gt;Το FluentValidation είναι ένα εξαιρετικό εργαλείο για τον έλεγχο της μορφής των requests, αλλά δεν πρέπει να χρησιμοποιείται για επιχειρησιακή λογική. Όταν οι validators γεμίζουν με if συνθήκες, database calls ή permission checks, αυτό είναι συνήθως ένδειξη ότι η αρχιτεκτονική έχει αρχίσει να εκτρέπεται από τις αρχές της Clean Architecture.&lt;/p&gt;

&lt;p&gt;Η σωστή προσέγγιση είναι να κρατάμε το FluentValidation για input validation, να τοποθετούμε τους επιχειρησιακούς κανόνες στο application layer και να αφήνουμε το domain model να προστατεύει την ακεραιότητα των entities.&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>codequality</category>
      <category>csharp</category>
      <category>dotnet</category>
    </item>
  </channel>
</rss>
