<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: nikosst</title>
    <description>The latest articles on DEV Community by nikosst (@__b63657).</description>
    <link>https://dev.to/__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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2710936%2F20075e34-3f33-44d9-a08b-c1703ff98457.jpg</url>
      <title>DEV Community: nikosst</title>
      <link>https://dev.to/__b63657</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/__b63657"/>
    <language>en</language>
    <item>
      <title>Πλήρης αρχιτεκτονική JWT, Middleware, Authorization, Permissions και Cache σε .NET</title>
      <dc:creator>nikosst</dc:creator>
      <pubDate>Fri, 12 Jun 2026 13:34:24 +0000</pubDate>
      <link>https://dev.to/__b63657/pleres-arkhitektonike-jwt-middleware-authorization-permissions-kai-cache-se-net-1c05</link>
      <guid>https://dev.to/__b63657/pleres-arkhitektonike-jwt-middleware-authorization-permissions-kai-cache-se-net-1c05</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%2Fmm5roxuovsbwfzbiwgnr.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%2Fmm5roxuovsbwfzbiwgnr.png" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Σε ένα σύγχρονο ASP.NET Core backend, το authentication και το authorization δεν είναι μία ενιαία διαδικασία, ούτε ένα σημείο κώδικα που “ανεβάζεις και τελείωσες”. Είναι μια ροή από στάδια, όπου κάθε στάδιο έχει διαφορετική ευθύνη και κανένα δεν πρέπει να μπερδεύεται με το άλλο.&lt;/p&gt;

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




&lt;p&gt;&lt;strong&gt;Το πρώτο σημείο: Middleware και JWT validation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Κάθε request που έρχεται στο API δεν φτάνει ποτέ απευθείας στον controller. Περνάει πρώτα από το middleware pipeline της ASP.NET Core.&lt;/p&gt;

&lt;p&gt;Εκεί βρίσκεται το authentication middleware, το οποίο είναι υπεύθυνο να ελέγξει το JWT token πριν συμβεί οτιδήποτε άλλο.&lt;/p&gt;

&lt;p&gt;Σε αυτό το σημείο γίνεται ο έλεγχος της υπογραφής του token, του issuer, του audience και κυρίως του χρόνου ζωής του. Αν το token έχει λήξει, το request απορρίπτεται άμεσα και επιστρέφεται 401 Unauthorized χωρίς να εκτελεστεί τίποτα άλλο.&lt;/p&gt;

&lt;p&gt;Αυτό σημαίνει πρακτικά ότι ο χρήστης “κόβεται” πριν φτάσει σε business logic, πριν φτάσει σε authorization, πριν γίνει οποιοδήποτε database call. Το middleware λειτουργεί σαν απόλυτο security gate.&lt;/p&gt;

&lt;p&gt;Αν το token είναι έγκυρο, τότε δημιουργείται το HttpContext.User και το request συνεχίζει τη διαδρομή του μέσα στο pipeline.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Τι σημαίνει στην πράξη η λήξη του JWT&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Η λήξη του JWT δεν είναι κάτι που το χειρίζεται η εφαρμογή ως business logic. Είναι καθαρά security rule.&lt;/p&gt;

&lt;p&gt;Όταν ο χρόνος exp περάσει, το middleware απλά απορρίπτει το request. Δεν υπάρχει session, δεν υπάρχει “logout server side”, δεν υπάρχει κάποια επιπλέον διαδικασία. Το token θεωρείται άκυρο και τελείωσε εκεί η διαδρομή.&lt;/p&gt;

&lt;p&gt;Ο client είναι αυτός που πρέπει να αντιδράσει σε αυτή την κατάσταση, είτε κάνοντας refresh token call είτε ζητώντας login ξανά.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Μετά το middleware: αρχίζει το authorization&lt;/strong&gt;&lt;/p&gt;

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

&lt;p&gt;Εδώ ξεκινά το authorization κομμάτι, το οποίο δεν είναι ένα απλό if statement, ούτε ένα δεύτερο middleware που “τρέχει μόνο του”. Είναι ένα policy-based σύστημα που ενεργοποιείται όταν ένα endpoint απαιτεί συγκεκριμένο δικαίωμα.&lt;/p&gt;

&lt;p&gt;Για παράδειγμα, όταν ένα endpoint έχει attribute όπως “Orders.Read”, δεν σημαίνει ότι το framework απλά κοιτάει roles. Σημαίνει ότι ενεργοποιείται μια διαδικασία ελέγχου που θα αποφασίσει δυναμικά αν ο συγκεκριμένος χρήστης μπορεί να εκτελέσει αυτή την ενέργεια.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Πώς υλοποιείται στην πράξη το authorization&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Στην πράξη, το authorization εκφράζεται μέσω policies και handlers. Το endpoint δηλώνει τι απαιτείται, και ο handler αποφασίζει αν ο χρήστης το πληροί.&lt;/p&gt;

&lt;p&gt;Όταν ένα request φτάνει σε ένα endpoint με policy, η ASP.NET ενεργοποιεί τον αντίστοιχο authorization handler. Εκεί γίνεται η πραγματική αξιολόγηση.&lt;/p&gt;

&lt;p&gt;Ο handler δεν παίρνει την απόφαση από μόνος του. Ζητάει δεδομένα από ένα service, το PermissionService, το οποίο είναι υπεύθυνο να βρει τα δικαιώματα του χρήστη.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Πού αποθηκεύονται τα permissions&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Τα permissions δεν πρέπει να βρίσκονται μέσα στο JWT. Ο λόγος είναι ότι αλλάζουν συχνά, πρέπει να μπορούν να ανακληθούν και δεν πρέπει να “παγώνουν” μέσα σε ένα token.&lt;/p&gt;

&lt;p&gt;Αντίθετα, αποθηκεύονται σε βάση δεδομένων σε κανονικό relational μοντέλο.&lt;/p&gt;

&lt;p&gt;Υπάρχουν users, roles και permissions. Οι χρήστες συνδέονται με ρόλους και οι ρόλοι συνδέονται με permissions. Έτσι το σύστημα γίνεται δυναμικό και μπορεί να αλλάξει ανά πάσα στιγμή χωρίς να χρειάζεται reissue όλων των tokens.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;PermissionService: το σημείο που διαβάζονται τα δικαιώματα&lt;/strong&gt;&lt;/p&gt;

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

&lt;p&gt;Όταν καλείται, κάνει join μεταξύ users, roles και permissions και επιστρέφει τη λίστα των δικαιωμάτων.&lt;/p&gt;

&lt;p&gt;Αυτό το κομμάτι είναι business data access layer και όχι security logic. Η απόφαση για το αν επιτρέπεται μια ενέργεια γίνεται αλλού, όχι εδώ.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Cache γιατί υπάρχει και πού μπαίνει&lt;/strong&gt;&lt;/p&gt;

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

&lt;p&gt;Γι’ αυτό μπαίνει cache.&lt;/p&gt;

&lt;p&gt;Η cache δεν αλλάζει τη λογική του συστήματος. Δεν αποφασίζει τίποτα. Απλώς αποθηκεύει προσωρινά το αποτέλεσμα ενός προηγούμενου query ώστε να μην χρειάζεται να επαναληφθεί.&lt;/p&gt;

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

&lt;p&gt;Έτσι το πρώτο request είναι πιο “βαρύ”, αλλά όλα τα επόμενα είναι σχεδόν άμεσα.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Όλη η ροή μαζί&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Όταν όλα τα κομμάτια συνδεθούν μεταξύ τους, η ροή του request είναι καθαρή.&lt;/p&gt;

&lt;p&gt;Το request έρχεται στο σύστημα και περνάει πρώτα από το middleware. Εκεί γίνεται ο έλεγχος του JWT. Αν το token έχει λήξει, το request σταματάει αμέσως. Αν είναι valid, δημιουργείται το user context και το request συνεχίζει.&lt;/p&gt;

