|
Îåêßíçóå áðü ôï ìÝëïò m6s. Τελευταία δημοσίευση από το μέλος m6s στις 30-01-2008, 00:09. Υπάρχουν 10 απαντήσεις.
-
27-01-2008, 02:14
|
-
m6s
-
-

-
Μέλος από τις 01-06-2007
-
Αθήνα
-
Δημοσιεύσεις 151
-
-
|
Transactions C# SQL express 2005
Καλησπέρα, παιδιά.. Εχω το εξής θεματάκι :
DbConnection dbconn = _db.CreateConnection(); DbTransaction trans = null; try { dbconn.Open(); trans = dbconn.BeginTransaction(IsolationLevel.Serializable);
foreach (DataRow row in Data.Tables["Production"].Select("Χ = '" + ΧTxt.Value + "'")) { row["Χ"] = ProductionCbo.Value; row["Υ"] = 0; }
DbCommand dbCmd = _db.GetStoredProcCommand(sp_XYProc);
_db.AddInParameter(dbCmd, "Χ", DbType.Double, XTxtValue); _db.AddInParameter(dbCmd, "Υ", DbType.Double, "0" );
//int rowsAffected = _db.UpdateDataSet(this.Data, "Math", null, dbCmd, null, // Microsoft.Practices.EnterpriseLibrary.Data.UpdateBehavior.Transactional);
_db.ExecuteNonQuery(dbCmd, trans); trans.Commit(); } catch (DataException ex) { trans.Rollback(); GC.Collect(); Helper.ProblemShow(ref ex); } catch (Exception ex) { trans.Rollback(); GC.Collect(); Helper.ProblemShow(ref ex); } finally { dbconn.Close(); GC.Collect(); LoadData(); } }
To Db είναι μια σύνδεση μέσω του EntLib 2.0, έφτιαξα και μια proc η οποία κάνει 100000000 φορές update στην τιμή Υ, στον SQL 2005 και τρέχω το παραπάνω κώδικα, ταυτόχρονα. ΕΥΕΛΠΙΣΤΩΝΤΑΣ, ότι ναι θα δώ το λάθος, και θα κάνει rollback & θα μου πεί ενα με ωραίο μήνυμα, λυπάμαι χάσατε! το update δεν μπορεί να γίνει...ομως μάταιο. Το χόντρινα περισσότερο βάζωντας τον κώδικα σε ένα ατέρμονο βρόχο (while(true).....loop) ώστε μήπως και είναι θέμα τύχης το να συμπέσουν οι ανανεώσεις στην γράμμη στο ίδιο δευτερο... Μάταιο και αυτό. Δεν θα έπρεπε να χα κλειδώσει την εγγραφή που θέλω να ανανεώσω; Δεν θα έπρεπε να δώ μήνυμα λάθους;
Διαβάζω για μια μέθοδο ( δεν βρίσκω όμως καλά links) και μιλάνε για timestamps, δεν καταλαβαίνω, πρέπει να κάνω select πρίν κάνω την γραμμή update? οταν λέμε timestamp, τι ενοούμε; Δευτερόλεπτα ή χωρίς δευτερόλεπτα, και αν κρατήσω το 11:59 και μέχρι να γίνει το update είναι 12:00, τότε τι ;
Υπάρχεί καποιο tutorial για να δούμε πώς λειτουργεί το θέμα αυτό;
Δοκίμασα και με System.Transactions (Scope etc) το μόνο που πέτυχα είναι να αποτύχει η ανανέωση λόγω timeout :-( Οπως θα είδατε ίσως, χρησιμοποίησα και του EntLib την ανανέωση του dataset με transactional behavior....τζίφος :-(
Σας ευχαριστώ εκ των προτέρων... Δημήτρης
|
|
-
27-01-2008, 10:35
|
-
KelMan
-
-
-
Μέλος από τις 03-11-2004
-
Planet Earth
-
Δημοσιεύσεις 2.851
-
-
|
Απ: Transactions C# SQL express 2005
Δεν κατάλαβα τι προσπαθείς να κάνεις και ποιός είναι ο σκοπός του παραπάνω, ωστόσο προκειμένου να αποτύχει λόγω locking το transaction θα πρέπει να βρει στον server locks που προέρχονται από άλλο session. Εξάλλου, τα errors που εμφανίζονται στον server δεν είναι όλα fatal. Μερικά δεν προκαλλούν rollback γιατί θεωρούνται non-fatal, είναι το λεγόμενο severity level που παίζει ρόλο. Το πιο απλό πράγμα που μπορείς να κάνεις για να δοκιμάσεις τον υπάρχον κώδικα είναι να τρέξεις δύο φορές το πρόγραμμα για να έχεις δύο διαφορετικά connections ώστε τελικά να δεις τη συπεριφορά του locking.
Vir prudens non contra ventum mingit
|
|
-
27-01-2008, 11:13
|
-
m6s
-
-

-
Μέλος από τις 01-06-2007
-
Αθήνα
-
Δημοσιεύσεις 151
-
-
|
Απ: Transactions C# SQL express 2005
Μα δεν υποτίθεται, ότι αν πας να πηράξεις την γραμμή απο οποιοδήποτε client, το πρόγραμμα έχει Isolation.Seriazable, οπότε δεν επιτρέπει εξωτερικό πείραγμα; Ο σκοπός δεν είναι τα δεδομένα, να είναι προστατευμένα μεταξύ, update,insert, delete διαφορετικών session της εφαρμογής. Αλλά γενικότερα. Concurency,νομίζω ότι είναι ένας όρος για αυτό; Που με τα transactions δεν επιτυγχάνεται;
|
|
-
27-01-2008, 16:25
|
-
KelMan
-
-
-
Μέλος από τις 03-11-2004
-
Planet Earth
-
Δημοσιεύσεις 2.851
-
-
|
Απ: Transactions C# SQL express 2005
Τα isolation levels έχουν να κάνουν με το locking. Όσο πιο "αυστηρό" είναι το isolation level, τόσο πιο εκτενή είναι τα locks. Αυτό όμως δεν σημαίνει απαραίτητα ότι θα πάρεις κάποιο exception (σε επίπεδο ADO.NET) καθώς σε πρώτη φάση οι clients που προσπαθούν να προσπελάσουν locked rows θα περιμένουν (οπότε μιλάμε για blocking) που το πολύ-πολύ να σε οδηγήσει σε timeout exception. Για να λάβεις exception λόγω του locking θα πρέπει να οδηγηθείς σε deadlock κατά το οποίο ο SQL Server μόλις το ανιχνεύσει θα κάνει kill το victim process και μοιραία θα ρίξει το connection. To concurrency - στην περίπτωση των exceptions - δεν έχει να κάνει με τον SQL Server αλλά με το ADO.NET δηλαδή είναι ένα error που δεν συμβαίνει σε επίπεδο SQL Server αλλά το δημιουργεί ο TableAdapter με μία λογική του τύπου "αν το update της εγγραφής με id=123 φέρει 0 updated rows κάνε raise το ConcurrencyException".
Τελικά όμως δεν είπες τι προσπαθείς να κάνεις...
Sidenote: Δεν είναι ανάγκη να κάνεις GC.Collect. Για την ακρίβεια είναι μια τακτική που αντενδείκνυται. Είναι μια διαδικασία που έχει σημαντικό κόστος κατά την εκτέλεσή της και αν όλοι οι developers βάζουμε GC.Collect, τότε τελικά αν εκτελλούνται μερικές εφαρμογές ταυτόχρονα, θα καταντήσει ο υπολογιστής να μην κάνει και τίποτε άλλο...
Vir prudens non contra ventum mingit
|
|
-
27-01-2008, 16:47
|
-
m6s
-
-

-
Μέλος από τις 01-06-2007
-
Αθήνα
-
Δημοσιεύσεις 151
-
-
|
Απ: Transactions C# SQL express 2005
Καταρχήν να σε ευχαριστήσω, που το προσπαθεις μαζί μου :-)
Τι εννοείς τι προσπαθώ να κάνω; Ο κώδικας το δείχνει νομίζω, ότι προσπαθώ να κάνω update μια εγγραφή, που προηγουμένως να την έχω "κλειδώσει", ώστε π.χ. μια stored procedure, να μην μπορεί να πειράξει την γραμμή, ότι και να γίνει. Με αποτέλεσμα να μου επιστρέψει λάθος, ο client (που μπορεί να είναι οτιδήποτε) ότι η εγγραφή ειναι κλειδωμένη, οτι η εγγραφή είναι υπο επεξεργασία απο κάποιο άλλο client ( π.χ. απο το προγραμματάκι μου ).
Θέλω με κάποιο τρόπο να κλειδώνονται όσο επεξεργάζονται οι εγγραφές. Ωστε αν το ένα τερματικό, τις επεξεργάζεται, κανείς άλλος να μην τις πειράζει... Νομίζω το κάνω, αρκετά ξεκάθαρο :-)
Επίσης, υπάρχουν κάποια tutorial? samples? γιατι απο την αναζήτηση, δεν βρίσκω παρόλο που βλέπω topics για transactions, τίποτα που να μην αγνοεί την κλειδωμένη εγγραφή.
Τέλος για την παρατήρηση σου, καλώς που μου το είπες, θα το κοιτάξω. Απλά θέλω να είμαι σίγουρος ότι "τώρα" και όχι αυριο θα καθαρίσει την μνήμη.
|
|
-
27-01-2008, 17:07
|
-
KelMan
-
-
-
Μέλος από τις 03-11-2004
-
Planet Earth
-
Δημοσιεύσεις 2.851
-
-
|
Απ: Transactions C# SQL express 2005
Ωραία, τώρα κατάλαβα (όχι, δεν ήταν ξεκάθαρο, άλλο πράγμα το τι κάνει ο κώδικας και άλλο το τι προσπαθείς να κάνεις).
Λοιπόν, αρχικά να σου πω ότι ΔΕΝ γίνεται αυτό που προσπαθείς να κάνεις. Το ADO.NET είναι disconnected πράγμα που σημαίνει ότι λειτουργεί κάπως έτσι:
- Ανοίγεις το connection
- Τραβάς τα data και πλέον τα έχεις τοπικά
- Κλείνεις το connection
- Επεξεργάσεσαι τα τοπικά data
- Ξανανοίγεις το connection
- Ενημερώνεις τα data της βάσης σύμφωνα με τα τοπικά
- Κλείνεις το connection
Αυτό δείχνει ότι από τη στιγμή που κλείνεις το connection δεν μπορείς να έχεις locked εγγραφές. Επίσης, όπως σωστά αντιλαμβάνεσαι, ανάμεσα στο 4 και το 6 μπορεί κάποιοι άλλοι να έχουν πειράξει τα data οπότε να έχεις πλέον λανθασμένη εικόνα του τι υπάρχει στη βάση. Για παράδειγμα μπορεί να έχεις κάνει update μια εγγραφή που πλέον κάποιος άλλος την έχει κάνει delete. Ουσιαστικά, στον κώδικά σου βάζεις locks μόνο τη στιγμή που λες
_db.ExecuteNonQuery(dbCmd, trans);
Οπότε τι γίνεται πλέον στην περίπτωση που αναφέραμε; Είναι μια περίπτωση conflict και θα πρέπει να τη χειριστείς με business λογική. Αν βάλεις στις μηχανές αναζήτησης "ADO.NET concurrency" θα βρεις αρκετό υλικό. Επίσης, ρίξε μια ματιά εδώ: http://www.dotnetzone.gr/cs/blogs/mkelaiditis/pages/9899.aspx
Vir prudens non contra ventum mingit
|
|
-
27-01-2008, 17:13
|
-
KelMan
-
-
-
Μέλος από τις 03-11-2004
-
Planet Earth
-
Δημοσιεύσεις 2.851
-
-
|
Απ: Transactions C# SQL express 2005
Ως προς το GC.Collect, δεν έχει αυτή τη φιλοσοφία. Θα πρέπει να αφήσεις το Framework να κάνει το garbage collection όταν αυτό θεωρεί ότι πρέπει. Υπάρχουν άλλες τεχνικές για να καθαρίσεις τη μνήμη από άχρηστα objects αν όντως θες να κάνεις κάτι τέτοιο. Το GC.Collect δεν επηρεάζει μόνο την εφαρμογή σου, επηρεάζει όλες τις .NET εφαρμογές που τρέχουν παράλληλα. Έχουμε κάνει αρκετές συζητήσεις γι αυτό.
Vir prudens non contra ventum mingit
|
|
-
27-01-2008, 21:48
|
|
Απ: Transactions C# SQL express 2005
Να προσθέσω σε όσα έγραψε ο Μανώλης, ότι το ADO.NET δεν φέρεται με αυτό τον τρόπο από παραξενιά. Η λογική των disconnected recordsets υπάρχει ήδη από την Visual Basic 6 και είναι απαραίτητη αν θέλεις η εφαρμογή σου να μπορεί να υποστηρίξει παραπάνω από λίγες (πολύ λίγες) δεκάδες clients/τερματικά. Για web εφαρμογές, ακόμα περισσότερο. Ο λόγος είναι ότι όσο περισσότερο κρατάς ανοικτά transactions, τόσο περισσότερες εγγραφές κλειδώνονται και οι υπόλοιποι clients αναγκάζοται να περιμένουν να τις ελευθερώσεις εσύ. Θέλει εξαιρετική σε όλα τα κομμάτια κώδικα που ακουμπάνε τη βάση και απ' όλους τους προγραμματιστές για να αποφύγεις τέτοια λάθη. Έχω συναντήσει εφαρμογές οι οποίες καταφέρνανε να σέρνονται με 10 χρήστες ή και λιγότερους. Γενικά, αν θέλεις να έχεις καλή απόδοση, πρέπει να αφήνεις ανοικτά transactions όσο λιγότερο γίνεται. Transactions μεγάλης διαρκείας δημιουργούν προβλήματα ακόμα και σε βάσεις όπως οι Oracle και SQL Server οι οποίες επιτρέπουν να διαβάσεις μία παλιότερη έκδοση των δεδομένων όταν κάποιος τα έχει κλειδώσει για τροποποίηση. Αυτή η παλιότερη έκδοση των δεδομένων κάπου πρέπει να αποθηκευθεί, είτε αυτό είναι log file ή η temporary database. Η χρήση του transaction ως "εύκολου" τρόπου για κλείδωμα εγγραφών μπορεί να οδηγήσει σε πολύ μεγάλα προβλήματα, πολύ γρήγορα. Αν κάποιος ξεχάσει ανοικτή τη φόρμα και πάει για καφέ? Αν κάποιος θέλει δεδομένα τα οποία δεν εμφανίζονται στη φόρμα, αλλά έχουν κλειδωθεί παρόλα αυτά? Αν το query που τραβάει τα δεδομένα περιλαμβάνει joins μεταξύ πινάκων? Θα κλειδώσουν όλοι. Αν στους πίνακες που κλειδώνουν περιλαμβάνονται και κάποιοι πίνακες που χρησιμοποιούνται συχνά, π.χ. lookup πίνακες, μπορεί ένα τερματικό να κλειδώσει κάποιο άλλο παρότι δεν κοιτάνε π.χ. τον ίδιο πελάτη, απλά γιατί οι πελάτες αυτοί ανήκουν στην ίδια κατηγορία πελατών.
Αν θέλεις να "κλειδώσεις" κάποια εγγραφή υπάρχουν διάφοροι, πολύ πιο αποτελεσματικοί τρόποι. Ένας τρόπος είναι να κρατάς σε κάποιο κεντρικό σημείο ποιές είναι οι κλειδωμένες εγγραφές. Ο καλύτερος τρόπος είναι να φτιάξεις ένα server component (WCF ή Enterprie Service component) το οποίο θα κρατάει μία λίστα εσωτερικά και θα απαντάει σε όποιον το ρωτάει αν μία εγγραφή είναι κλειδωμένη ή όχι. Αν έχεις εφαρμογή client/server, μπορείς να χρησιμοποιήσεις ένα πίνακα γι αυτό το σκοπό, μόνο που τότε θα πρέπει να φροντίσεις να καθαρίζεις περιοδικά τον πίνακα από κλειδώματα που "ξεχάστηκαν", είτε γιατί έκλεισε απότομα η εφαρμογή, είτε γιατί έπεσε ο server, είτε γιατί ο χρήστης έκλεισε τον υπολογιστή του κλπ.
Πέρα από αυτό όμως, θα πρέπει να σκεφτείς το ΓΙΑΤΙ χρειάζεσαι το κλείδωμα? Κατά κανόνα, ο λόγος που θέλεις να "κλειδώσεις" μία εγγραφή, π.χ. ένα πελάτη, είναι γιατί δεν θέλεις να γίνουν αλλαγές στα πλαίσια μίας διαδικασίας που επηρεάζουν μία άλλη διαδικασία. Αν όμως κάποιος αλλάζει τη διεύθυνση του πελάτη, ενώ κάποιος άλλος (ή κάποιο άλλο κομμάτι κώδικα) αλλάζει την κατηγορία του πελάτη από απλό σε preferred customer, γιατί να μην μπορείς να τα κάνεις μαζί? Εδώ είναι που μπαίνει η έννοια του workflow το οποίο απλοποιεί και σχεδόν εξαλείφει την ανάγκη για κλείδωμα. Μπορείς να πεις για παράδειγμα ότι όταν ξεκινάει η "Αλλαγή των προσωπικών στοιχείων" θα εμποδίζεται η τροποποίηση και των στοιχείων επικοινωνίας αλλά δεν θα εμποδίζεται η αλλαγή π.χ. των οικονομικών στοιχείων. Σε εφαρμογές τύπου CRM όπου τα πάντα σχεδόν ξεκινάνε από την ίδια φόρμα πελάτη, αυτό αποτελεί φοβερή διευκόλυνση.
Έχει τύχει να δουλέψω και να φτιάξω πλατφόρμες στις οποίες υπήρχε και workflow και κλείδωμα. Το κλείδωμα είχε μπει κυρίως επειδή ο project manager δεν αισθανόταν βολικά με το workflow. Τελικά, σε ένα έργο τύπου CRM με μερικές εκατοντάδες διαδικασίες δεν χρησιμοποιήθηκε ποτέ το κλείδωμα αλλά μόνο το workflow.
Παναγιώτης Καναβός, Freelancer Twitter: http://www.twitter.com/pkanavos
|
|
-
27-01-2008, 22:39
|
-
m6s
-
-

