Είναι δυνατόν να υλοποιήσει κανείς και τα άλλα δύο μοντέλα (table per class και table per concrete class) inheritance με το LINQ to SQL αν και θέλει ολίγον κώδικα και η κάθε λύση έχει τους δικούς της περιορισμούς. Σαν παράδειγμα, έστω ότι υπάρχουν τρεις κλάσεις, Person (ID, Name), Employee (ID, Name, HireDate) και Manager (ID, Name, HireDate, Department), όπου η κάθε κλάση κληρονομεί από την προηγούμενη.
Table per Class
Στο μοντέλο table per class υπάρχει ένας πίνακας για κάθε κλάση, ο οποίος κρατάει μόνο τα πεδία που ορίζονται σε αυτή την κλάση. Χρησιμοποιώντας το παραπάνω παράδειγμα, θα υπάρχει ένας πίνακας Persons (PersonID, Name) , ένας πίνακας Employees (PersonID, HireDate) και ένας πίνακας Manager (PersonID,Department). Για να πάρουμε τους Employees πρέπει να κάνουμε join τους πίνακες Persons και Employees. Για να πάρουμε τους Managers, θα πρέπει να κάνουμε join τους πίνακες Persons, Employees, Managers. Για να βρούμε όλα τα αντικείμενα, τόσο Person όσο και Employee όσο και Manager, κάνουμε select από τον πίνακα Persons.
Το μοντέλο αυτό είναι το πιο συνηθισμένο γιατί επιτρέπει τη χρήση κανονικοποιημένων πινάκων και εύκολα πολυμορφικά queries ακόμα και με απλή SQL.
Για να δουλέψει αυτό το μοντέλο με το LINQ to SQL χρειάζονται τα παρακάτω:
- Ένα view για κάθε concrete class πέρα από την root, π.χ. vwEmployees και vwManagers το οποίο θα κάνει join όλους τους πίνακες που απαιτούνται για κάθε κλάση.
- Insert, Update, Delete stored procedures για κάθε κλάση.
- Οι κλάσεις Person, Employee, Manager θα πρέπει να περιέχουν όλα τα properties με τα κατάλληλα LINQ attributes.
- Οι κλάσεις θα κάνουν κανονικά inherit η μία από την άλλη, αλλά θα πρέπει τα properties να ορίζονται ως virtual στις base κλάσεις και ως override στις derived
Τα παραπάνω μπορούν να γίνουν και μέσω του LINQ to SQL designer, ορίζοντας το inheritance στο code-behind αρχείο και όχι στον ίδιο το designer. Υπάρχει όμως πρόβλημα με τα functions που προσθέτει ο designer για την υλοποίηση των INotifyPropertyChanging, INotifyPropertyChanged. Αυτά μπορούν να αφαιρεθούν από τις derived κλάσεις αλλά θα ξαναδημιουργηθούν όταν αλλάξει κάτι στο designer.
Table per Concrete Class
Στο μοντέλο table per class υπάρχει ένας πίνακας για κάθε κλάση, ο οποίος κρατάει όλα τα πεδία που ορίζονται σε αυτή την κλάση. Χρησιμοποιώντας το παραπάνω παράδειγμα, θα υπάρχει ένας πίνακας Persons (PersonID, Name) , ένας πίνακας Employees (PersonID, Name, HireDate) και ένας πίνακας Manager (PersonID,Name, HireDate, Department). Για να βρούμε ένα Person, Employee, Manager απλά κάνουμε query τον αντίστοιχο πίνακα. Για να βρούμε όλα τα Person αντικείμενα θα πρέπει να φτιάξουμε ένα query το οποίο θα ψάχνει σε όλους τους πίνακες και θα κάνει union τα αποτελέσματα.
Για να δουλέψει αυτό το μοντέλο με το LINQ to SQL χρειάζονται τα παρακάτω:
- Οι κλάσεις Person, Employee, Manager θα πρέπει να περιέχουν όλα τα properties με τα κατάλληλα LINQ attributes.
- Οι κλάσεις θα κάνουν κανονικά inherit η μία από την άλλη
Τα παραπάνω μπορούν να γίνουν και μέσω του LINQ to SQL designer, ορίζοντας το inheritance στο code-behind αρχείο και όχι στον ίδιο το designer, προσέχοντας πάλι τα functions που προσθέτει ο designer.
Το πρόβλημα εδώ είναι ότι για να κάνουμε αναζήτηση όλων των persons θα πρέπει να φτιάξουμε ένα union query . Αν θέλουμε να βρούμε τους Employees θα πρέπει να φτιάξουμε νέο union query.
Συμπέρασμα
Μπορεί κανείς να χρησιμοποιήσει και το μοντέλο Table-per-Class και το Table-per-Concrete-Class προσθέτοντας κώδικα. Δυστυχώς, το πιο χρήσιμο μοντέλο απαιτεί και τον περισσότερο κώδικα. Αυτό δημιουργεί το εξής δίλημα:
- Χρησιμοποιούμε το μοντέλο που ταιριάζει στην εφαρμογή μας και προσθέτουμε τα απαραίτητα view, stored procedures ή
- Προσαρμόζουμε την εφαρμογή και τη βάση στο μοντέλο για να γλυτώσουμε τον επιπλέον κώδικα
Προσωπικά θα προτιμούσα να χρησιμοποιήσω το μοντέλο που χρειάζεται η βάση και η εφαρμογή μου και ας χρειάζεται επιπλέον κώδικα. Όταν βγει το LINQ to Entities θα μετατρέψω τον κώδικα ώστε να δουλεύει σύμφωνα με αυτό και θα αφαιρέσω τα επιπλέον views, κλπ. Αν προσαρμόσω την εφαρμογή στο μοντέλο, κινδυνεύω να καταλήξω με μία εφαμοργή η οποία θα είναι δύσκολη στη συντήρηση μελλοντικά.
Παναγιώτης Καναβός, Freelancer
Twitter: http://www.twitter.com/pkanavos