&lt;p&gt;Στη συνέχεια ενεργοποιείται το authorization layer, το οποίο βλέπει ότι το συγκεκριμένο endpoint απαιτεί συγκεκριμένα permissions. Ο authorization handler καλείται και ζητάει τα permissions του χρήστη από το PermissionService.&lt;/p&gt;

&lt;p&gt;Το PermissionService πρώτα κοιτάει στην cache. Αν τα δεδομένα υπάρχουν εκεί, τα επιστρέφει άμεσα. Αν όχι, κάνει query στη βάση δεδομένων, τα αποθηκεύει στην cache και τα επιστρέφει.&lt;/p&gt;

&lt;p&gt;Αν ο χρήστης έχει το απαιτούμενο permission, το request συνεχίζει στον controller και από εκεί μπορεί να καλέσει άλλα microservices ή να εκτελέσει business logic.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Γιατί αυτό δεν γίνεται bottleneck&lt;/strong&gt;&lt;/p&gt;

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

&lt;p&gt;Η cache απορροφά τη συντριπτική πλειοψηφία των requests, οπότε η βάση χρησιμοποιείται μόνο σε cache miss ή όταν αλλάξουν τα permissions.&lt;/p&gt;

&lt;p&gt;Αυτό σημαίνει ότι το authorization δεν είναι hot path προς τη βάση. Είναι hot path προς τη cache, η οποία είναι εξαιρετικά γρήγορη.&lt;/p&gt;

&lt;p&gt;Έτσι το σύστημα παραμένει σταθερό ακόμα και υπό μεγάλο φορτίο.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Διάγραμμα&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌───────────────────────────────────────────────────────────┐
│                       Client / SPA                        │
└───────────────────────┬───────────────────────────────────┘
                        │
                        ▼
┌───────────────────────────────────────────────────────────┐
│                     HTTP Request                          │
│                  Authorization: Bearer JWT                │
└───────────────────────┬───────────────────────────────────┘
                        │
                        ▼
┌───────────────────────────────────────────────────────────┐
│                 Authentication Middleware                 │
│                                                           │
│  Validate JWT Signature                                   │
│  Validate Issuer / Audience                               │
│  Validate Expiration (exp)                                │
└───────────────────────┬───────────────────────────────────┘
                        │
          ┌─────────────┴─────────────┐
          │                           │
          ▼                           ▼
     Token Invalid               Token Valid
          │                           │
          ▼                           ▼
   401 Unauthorized          HttpContext.User Created
          │                           │
          │                           ▼
          │             ┌──────────────────────────────┐
          │             │    Token Revocation Check    │
          │             │                              │
          │             │ Redis / Cache / DB Lookup    │
          │             └──────────────┬───────────────┘
          │                            │
          │              ┌─────────────┴────────────┐
          │              │                          │
          │              ▼                          ▼
          │        Revoked                    Active
          │              │                          │
          │              ▼                          ▼
          │       401 Unauthorized        Authorization Layer
          │                                         │
          │                                         ▼
          │                     ┌────────────────────────────┐
          │                     │    Policy / Handler        │
          │                     │  Orders.Read Permission    │
          │                     └─────────────┬──────────────┘
          │                                   │
          │                                   ▼
          │                     ┌────────────────────────────┐
          │                     │     PermissionService      │
          │                     └─────────────┬──────────────┘
          │                                   │
          │                                   ▼
          │                     ┌────────────────────────────┐
          │                     │         Cache              │
          │                     │ (MemoryCache / Redis)      │
          │                     └─────────────┬──────────────┘
          │                                   │
          │                 ┌─────────────────┴────────────┐
          │                 │                              │
          │                 ▼                              ▼
          │           Cache Hit                      Cache Miss
          │                 │                              │
          │                 │                              ▼
          │                 │              ┌────────────────────────┐
          │                 │              │      Database          │
          │                 │              │ Users / Roles / Perms  │
          │                 │              └───────────┬────────────┘
          │                 │                          │
          │                 └──────────────┬───────────┘
          │                                │
          │                                ▼
          │                    Permission Decision
          │                                │
          │                    ┌───────────┴───────────┐
          │                    │                       │
          │                    ▼                       ▼
          │                 Allow                   Deny
          │                    │                       │
          │                    ▼                       ▼
          │             Controller              403 Forbidden
          │                    │
          │                    ▼
          │        Orders API / Customers API
          │
          │
          └─────────────────────────────────────────────────


              ACCESS TOKEN EXPIRES
                        │
                        ▼
                Client receives 401
                        │
                        ▼
                POST /auth/refresh
                        │
                        ▼
              Refresh Token Validation
                        │
                        ▼
                Refresh Token Store
                  (DB / Redis)
                        │
          ┌─────────────┴─────────────┐
          │                           │
          ▼                           ▼
      Invalid                    Valid
          │                           │
          ▼                           ▼
   Login Required          New JWT Issued
                                   │
                                   ▼
                          User Continues
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






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

&lt;p&gt;Ένα σωστό authentication και authorization σύστημα δεν βασίζεται σε ένα μόνο layer, αλλά σε καθαρό διαχωρισμό ευθυνών.&lt;/p&gt;

&lt;p&gt;Το middleware είναι το security gate που απορρίπτει invalid tokens πριν μπουν στο σύστημα.&lt;/p&gt;

&lt;p&gt;Το authorization layer είναι το σημείο που αποφασίζει αν ένας χρήστης έχει πρόσβαση σε ένα συγκεκριμένο endpoint.&lt;/p&gt;

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

&lt;p&gt;Και η cache είναι απλώς ένα performance layer που μειώνει το load χωρίς να επηρεάζει τη λογική του συστήματος.&lt;/p&gt;

&lt;p&gt;Όταν όλα αυτά λειτουργούν μαζί με καθαρό διαχωρισμό, έχεις ένα σύστημα που είναι ταυτόχρονα ασφαλές, γρήγορο και έτοιμο για production scale χωρίς bottlenecks.&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>AI Development Tools για Σύγχρονους Software Engineers: Πότε να χρησιμοποιείς Agent, Ask, Plan, GitHub Copilot, Debugger, Git και Profiler</title>
      <dc:creator>nikosst</dc:creator>
      <pubDate>Wed, 10 Jun 2026 14:41:33 +0000</pubDate>
      <link>https://dev.to/__b63657/ai-development-tools-gia-sugkhronous-software-engineers-pote-na-khresimopoieis-agent-ask-plan-a0b</link>
      <guid>https://dev.to/__b63657/ai-development-tools-gia-sugkhronous-software-engineers-pote-na-khresimopoieis-agent-ask-plan-a0b</guid>
      <description>&lt;p&gt;Η ανάπτυξη λογισμικού το 2026 έχει αλλάξει δραματικά. Ο σύγχρονος προγραμματιστής δεν γράφει απλώς κώδικα· συνεργάζεται καθημερινά με &lt;strong&gt;AI Agents&lt;/strong&gt;, &lt;strong&gt;εργαλεία αυτόματης δημιουργίας κώδικα&lt;/strong&gt;, &lt;strong&gt;συστήματα debugging&lt;/strong&gt;, &lt;strong&gt;profiling&lt;/strong&gt; και &lt;strong&gt;πλατφόρμες διαχείρισης εκδόσεων&lt;/strong&gt;. Η πρόκληση πλέον δεν είναι μόνο να γνωρίζει κάποιος προγραμματισμό, αλλά να ξέρει ποιο εργαλείο πρέπει να χρησιμοποιήσει σε κάθε στάδιο του Software Development Lifecycle.&lt;/p&gt;

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

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




