Ακόμα καλύτερα: ΔΕΝ χρησιμοποιούμε Linq to SQL. Οι lookup τιμές είναι απλά ένα σύνολο από χύμα τιμές. Δεν έχουν συμπεριφορά, άρα δεν υπάρχει λόγος να τις χειριστούμε ως αντικείμενα. Επίσης, η μαζική εισαγωγή χύμα τιμών πάλι δεν περιέχει συμπεριφορά, άρα δεν χρειάζεται αντικείμενα. Από τη στιγμή που το Context μας δίνει πρόσβαση στο connection μπορούμε να το χρησιμοποιήσουμε για να τρέξουμε ένα απλό SQL command και να εισάγουμε τις lookup τιμές χωρίς να παρεμβαίνει το Linq to SQL.
Όσον αφορά τη συμπεριφορά του DataContext.Executecommand, δεν είναι ξεκάθαρο αν τα Inserts γίνονται μέσω Linq to SQL ή μέσω του ExecuteCommand. Αν γίνονται μέσω του ExecuteCommand το Linq θα εισάγει τις τιμές με βάση όσα έχουν οριστεί στο DataContext, δηλαδή θα θεωρήσει ότι είναι generated και θα τις αγνοήσει. Η απορία όμως μπορεί να λυθεί άνετα βάζοντας τον SQL Profiler και βλέποντας τί είδους SQL εκτελείται. Όπως είπα και παραπάνω όμως, δεν αξίζει να χρησιμοποιήσει κανείς Linq για να εισάγει lookup values.
Τέλος, ο Udi Dahan περιγράφει στο "Object Relational Mapping Sucks (for reporting that is)" τρεις απλές περιπτώσεις στις οποίες δεν πρέπει να χρησιμοποιούμε ORM αν θέλουμε scalable εφαρμογές.
- Όχι για Reporting
- Δεν χρησιμοποιούμε setters
- Τα grids είναι reports, άρα όχι ORM για να δείξουμε grids
Ειδικά το #2 χρειάζεται λίγο εξήγηση καθώς ουσιαστικά λέει ότι .... δεν πρέπει να γράφουμε μέσω properties! Όπως εξηγεί, αν θέλουμε scalable λύσεις χωρίς προβλήματα από conflicts και κλειδώματα, αντί να πειράζουμε τα properties ενός αντικειμένου και να του λέμε να σώσει τον εαυτό του είναι προτιμότερο να καλούμε μεθόδους οι οποίες θα υλοποιούν ένα σενάριο. Έτσι, ειδικά σε n-tier περιβάλλοντα αντί να κάνουμε serialize ολόκληρο το αντικείμενο ή έστω τις αλλαγές του και να το στείλουμε στο server, στέλνουμε μόνο την εντολή και τις παραμέτρους της.
Το #2 είναι πολύ σημαντικό και δεν είναι κάτι καινούριο. Από τον καιρό του COM+ και των πρώτων υλοποιήσεων του J2EE μάθαμε ότι δεν είναι καθόλου καλή ιδέα να μεταφέρεις ολόκληρα αντικείμενα από τον client στο server. Αντίθετα είναι πολύ ευκολότερο να βάλεις στα αντικείμενα μεθόδους που θα κάνουν αυτό που θέλεις και να κάνεις serialize τις κλήσεις. Έτσι πετυχαίνεις και μικρότερο φόρτο στο δίκτυο, και μικρότερη πιθανότητα conflicts αλλά και ξέρεις πλέον πως να αντιμετωπίσεις πιθανά blocks. Αν για παράδειγμα θέλω να μεταφέρω χρήματα από ένα λογαριασμό σε άλλο πειράζοντας τα properties δύο αντικειμένων Account, σε περίπτωση concurrency conflig κανένα από τα δύο αντικείμενα δεν θα ξέρει τί να κάνει. Θα πρέπει κάποιος άλλος κώδικας, ο οποίος ξέρει για ποιό λόγο εγώ πείραξα τα properties, να χειριστεί το conflict. Αντίθετα, αν καλέσω απλά τη μέθοδο TransferAmount στο server, μπορώ εκεί μέσα να βάλω την κατάλληλη λογική έτσι ώστε να αντιμετωπίσω πιθανά conflicts.
Εδώ είναι και η μεγάλη διαφορά μεταξύ Datasets και ORM. Όταν χρησιμοποιεί κανείς datasets η μόνη επιλογή που έχει είναι να στείλει στο server είτε ολόκληρο το dataset είτε τα δεδομένα που άλλαξαν. Δεν υπάρχει τρόπος όμως να περιγράψεις γιατί τα δεδομένα άλλαξαν, τί σημασία έχουν αυτές οι αλλαγές και πως να τις χειριστεί κανείς. Επιπλέον, αναγκαστικά θα πρέπει να στείλει κανείς πολύ περισσότερα δεδομένα απ' όσα πραγματικά χρειάζονται.
Ουσιαστικά, το #2 καταργεί την ανάγκη για n-tier serialization τόσο στο Linq όσο και στο Entity Framework, τουλάχιστον αν μας ενδιαφέρει το scalability.
Παναγιώτης Καναβός, Freelancer
Twitter: http://www.twitter.com/pkanavos