-
Μέλος από τις 01-06-2007
-
Αθήνα
-
Δημοσιεύσεις 151
-
-
|
Απ: Transactions C# SQL express 2005
Πρός Kelman, θα τσεκάρω το λίνκ, και καταλαβαίνω τι λές περί των βημάτων.
Γενικα, Απλά έφθασα να μιλάω για "στεγνό" κλείδωμα επάνω στην εγγραφή γιατί βασικά είχα πάρει ανάποδες. "Πονάει κεφάλι, κόψει κεφάλι". Αυτό που θέλω να κάνω, είναι στην πραγματικότητα, όχι τόσο να κλειδώνω την εγγραφή, όσο το να αποτρέπεται ένα δεύτερο update εφόσον την ίδια εγγραφή, την έχω κάνει ήδη εγώ update. Να λέει δηλαδή, η εγγραφή μεταβλήθηκε. Εντάξει όχι να παγώνει τελείως το σύστημα. Τέλος πάντων απο εκεί κατέληξα στο να μην καταλαβαίνω γιατί ποτε δεν κλειδώνεται η εγγραφή.
Τώρα για το workflow, που αναφέρεις παναγιώτη ( επιτρέπεται με το μικρό;) είναι κάτι που υπάρχει σε κάποιο tutorial ? ή sample απο την microsoft ισως; Κάτι που να δώ ότι κάνει κάτι! "It's alive" ;-) γιατί με τα transactions... δεν έχω δεί τίποτα. Μόνο θεωρία... :-)
|
|
-
29-01-2008, 10:01
|
|
Απ: Transactions C# SQL express 2005
Με αυτά που λες δεν νομίζω ότι το χρειάζεσαι τελικά. Απλά να διαβάσεις λίγο για optimistic locking και πως δουλεύει. Αυτά που θα βρεις ΔΕΝ είναι θεωρία. Δεν υπάρχει περίπτωση να γράψεις σωστό κώδικα, ή να καταλάβεις τα samples αν δεν διαβάσεις και το υπόλοιπο άρθρο. Διάβασε για παράδειγμα τα 3 άρθρα κάτω από το Concurrency Control in .NET . Το πρώτο εξηγεί τί γίνεται γενικά με το concurrency και πως δουλεύει το disconnected μοντέλο και το optimistic concurrency και πως καταλαβαίνεις ότι έχει αλλάξει μία εγγραφή (στον SQL Server προσθέτεις ένα timestamp πεδίο). Το δεύτερο σου λέει τί κάνεις όταν καταλάβεις ότι άλλαξε η εγγραφή. Το τρίτο είναι το walkthrough. Αν δεν διαβάσεις όμως πρώτα το #1 δεν θα καταλάβεις τί είναι το optimistic locking, γιατί χρειάζεσαι το timestamp και γιατί πρέπει να πιάνεις τα DBConcurrencyExceptions.
Αξίζει επίσης να διαβάσεις και το άρθρο του John Papa στο MSDN Magazine, Handling Data Concurrency Using ADO.NET ή την πολύ συνοπτική περιγραφή του Christian Nagel :
"ADO.NET uses optimistic concurrency by default. The data-wizard generated code for an update statement checks for changes of the records by comparing the current values in the database with the original values inside the DataSet using a WHERE clause. Of course only the columns that are in the SELECT statement can be compared. In case another user changed a record before the update is invoked, an exception is thrown.
If the wizard is not used, the UPDATE statement must be programmed accordingly. Of course it is also possible to ignore all changes that happened from the time reading the data - just by doing a simpler UPDATE statement.
A simpler UPDATE statement with the same results as the data-wizard generated code can be done by using a timestamp-column where it is only needed to compare the timestamp-column of the original data.
With the default behavior it is important to mention if multiple records are updated, partial updates can happen. Before the conflict happens, the records are updated successfully, but because an exception is thrown, following records don't get updated even if there is no conflict. Of course this behavior can be changed.
If all or nothing should be updated, a transaction can be started before the DataAdapter.Update, and committed on success.
If all records without a conflict should be updated, there is a property in the DataAdapter, so that no exception is thrown: setting ContinueUpdateOnError to true does not generate exceptions with concurrency errors. Instead, the HasErrors and RowError properties of the DataSet and DataRow can be checked.
If pessimistic concurrency is needed, this is also possible (but should be avoided in distributed solutions).
Placing locks on the database can be done by starting a transaction before reading the data and filling the DataSet, and committing the transaction after invoking the update command. User interaction shouldn't happen in between, because here the transaction timeout can occur. Of course this is not scalable, and such a model be avoided. Until now I haven't seen such a scenario where DataSets would be useful.
Another approach is by using virtual locks: recording custom information into the database who has "locked" a record, and when. Before reading the data to do an update, the lock is checked. With this approach users can be grouped, and a supervisor may delete such virtual locks.
Using virtual locks is not useful in a disconnected scenario.
Optimistic concurrency usually is the best way updating data in a distributed, scalable solution."
Παναγιώτης Καναβός, Freelancer Twitter: http://www.twitter.com/pkanavos
|
|
-
30-01-2008, 00:09
|
-
m6s
-
-

-
Μέλος από τις 01-06-2007
-
Αθήνα
-
Δημοσιεύσεις 151
-
-
|
Απ: Transactions C# SQL express 2005
Σας ευχαριστώ και τους δυο, θα το ψάξω στα βήματα που μου είπατε. Καλησπέρα...
|
|
|
|
|