&lt;p&gt;&lt;strong&gt;1. Ask Mode – Όταν χρειάζεσαι γνώση και καθοδήγηση&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Το &lt;strong&gt;Ask Mode&lt;/strong&gt; είναι η πιο απλή μορφή αλληλεπίδρασης με ένα AI σύστημα. Ο προγραμματιστής θέτει ερωτήσεις και λαμβάνει απαντήσεις, εξηγήσεις ή παραδείγματα.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Θέλεις να κατανοήσεις μια βιβλιοθήκη ή framework.&lt;/li&gt;
&lt;li&gt;Αναζητάς βέλτιστες πρακτικές.&lt;/li&gt;
&lt;li&gt;Θέλεις επεξήγηση ενός error message.&lt;/li&gt;
&lt;li&gt;Χρειάζεσαι παραδείγματα κώδικα.&lt;/li&gt;
&lt;/ul&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;Χρειάζεται ανάλυση ολόκληρου project.&lt;/li&gt;
&lt;li&gt;Πρέπει να γίνει refactoring μεγάλης κλίμακας.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Το Ask λειτουργεί σαν ένας πολύ έμπειρος συνάδελφος που απαντά σε ερωτήσεις, αλλά δεν αναλαμβάνει εργασία.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;2. Plan Mode – Πριν γράψεις γραμμή κώδικα&lt;/strong&gt;&lt;/p&gt;

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

&lt;p&gt;Το Plan Mode δημιουργήθηκε ακριβώς για αυτόν τον λόγο.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Σχεδιάζεις νέο feature.&lt;/li&gt;
&lt;li&gt;Θέλεις να σπάσεις ένα έργο σε tasks.&lt;/li&gt;
&lt;li&gt;Κάνεις migration συστήματος.&lt;/li&gt;
&lt;li&gt;Προετοιμάζεις αρχιτεκτονική λύση.&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;code&gt;"Γράψε authentication"&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;ζήτησε:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;"Σχεδίασε authentication architecture για .NET microservices με JWT και refresh tokens."&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Το αποτέλεσμα θα είναι σαφώς καλύτερο.&lt;/p&gt;

&lt;p&gt;Οι senior engineers επενδύουν περισσότερο χρόνο στον σχεδιασμό παρά στην υλοποίηση.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;3. Agent Mode – Ο ψηφιακός συνεργάτης&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Το Agent Mode αποτελεί τη μεγαλύτερη εξέλιξη των τελευταίων ετών.&lt;/p&gt;

&lt;p&gt;Ένας Agent δεν απαντά απλώς σε ερωτήσεις. Μπορεί να:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;διαβάσει ολόκληρο repository,&lt;/li&gt;
&lt;li&gt;αναλύσει requirements,&lt;/li&gt;
&lt;li&gt;τροποποιήσει πολλαπλά αρχεία,&lt;/li&gt;
&lt;li&gt;δημιουργήσει tests,&lt;/li&gt;
&lt;li&gt;κάνει refactoring,&lt;/li&gt;
&lt;li&gt;ανοίξει Pull Requests.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;Υπάρχει μεγάλο codebase.&lt;/li&gt;
&lt;li&gt;Θέλεις αυτοματοποίηση εργασιών.&lt;/li&gt;
&lt;li&gt;Κάνεις modernization legacy εφαρμογών.&lt;/li&gt;
&lt;li&gt;Χρειάζεσαι end-to-end υλοποίηση.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Έχεις μια απλή απορία.&lt;br&gt;
Χρειάζεται μόνο ένα μικρό snippet.&lt;/p&gt;

&lt;p&gt;Ο Agent λειτουργεί περισσότερο σαν junior-to-mid developer παρά σαν chatbot.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;4. GitHub Copilot – Ο καθημερινός βοηθός προγραμματισμού&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Το GitHub Copilot είναι το πιο διαδεδομένο AI εργαλείο ανάπτυξης λογισμικού.&lt;/p&gt;

&lt;p&gt;Βρίσκεται μέσα στο IDE και προσφέρει:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;autocomplete,&lt;/li&gt;
&lt;li&gt;code generation,&lt;/li&gt;
&lt;li&gt;chat assistance,&lt;/li&gt;
&lt;li&gt;code reviews,&lt;/li&gt;
&lt;li&gt;agent workflows.&lt;/li&gt;
&lt;/ul&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;Δημιουργείς APIs.&lt;/li&gt;
&lt;li&gt;Υλοποιείς CRUD λειτουργίες.&lt;/li&gt;
&lt;li&gt;Γράφεις unit tests.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Πλεονεκτήματα&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Μικρό context switching.&lt;/li&gt;
&lt;li&gt;Εξαιρετική ενσωμάτωση σε VS Code.&lt;/li&gt;
&lt;li&gt;Υψηλή παραγωγικότητα.&lt;/li&gt;
&lt;/ul&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;Μπορεί να παράγει λανθασμένο κώδικα.&lt;/li&gt;
&lt;li&gt;Απαιτεί code review.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;5. Debugger – Το εργαλείο που δεν αντικαθίσταται&lt;/strong&gt;&lt;/p&gt;

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

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

&lt;p&gt;Υπάρχει runtime σφάλμα.&lt;br&gt;
Οι τιμές μεταβλητών είναι λανθασμένες.&lt;br&gt;
Το σύστημα συμπεριφέρεται διαφορετικά από το αναμενόμενο.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Βέλτιστη πρακτική&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Reproduce.&lt;/li&gt;
&lt;li&gt;Breakpoint.&lt;/li&gt;
&lt;li&gt;Step Through.&lt;/li&gt;
&lt;li&gt;Root Cause Analysis.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Οι καλύτεροι developers δεν μαντεύουν. Μετρούν.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;6. Git – Η ασφάλεια του project&lt;/strong&gt;&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Χρησιμοποίησέ το για:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Version control.&lt;/li&gt;
&lt;li&gt;Feature branching.&lt;/li&gt;
&lt;li&gt;Pull Requests.&lt;/li&gt;
&lt;li&gt;Code Reviews.&lt;/li&gt;
&lt;li&gt;Rollback.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Senior πρακτικές&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Feature branches.&lt;/li&gt;
&lt;li&gt;Squash merges.&lt;/li&gt;
&lt;li&gt;Atomic commits.&lt;/li&gt;
&lt;li&gt;Protected main branch.&lt;/li&gt;
&lt;/ul&gt;

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




&lt;p&gt;&lt;strong&gt;7. Profiler – Όταν η εφαρμογή είναι αργή&lt;/strong&gt;&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Ο profiler δείχνει:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CPU bottlenecks,&lt;/li&gt;
&lt;li&gt;memory leaks,&lt;/li&gt;
&lt;li&gt;database latency,&lt;/li&gt;
&lt;li&gt;inefficient algorithms.&lt;/li&gt;
&lt;/ul&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;Αυξάνεται η κατανάλωση μνήμης.&lt;/li&gt;
&lt;li&gt;Υπάρχουν μεγάλοι χρόνοι απόκρισης.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Υπάρχει λογικό bug.&lt;br&gt;
Το πρόβλημα είναι business logic.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;8. Test Generation – Η ασπίδα της ποιότητας&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Τα σύγχρονα AI εργαλεία μπορούν να δημιουργούν:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Unit Tests&lt;/li&gt;
&lt;li&gt;Integration Tests&lt;/li&gt;
&lt;li&gt;API Tests&lt;/li&gt;
&lt;li&gt;Regression Tests&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;Προσθέτεις νέα features.&lt;/li&gt;
&lt;li&gt;Κάνεις refactoring.&lt;/li&gt;
&lt;li&gt;Κάνεις modernization.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ένα καλό σύστημα testing μειώνει δραματικά το τεχνικό χρέος.&lt;/p&gt;




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

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

&lt;p&gt;Για καθημερινό development, το GitHub Copilot παραμένει η πιο αποδοτική επιλογή. Για σύνθετες αλλαγές σε μεγάλα codebases, οι AI Agents και το Codex προσφέρουν τεράστια επιτάχυνση. Για σχεδιασμό αρχιτεκτονικής, το Plan Mode μειώνει τα λάθη πριν γραφτεί κώδικας. Για κατανόηση και εκπαίδευση, το Ask Mode είναι ιδανικό. Όταν εμφανίζονται σφάλματα, ο Debugger παραμένει αναντικατάστατος, ενώ ο Profiler είναι το μοναδικό εργαλείο που μπορεί να αποδείξει με δεδομένα πού πραγματικά χάνεται η απόδοση.&lt;/p&gt;

&lt;p&gt;Οι κορυφαίοι software engineers του 2026 δεν είναι αυτοί που γράφουν περισσότερο κώδικα. Είναι εκείνοι που ξέρουν να συνδυάζουν σωστά ανθρώπινη εμπειρία, αρχιτεκτονική σκέψη και AI εργαλεία, δημιουργώντας λογισμικό ταχύτερα, ασφαλέστερα και με υψηλότερη ποιότητα.&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>ai</category>
      <category>productivity</category>
      <category>softwareengineering</category>
      <category>tooling</category>
    </item>
    <item>
      <title>Local Time, UTC, Offset και Epoch: Ο απόλυτος οδηγός για developers</title>
      <dc:creator>nikosst</dc:creator>
      <pubDate>Tue, 09 Jun 2026 21:29:38 +0000</pubDate>
      <link>https://dev.to/__b63657/local-time-utc-offset-kai-epoch-o-apolutos-odegos-gia-developers-3h0f</link>
      <guid>https://dev.to/__b63657/local-time-utc-offset-kai-epoch-o-apolutos-odegos-gia-developers-3h0f</guid>
      <description>&lt;p&gt;&lt;strong&gt;Το πρόβλημα της ώρας&lt;/strong&gt;&lt;/p&gt;

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

&lt;p&gt;Αν ένας χρήστης στην Αθήνα δημιουργήσει μια παραγγελία στις 20:00 και ένας άλλος στη Νέα Υόρκη τη δει στις 13:00, ποια είναι η "σωστή" ώρα;&lt;/p&gt;

&lt;p&gt;Αν μια εφαρμογή αποθηκεύσει μόνο το 20:00, χωρίς να γνωρίζει τη ζώνη ώρας, τότε η πληροφορία είναι πρακτικά άχρηστη.&lt;/p&gt;

&lt;p&gt;Αυτός είναι ο λόγος που υπάρχουν έννοιες όπως:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Local Time&lt;/li&gt;
&lt;li&gt;UTC&lt;/li&gt;
&lt;li&gt;UTC Offset&lt;/li&gt;
&lt;li&gt;Epoch / Unix Timestamp&lt;/li&gt;
&lt;/ul&gt;

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




&lt;p&gt;Local Time&lt;/p&gt;

&lt;p&gt;Το Local Time είναι η ώρα που βλέπει ο χρήστης στη χώρα του.&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;Αθήνα:    2026-06-09 20:00
Λονδίνο:  2026-06-09 18:00
Νέα Υόρκη:2026-06-09 13:00
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;&lt;strong&gt;Πότε χρησιμοποιούμε Local Time;&lt;/strong&gt;&lt;/p&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;Ημερομηνία παραγγελίας&lt;/li&gt;
&lt;li&gt;Ώρα δημιουργίας post&lt;/li&gt;
&lt;li&gt;Ημερολόγιο συναντήσεων&lt;/li&gt;
&lt;li&gt;Reports προς τον χρήστη&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;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;2026-06-09 20:00
&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;li&gt;Αν ίσχυε θερινή ώρα (DST)&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;UTC (Coordinated Universal Time)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Το UTC είναι η παγκόσμια αναφορά χρόνου.&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;UTC: 2026-06-09 17:00
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Την ίδια στιγμή με βάση την UTC ώρα μπορούμε να έχουμε:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;στην Αθήνα   UTC+3 -&amp;gt; 20:00
στο Λονδίνο UTC+1 -&amp;gt; 18:00
στη Νέα Υόρκη UTC-4 -&amp;gt; 13:00
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;Σχεδόν πάντα στο backend.&lt;/p&gt;

&lt;p&gt;Αποθηκεύουμε:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="mi"&gt;2026-06-09&lt;/span&gt;&lt;span class="err"&gt;T&lt;/span&gt;&lt;span class="mi"&gt;17&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;&lt;span class="err"&gt;Z&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Το Z σημαίνει UTC.&lt;/p&gt;

&lt;p&gt;Γιατί;&lt;/p&gt;

&lt;p&gt;Επειδή:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Δεν αλλάζει με DST&lt;/li&gt;
&lt;li&gt;Δεν εξαρτάται από χώρα&lt;/li&gt;
&lt;li&gt;Είναι παγκόσμιο σημείο αναφοράς&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ένας κανόνας που ακολουθούν σχεδόν όλες οι μεγάλες εταιρείες:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Store in UTC, display in Local Time.&lt;/code&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;UTC Offset&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 plaintext"&gt;&lt;code&gt;UTC+3
UTC+2
UTC-5
UTC+9
&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 plaintext"&gt;&lt;code&gt;Χειμώνας -&amp;gt; UTC+2
Καλοκαίρι -&amp;gt; UTC+3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Χειμώνας -&amp;gt; UTC+2
Καλοκαίρι -&amp;gt; UTC+3
&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 json"&gt;&lt;code&gt;&lt;span class="mi"&gt;2026-06-09&lt;/span&gt;&lt;span class="err"&gt;T&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;&lt;span class="err"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;03&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;&lt;span class="w"&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 plaintext"&gt;&lt;code&gt;Τοπική ώρα: 20:00
Offset: +03:00
UTC: 17:00
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Πότε αποθηκεύουμε Offset;&lt;/strong&gt;&lt;/p&gt;

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

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

&lt;ul&gt;
&lt;li&gt;Αεροπορικά εισιτήρια&lt;/li&gt;
&lt;li&gt;Νομικά έγγραφα&lt;/li&gt;
&lt;li&gt;Ιατρικά συστήματα&lt;/li&gt;
&lt;li&gt;Financial systems&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Epoch / Unix Timestamp&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Το Epoch είναι η πιο "καθαρή" αναπαράσταση χρόνου.&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;1970-01-01 00:00:00 UTC
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Αυτό ονομάζεται Unix Epoch. &lt;a href="https://www.epochconverter.com/?utm_source=chatgpt.com" rel="noopener noreferrer"&gt;epochconverter&lt;/a&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;1780968655
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Αυτός είναι απλά ένας αριθμός.&lt;/p&gt;

&lt;p&gt;Δεν περιέχει:&lt;/p&gt;

&lt;p&gt;Timezone&lt;br&gt;
Offset&lt;br&gt;
DST&lt;/p&gt;

&lt;p&gt;Μόνο μια απόλυτη χρονική στιγμή.&lt;/p&gt;



&lt;p&gt;&lt;strong&gt;Γιατί οι developers αγαπούν τα Epochs;&lt;/strong&gt;&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;1780968655
1780968755
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ξέρεις αμέσως ότι το δεύτερο συνέβη αργότερα.&lt;/p&gt;

&lt;p&gt;Δεν χρειάζεται:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;μετατροπή timezone&lt;/li&gt;
&lt;li&gt;parsing&lt;/li&gt;
&lt;li&gt;date calculations&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Timestamp ως Long&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Στη .NET και στις περισσότερες γλώσσες το Epoch αποθηκεύεται ως:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;long timestamp
&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 plaintext"&gt;&lt;code&gt;1780968655
&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 plaintext"&gt;&lt;code&gt;1780968655000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(σε milliseconds)&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Databases&lt;/li&gt;
&lt;li&gt;APIs&lt;/li&gt;
&lt;li&gt;Event streams&lt;/li&gt;
&lt;li&gt;Kafka&lt;/li&gt;
&lt;li&gt;Message queues&lt;/li&gt;
&lt;li&gt;Logging systems&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Timestamp ως String&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 json"&gt;&lt;code&gt;&lt;span class="mi"&gt;2026-06-09&lt;/span&gt;&lt;span class="err"&gt;T&lt;/span&gt;&lt;span class="mi"&gt;17&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;&lt;span class="err"&gt;Z&lt;/span&gt;&lt;span class="w"&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 json"&gt;&lt;code&gt;&lt;span class="mi"&gt;2026-06-09&lt;/span&gt;&lt;span class="err"&gt;T&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;&lt;span class="err"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;03&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Αυτό είναι συνήθως ISO 8601 format.&lt;/p&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;Περιέχει timezone πληροφορία&lt;/li&gt;
&lt;li&gt;Ιδανικό για APIs&lt;/li&gt;
&lt;/ul&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;Πιο αργό parsing&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Timestamp ως DateTime&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Στη .NET:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;DateTime&lt;br&gt;
DateTimeOffset&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Σήμερα οι περισσότεροι senior developers προτιμούν:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;DateTimeOffset&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;γιατί περιέχει και το offset.&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;2026-06-09 20:00 +03:00
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;&lt;strong&gt;Τι να χρησιμοποιήσω τελικά;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Για αποθήκευση στη βάση&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Προτίμησε:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;UTC&lt;/code&gt; ή &lt;code&gt;DateTimeOffset&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Για APIs&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Προτίμησε:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ISO 8601&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 json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"createdAt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-06-09T17:00:00Z"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Για logs&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Προτίμησε:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;UTC&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;ή Epoch.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Για sorting και performance&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Προτίμησε:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Epoch Timestamp&lt;/code&gt;&lt;/p&gt;

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

&lt;p&gt;Προτίμησε:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Local Time&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;με βάση τη ζώνη ώρας του χρήστη.&lt;/p&gt;




&lt;p&gt;Τι είναι το Epoch Converter;&lt;/p&gt;

&lt;p&gt;Το &lt;a href="https://www.epochconverter.com/?utm_source=chatgpt.com" rel="noopener noreferrer"&gt;Epoch Converter&lt;/a&gt; είναι ένα πολύ γνωστό εργαλείο για developers που μετατρέπει:&lt;/p&gt;

&lt;p&gt;Epoch → Human Readable Date&lt;br&gt;
Date → Epoch&lt;br&gt;
UTC ↔ Local Time&lt;br&gt;
Seconds ↔ Milliseconds ↔ Microseconds&lt;/p&gt;

&lt;p&gt;και υποστηρίζει διάφορες μορφές timestamps.&lt;/p&gt;

&lt;p&gt;Είναι εξαιρετικά χρήσιμο όταν κάνεις debugging logs, APIs ή βάσεις δεδομένων.&lt;/p&gt;




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

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

&lt;p&gt;Στην πραγματικότητα, ο χρόνος είναι ένα παγκόσμιο πρόβλημα συγχρονισμού. Το Local Time εξυπηρετεί τον χρήστη, το UTC εξυπηρετεί το σύστημα, το Offset διατηρεί το πραγματικό τοπικό πλαίσιο μιας στιγμής και το Epoch παρέχει την πιο απλή και απόλυτη αναπαράσταση του χρόνου.&lt;/p&gt;

&lt;p&gt;Όταν σχεδιάζεις ένα σύστημα, η σωστή ερώτηση δεν είναι «ποιο type να χρησιμοποιήσω;». Η σωστή ερώτηση είναι:&lt;/p&gt;

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

&lt;p&gt;Αν απαντήσεις σωστά σε αυτή την ερώτηση, τότε η επιλογή μεταξύ Local Time, UTC, Offset και Epoch γίνεται σχεδόν αυτονόητη. Οι καλύτερες εφαρμογές δεν είναι αυτές που χειρίζονται σωστά τις ημερομηνίες στις εύκολες περιπτώσεις· είναι αυτές που συνεχίζουν να λειτουργούν σωστά όταν οι χρήστες βρίσκονται σε διαφορετικές ηπείρους, αλλάζουν ζώνες ώρας ή όταν η θερινή ώρα αποφασίζει να σου χαλάσει το βράδυ παραγωγής.&lt;/p&gt;




&lt;p&gt;&lt;a href="https://www.epochconverter.com" rel="noopener noreferrer"&gt;Το πιο γνωστό και ευρέως χρησιμοποιούμενο εργαλείο μετατροπής Unix timestamps για developers.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.epochconverter.it" rel="noopener noreferrer"&gt;Online εργαλείο μετατροπής Unix Epoch timestamps σε αναγνώσιμες ημερομηνίες και αντίστροφα.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://epochconverter.dev" rel="noopener noreferrer"&gt;Developer-focused εργαλείο για μετατροπές Epoch, UTC και ISO 8601 timestamps.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.epochconverter.io" rel="noopener noreferrer"&gt;Web εφαρμογή για μετατροπές Unix time, debugging και date calculations.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://epoch.to" rel="noopener noreferrer"&gt;Minimalistic converter για γρήγορη μετατροπή Epoch timestamps και ημερομηνιών.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://epochconverter24.com/" rel="noopener noreferrer"&gt;Online converter με υποστήριξη Epoch, UTC, timezone conversions και date utilities.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://epoch-timestamp.com/" rel="noopener noreferrer"&gt;Εργαλείο μετατροπής Unix timestamps με έμφαση σε προγραμματιστικές χρήσεις και APIs.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://unixtime.org/" rel="noopener noreferrer"&gt;Πληροφοριακό site για Unix Time με converter και επεξήγηση του Epoch format.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.unixcalculator.com/" rel="noopener noreferrer"&gt;Unix timestamp calculator με δυνατότητες date arithmetic και μετατροπών χρόνου.&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>beginners</category>
      <category>programming</category>
      <category>softwaredevelopment</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>ASP.NET Core Deep Dive: ViewData vs ViewBag vs TempData vs Session</title>
      <dc:creator>nikosst</dc:creator>
      <pubDate>Thu, 04 Jun 2026 10:22:14 +0000</pubDate>
      <link>https://dev.to/__b63657/aspnet-core-deep-dive-viewdata-vs-viewbag-vs-tempdata-vs-session-4845</link>
      <guid>https://dev.to/__b63657/aspnet-core-deep-dive-viewdata-vs-viewbag-vs-tempdata-vs-session-4845</guid>
      <description>&lt;p&gt;As ASP.NET Core developers, we often need to transfer data between controllers, views, requests, and even user sessions. The framework provides several mechanisms for this purpose: ViewData, ViewBag, TempData, and Session.&lt;/p&gt;

&lt;p&gt;Many junior developers learn these concepts separately and end up using them interchangeably. In reality, each one solves a completely different problem, has a different lifecycle, and affects maintainability in different ways.&lt;/p&gt;

&lt;p&gt;Understanding when to use each mechanism is essential for building scalable and maintainable MVC applications.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Why So Many Options Exist?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Imagine an e-commerce application.&lt;/p&gt;

&lt;p&gt;Sometimes you need to pass data from a controller to a view.&lt;/p&gt;

&lt;p&gt;Sometimes you need data to survive a redirect.&lt;/p&gt;

&lt;p&gt;Sometimes you need information available across multiple requests.&lt;/p&gt;

&lt;p&gt;Sometimes you need user-specific state that remains available during an entire browsing session.&lt;/p&gt;

&lt;p&gt;These are fundamentally different requirements. Microsoft introduced different mechanisms because a single solution would either be inefficient or overly complex.&lt;/p&gt;

&lt;p&gt;The key distinction is scope and lifetime.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;ViewData&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ViewData&lt;/strong&gt; is the oldest mechanism inherited from classic ASP.NET MVC.&lt;/p&gt;

&lt;p&gt;Internally, it is a dictionary of type:&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;ViewDataDictionary&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;which stores data as key-value pairs.&lt;/p&gt;

&lt;p&gt;A controller can populate the dictionary and the view can read from it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IActionResult&lt;/span&gt; &lt;span class="nf"&gt;Details&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;ViewData&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"Title"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Product Details"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;ViewData&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"ProductCount"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;15&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;View&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;Inside the Razor view:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;@ViewData["Title"]&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Total Products: @ViewData["ProductCount"]&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Although simple, ViewData has a major weakness.&lt;/p&gt;

&lt;p&gt;Because everything is stored as object, type casting becomes necessary.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;ViewData&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"ProductCount"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This means compile-time safety is lost.&lt;/p&gt;

&lt;p&gt;If someone changes the type later, the application may fail at runtime.&lt;/p&gt;

&lt;p&gt;For this reason, ViewData is generally considered suitable only for small pieces of UI-related information.&lt;/p&gt;

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

&lt;p&gt;Page titles&lt;br&gt;
Breadcrumb text&lt;br&gt;
Display messages&lt;br&gt;
Minor metadata&lt;/p&gt;

&lt;p&gt;Using ViewData for complex business objects usually creates maintenance problems.&lt;/p&gt;



&lt;p&gt;&lt;strong&gt;ViewBag&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ViewBag&lt;/strong&gt; was introduced to make ViewData easier to use.&lt;/p&gt;

&lt;p&gt;Interestingly, ViewBag is not a separate storage mechanism.&lt;/p&gt;

&lt;p&gt;Internally it wraps ViewData using dynamic properties.&lt;/p&gt;

&lt;p&gt;When you write:&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;ViewBag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Title&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Product Details"&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 Core actually stores the value inside ViewData.&lt;/p&gt;

&lt;p&gt;The equivalent ViewData version is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;ViewData&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"Title"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Product Details"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The 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="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IActionResult&lt;/span&gt; &lt;span class="nf"&gt;Details&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;ViewBag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ProductName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Gaming Laptop"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;ViewBag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Price&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;1500&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;View&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The view:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;@ViewBag.ProductName&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Price: @ViewBag.Price €&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The syntax is cleaner and easier to read.&lt;/p&gt;

&lt;p&gt;However, ViewBag introduces another problem: the use of dynamic.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;ViewBag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ProductName&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;the compiler will not detect the typo.&lt;/p&gt;

&lt;p&gt;The error appears only during execution.&lt;/p&gt;

&lt;p&gt;For small MVC applications this may not matter, but in enterprise systems it becomes a source of bugs.&lt;/p&gt;

&lt;p&gt;That is why many senior developers prefer strongly typed ViewModels over both ViewData and ViewBag.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;ViewData vs ViewBag&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Since both use the same underlying storage, their lifecycle is identical.&lt;/p&gt;

&lt;p&gt;They exist only during the current request.&lt;/p&gt;

&lt;p&gt;Once the response is sent, the data disappears.&lt;/p&gt;

&lt;p&gt;The following diagram describes their lifecycle conceptually:&lt;/p&gt;

&lt;p&gt;Controller → View → Destroyed&lt;/p&gt;

&lt;p&gt;Neither survives redirects.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IActionResult&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="n"&gt;ViewBag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Saved"&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;RedirectToAction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Index"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The message will be lost.&lt;/p&gt;

&lt;p&gt;The redirect creates an entirely new HTTP request.&lt;/p&gt;

&lt;p&gt;This is where TempData enters the picture.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;TempData&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TempData&lt;/strong&gt; was designed specifically for passing data between requests.&lt;/p&gt;

&lt;p&gt;Most commonly, it is used after redirects.&lt;/p&gt;

&lt;p&gt;A typical scenario is the Post-Redirect-Get 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="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;HttpPost&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;IActionResult&lt;/span&gt; &lt;span class="nf"&gt;Save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;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="c1"&gt;// Save logic&lt;/span&gt;

    &lt;span class="n"&gt;TempData&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"Success"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Product saved successfully"&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;RedirectToAction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Index"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then:&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;IActionResult&lt;/span&gt; &lt;span class="nf"&gt;Index&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;View&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;Inside the view:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;@if (TempData["Success"] != null)
{
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"alert alert-success"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        @TempData["Success"]
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The user sees the message after the redirect.&lt;/p&gt;

&lt;p&gt;This would not be possible using ViewData or ViewBag.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;The Special Behavior of TempData&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;TempData behaves differently from the other mechanisms.&lt;/p&gt;

&lt;p&gt;By default, data is automatically removed after it is read.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TempData&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"Success"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After this line executes, the value is marked for deletion.&lt;/p&gt;

&lt;p&gt;If you need to preserve it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;TempData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Keep&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or:&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;TempData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Keep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Success"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also inspect a value without removing it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TempData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Peek&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Success"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This makes TempData ideal for:&lt;/p&gt;

&lt;p&gt;Success notifications&lt;br&gt;
Error messages&lt;br&gt;
Wizard workflows&lt;br&gt;
Redirect scenarios&lt;/p&gt;

&lt;p&gt;It is not suitable for long-term storage.&lt;/p&gt;



&lt;p&gt;&lt;strong&gt;Session&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Session solves a completely different problem.&lt;/p&gt;

&lt;p&gt;While ViewData, ViewBag, and TempData focus on controller/view communication, Session stores user-specific state across many requests.&lt;/p&gt;

&lt;p&gt;Imagine a shopping cart.&lt;/p&gt;

&lt;p&gt;A user may browse dozens of pages before checking out.&lt;/p&gt;

&lt;p&gt;The cart must remain available throughout the session.&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;HttpContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"Username"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"Nick"&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Reading:&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;username&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
    &lt;span class="n"&gt;HttpContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s"&gt;"Username"&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Unlike ViewData and TempData, Session can remain available for minutes or hours depending on configuration.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Configuring Session in ASP.NET Core&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;First, register Session services:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddDistributedMemoryCache&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="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddSession&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IdleTimeout&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
        &lt;span class="n"&gt;TimeSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromMinutes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;30&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then enable 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="nf"&gt;UseSession&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now Session becomes available through:&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;HttpContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Session&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Without this configuration, Session operations will fail.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Session and Complex Objects&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Session natively supports strings and byte arrays.&lt;/p&gt;

&lt;p&gt;For custom objects, serialization is required.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ShoppingCart&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;ItemCount&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;set&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;Store:&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;cart&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;ShoppingCart&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;ItemCount&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="n"&gt;HttpContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"Cart"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;JsonSerializer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Serialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cart&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;Retrieve:&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;json&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
    &lt;span class="n"&gt;HttpContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s"&gt;"Cart"&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;cart&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
    &lt;span class="n"&gt;JsonSerializer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Deserialize&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ShoppingCart&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;
        &lt;span class="n"&gt;json&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This approach is common in real-world applications.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Performance Considerations&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A frequent mistake is treating Session as a database.&lt;/p&gt;

&lt;p&gt;Session should store only small amounts of user-specific state.&lt;/p&gt;

&lt;p&gt;Storing large datasets increases memory consumption and hurts scalability.&lt;/p&gt;

&lt;p&gt;In distributed environments, Session data often ends up in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Redis&lt;/li&gt;
&lt;li&gt;SQL Server&lt;/li&gt;
&lt;li&gt;Distributed Cache Providers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The larger the Session, the slower the application becomes.&lt;/p&gt;

&lt;p&gt;A good rule is simple:&lt;/p&gt;

&lt;p&gt;If data belongs in a database, keep it in a database.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;What Senior Developers Usually Choose&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In modern ASP.NET Core applications, strongly typed ViewModels are generally preferred over ViewData and ViewBag.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ProductViewModel&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;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;Price&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;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Controller:&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;IActionResult&lt;/span&gt; &lt;span class="nf"&gt;Details&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;model&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;ProductViewModel&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;"Laptop"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;Price&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;1500&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;View&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&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;View:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;@model ProductViewModel

&lt;span class="nt"&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;@Model.Name&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;@Model.Price €&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;ul&gt;
&lt;li&gt;Compile-time checking&lt;/li&gt;
&lt;li&gt;IntelliSense support&lt;/li&gt;
&lt;li&gt;Easier refactoring&lt;/li&gt;
&lt;li&gt;Better maintainability&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Consequently, ViewData and ViewBag are often reserved for small UI metadata.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Modern ASP.NET Core Perspective&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;While ViewData, ViewBag, TempData, and Session remain fully supported in ASP.NET Core, their role has evolved. Modern applications favor strongly typed &lt;strong&gt;ViewModels&lt;/strong&gt;, &lt;strong&gt;DTOs&lt;/strong&gt;, and API-based architectures because they provide better &lt;strong&gt;maintainability&lt;/strong&gt;, &lt;strong&gt;testability&lt;/strong&gt;, and &lt;strong&gt;compile-time safety&lt;/strong&gt;. ViewData and ViewBag are now typically reserved for small UI-related values, TempData remains a practical solution for passing messages across redirects, and Session is used more selectively for user-specific state. The key takeaway is that these mechanisms are still valuable, but in contemporary ASP.NET Core development they are no longer the primary way application data is modeled and transferred.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Final Verdict&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The biggest mistake developers make is viewing these four mechanisms as alternatives. They are not.&lt;/p&gt;

&lt;p&gt;ViewData and ViewBag exist to pass data from a controller to a view during a single request.&lt;/p&gt;

&lt;p&gt;TempData exists to transfer data between requests, especially after redirects.&lt;/p&gt;

&lt;p&gt;Session exists to maintain user-specific state across multiple requests and pages.&lt;/p&gt;

&lt;p&gt;When selecting one, ask a single question:&lt;/p&gt;

&lt;p&gt;How long should the data live?&lt;/p&gt;

&lt;p&gt;If the answer is until the view renders, use ViewData, ViewBag, or preferably a ViewModel.&lt;/p&gt;

&lt;p&gt;If the answer is until the next request, use TempData.&lt;/p&gt;

&lt;p&gt;If the answer is throughout the user's interaction with the application, use Session.&lt;/p&gt;

&lt;p&gt;Understanding this distinction is one of those small architectural decisions that separates maintainable ASP.NET Core applications from those that become increasingly difficult to evolve over time.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Συμπεράσματα&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Ένα από τα μεγαλύτερα λάθη που κάνουν οι προγραμματιστές είναι να αντιμετωπίζουν τα ViewData, ViewBag, TempData και Session ως εναλλακτικές λύσεις για το ίδιο πρόβλημα. Στην πραγματικότητα, δεν είναι.&lt;/p&gt;

&lt;p&gt;Το ViewData και το ViewBag υπάρχουν για να μεταφέρουν δεδομένα από έναν Controller σε ένα View κατά τη διάρκεια του ίδιου HTTP request.&lt;/p&gt;

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

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

&lt;p&gt;Όταν έρχεται η στιγμή να επιλέξετε ποιον μηχανισμό θα χρησιμοποιήσετε, κάντε πρώτα μία απλή ερώτηση:&lt;/p&gt;

&lt;p&gt;«Για πόσο χρονικό διάστημα πρέπει να παραμείνουν διαθέσιμα αυτά τα δεδομένα;»&lt;/p&gt;

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

&lt;p&gt;Αν τα δεδομένα πρέπει να επιβιώσουν μέχρι το επόμενο request, τότε το TempData είναι η σωστή λύση.&lt;/p&gt;

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

&lt;p&gt;Η κατανόηση αυτής της διαφοράς μπορεί να φαίνεται ως μια μικρή αρχιτεκτονική λεπτομέρεια. Στην πράξη, όμως, είναι μία από εκείνες τις αποφάσεις που διαχωρίζουν τις καθαρές, επεκτάσιμες και εύκολα συντηρήσιμες εφαρμογές ASP.NET Core από εκείνες που με την πάροδο του χρόνου γίνονται ολοένα και πιο δύσκολες στη συντήρηση και την εξέλιξή τους.&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>Strategy Pattern Θεωρία, Χρήση και Πρακτική Εφαρμογή</title>
      <dc:creator>nikosst</dc:creator>
      <pubDate>Thu, 30 Apr 2026 13:38:11 +0000</pubDate>
      <link>https://dev.to/__b63657/strategy-pattern-theoria-khrese-kai-praktike-epharmoge-34m</link>
      <guid>https://dev.to/__b63657/strategy-pattern-theoria-khrese-kai-praktike-epharmoge-34m</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdaak7qoeprxvd86enngz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdaak7qoeprxvd86enngz.png" alt=" " width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

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

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

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

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

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




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

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

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



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

&lt;p&gt;Φαντάσου αυτό:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"credit"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;PayWithCredit&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"paypal"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;PayWithPaypal&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"crypto"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;PayWithCrypto&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

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




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

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

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




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

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

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

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

&lt;ol&gt;
&lt;li&gt;Strategy Interface
&lt;/li&gt;
&lt;/ol&gt;

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

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Concrete Strategies
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CreditCardPayment&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IPaymentStrategy&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Pay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;decimal&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"Paid &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt; with Credit Card"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PaypalPayment&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IPaymentStrategy&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Pay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;decimal&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"Paid &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt; with PayPal"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Context
&lt;/li&gt;
&lt;/ol&gt;

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

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;PaymentContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IPaymentStrategy&lt;/span&gt; &lt;span class="n"&gt;strategy&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_strategy&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;strategy&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;SetStrategy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IPaymentStrategy&lt;/span&gt; &lt;span class="n"&gt;strategy&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_strategy&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;strategy&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Χρήση
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;PaymentContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;CreditCardPayment&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ExecutePayment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;100&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetStrategy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;PaypalPayment&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ExecutePayment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;200&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






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

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

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

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

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

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

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

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

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

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

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




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

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



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

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



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

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



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

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

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

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

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

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

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

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



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

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

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

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

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



&lt;p&gt;&lt;strong&gt;Παράδειγμα σε ASP.NET Core&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;WebApplication&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Register strategies&lt;/span&gt;
&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddScoped&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IPaymentStrategy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CreditCardPayment&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddScoped&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IPaymentStrategy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;PaypalPayment&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Register context &amp;amp; service&lt;/span&gt;
&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddScoped&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;PaymentContext&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddScoped&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;PaymentService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Service που χρησιμοποιεί το Strategy Pattern&lt;br&gt;
&lt;/p&gt;

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

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;PaymentService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PaymentContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_context&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Pay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;decimal&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ExecutePayment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Παράδειγμα χρήσης (π.χ. σε Controller ή minimal API)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapPost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/pay"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PaymentService&lt;/span&gt; &lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;decimal&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Pay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Payment completed"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;Request παραδείγματα&lt;/code&gt;&lt;/p&gt;

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

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



&lt;p&gt;Αν τώρα προσθέσεις:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CryptoPayment&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IPaymentStrategy&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Key&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"crypto"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Pay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;decimal&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"Paid &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt; with Crypto"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;και κάνεις:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;






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

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

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

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

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

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

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




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

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

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

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

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

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




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

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

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

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

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

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




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

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

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

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

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




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

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

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

&lt;/div&gt;



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

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




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

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

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

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

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




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

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

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

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

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

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

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




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

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

&lt;p&gt;Εδώ εφαρμόζονται limits μέσω μηχανισμών όπως:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;RequestSizeLimit&lt;/span&gt;&lt;span class="p"&gt;(...)]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

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

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

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

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

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




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

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

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

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

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

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

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

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

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

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




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

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




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

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




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

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

&lt;/div&gt;

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

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

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

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

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

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




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

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

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

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

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

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

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

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

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

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

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




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

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

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

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

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

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

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

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

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




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

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

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

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




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

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

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

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

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

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




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

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

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

&lt;/div&gt;



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

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




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

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

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

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

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

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




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

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

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

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

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

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

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




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

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

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

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

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

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

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




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

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

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

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




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

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

&lt;p&gt;Για παράδειγμα, αν έχεις έναν μεγάλο πίνακα Orders και εκτελέσεις:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;SUM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;Orders&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

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

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

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

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

&lt;/div&gt;






&lt;p&gt;&lt;strong&gt;Πότε το parallelism γίνεται πρόβλημα&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Για να δούμε πόσα logical CPUs βλέπει ο SQL Server μπορούμε να τρέξουμε &lt;code&gt;SELECT cpu_count FROM sys.dm_os_sys_info;&lt;/code&gt;, όμως το αν “αξίζει” MAXDOP = 1 δεν κρίνεται μόνο από τον αριθμό των πυρήνων. Σε μικρά συστήματα με λίγους πυρήνες, πολλά ταυτόχρονα sessions και κυρίως OLTP workload, το υπερβολικό parallelism μπορεί να δημιουργήσει ανταγωνισμό για CPU workers και να κάνει τα queries να μπλοκάρουν μεταξύ τους. Παρ’ όλα αυτά, το MAXDOP = 1 είναι ακραία ρύθμιση, γιατί απενεργοποιεί το parallel execution για τα queries· συνήθως πρώτα εξετάζουμε MAXDOP 2, 4 ή 8, το cost threshold for parallelism, τα wait stats, το CPU pressure, το είδος του workload, τον αριθμό των ταυτόχρονων χρηστών και αν το πρόβλημα είναι όντως CPU ή κακό execution plan/indexing. Με απλά λόγια: δεν λέμε “πάνω ή κάτω από Χ πυρήνες βάζω MAXDOP 1”, αλλά “βάζω χαμηλότερο MAXDOP όταν το parallelism αποδεδειγμένα χειροτερεύει τη συνολική απόδοση”.&lt;/p&gt;




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

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

&lt;p&gt;Αν ένα query είναι κακοσχεδιασμένο, θα παραμείνει κακοσχεδιασμένο, απλώς θα εκτελείται σε περισσότερους πυρήνες. Το parallelism μπορεί να μειώσει τον elapsed time, αλλά δεν μειώνει τη συνολική δουλειά ούτε το συνολικό resource consumption.&lt;br&gt;
Παράδειγμα:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;Customers&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="k"&gt;LIKE&lt;/span&gt; &lt;span class="s1"&gt;'%john%'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;Αντίθετα:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;Customers&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="k"&gt;LIKE&lt;/span&gt; &lt;span class="s1"&gt;'john%'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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




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

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

&lt;p&gt;Η σύνταξη είναι απλή:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;OPTION&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MAXDOP&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;σημαίνει ότι το query θα εκτελεστεί σειριακά, δηλαδή σε έναν πυρήνα.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;OPTION&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MAXDOP&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;σημαίνει ότι μπορεί να χρησιμοποιήσει μέχρι 4 πυρήνες.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;OPTION&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MAXDOP&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

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

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

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




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

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

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

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

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

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

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



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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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



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

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

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

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

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

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

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

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



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

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

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

&lt;p&gt;Σε αυτή την περίπτωση, αν βάλεις:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;OPTION&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MAXDOP&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

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

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

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




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

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

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;Region&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;SUM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Sales&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;SalesData&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;Region&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

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




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

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

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

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

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

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




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

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

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




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

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

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

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




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

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

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

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

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

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

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

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

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




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

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

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

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

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

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

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

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

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




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

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

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

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




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

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

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

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

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

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

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

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

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

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

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

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

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

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

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




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

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

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

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




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

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

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

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

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

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

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

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

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

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

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

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

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

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

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




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

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

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

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

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

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

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




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

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

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

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

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

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

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

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

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

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

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

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

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

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

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



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

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

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

&lt;p&gt;Το AsTracking() ενεργοποιεί ρητά το tracking σε ένα query. Στην πράξη, επιβεβαιώνει το default behavior και χρησιμοποιείται κυρίως για σαφήνεια ή όταν έχει προηγηθεί απενεργοποίηση tracking.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Users&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AsTracking&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToList&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Updated Name"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

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

&lt;/div&gt;



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

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




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

&lt;p&gt;Το &lt;strong&gt;AsNoTracking()&lt;/strong&gt; απενεργοποιεί τον μηχανισμό παρακολούθησης (change tracking) του Entity Framework Core για τα entities που επιστρέφει ένα query. Αυτό σημαίνει ότι τα αντικείμενα που θα φορτωθούν από τη βάση δεδομένων δεν αποθηκεύονται στο Change Tracker του DbContext και το EF δεν κρατάει καμία πληροφορία για την αρχική τους κατάσταση.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Users&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AsNoTracking&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToList&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Updated Name"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

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

&lt;/div&gt;



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

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

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

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




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

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

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

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

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




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

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

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

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

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Orders&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AsNoTracking&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Include&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Customer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToList&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

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



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

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

&lt;p&gt;Δεν κάνει tracking (άρα είναι πιο ελαφρύ από AsTracking())&lt;br&gt;
ΑΛΛΑ διατηρεί identity resolution&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Orders&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AsNoTrackingWithIdentityResolution&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Include&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Customer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToList&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

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

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

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

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

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

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

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




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

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

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

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

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

&lt;/div&gt;






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

&lt;p&gt;&lt;strong&gt;Χωρίς Identity Resolution&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Orders&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AsNoTracking&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Include&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Customer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToList&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;sameCustomer&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;ReferenceEquals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;Customer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;Customer&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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




&lt;p&gt;&lt;strong&gt;Με Identity Resolution&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Orders&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AsNoTrackingWithIdentityResolution&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Include&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Customer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToList&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;sameCustomer&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;ReferenceEquals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;Customer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;Customer&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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




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

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

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




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

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

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

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

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




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

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

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

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




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

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

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

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




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

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

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

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

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

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

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

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

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




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

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

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

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

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

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




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

</description>
      <category>csharp</category>
      <category>database</category>
      <category>dotnet</category>
      <category>performance</category>
    </item>
    <item>
      <title>Specification Pattern υπό το πρίσμα του SOLID και της Clean Architecture</title>
      <dc:creator>nikosst</dc:creator>
      <pubDate>Wed, 25 Mar 2026 18:24:21 +0000</pubDate>
      <link>https://dev.to/__b63657/specification-pattern-upo-to-prisma-tou-solid-kai-tes-clean-architecture-8oh</link>
      <guid>https://dev.to/__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://dev.to/__b63657/asynchronous-programming-sten-c-themeliodeis-arkhes-kanones-kai-bathia-katanoese-2nd5</link>
      <guid>https://dev.to/__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://dev.to/__b63657/helper-classes-vs-extension-methods-3f8f</link>
      <guid>https://dev.to/__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://dev.to/__b63657/apo-static-classes-se-services-me-dependency-injection-me-optike-clean-architecture-layers--cf7</link>
      <guid>https://dev.to/__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>
  </channel>
</rss>
