Καλώς ορίσατε στο dotNETZone.gr - Σύνδεση | Εγγραφή | Βοήθεια
σε

 

Αρχική σελίδα Ιστολόγια Συζητήσεις Εκθέσεις Φωτογραφιών Αρχειοθήκες

Διαβάζοντας ένα xml πεδίο σαν stream ...

Îåêßíçóå áðü ôï ìÝëïò anjelinio. Τελευταία δημοσίευση από το μέλος anjelinio στις 16-02-2007, 14:18. Υπάρχουν 3 απαντήσεις.
Ταξινόμηση Δημοσιεύσεων: Προηγούμενο Επόμενο
  •  16-02-2007, 12:33 25108

    Διαβάζοντας ένα xml πεδίο σαν stream ...

    Κάποια στιγμή στη δουλειά χρειάστηκε να αποθηκεύουμε την xml ενός DataSet σε ένα πεδίο στη βάση - κάτι σαν extended properties για το συγκεκριμένο row.

    Όταν λοιπόν θέλω να διαβάσω αυτή την xml και να φτιάξω ένα instance του DataSet απο το οποίο προήλθε ( είναι ένα typed DataSet ), πρέπει να διαβάσω αυτό το string ας πούμε απο τη βάση, και να το δώσω στο DataSet μου καλώντας την ReadXml(...).

    Η αρχική υλοποίηση χρησιμοποιούσε το παλιό DataAccess App. Block ως εξής:

    SqlParameter[] arParms = new SqlParameter[3];

    arParms[0] = new SqlParameter("@ContractActivityID", SqlDbType.Int);

    arParms[0].Value = contractActivityId;

    arParms[1] = new SqlParameter("@DataSetName", SqlDbType.VarChar, 120);

    arParms[1].Value = dataSetName;

    arParms[2] = new SqlParameter("@LoadOriginalData", SqlDbType.Bit);

    arParms[2].Value = loadOriginal;

                 

    string modXml = string.Empty;

     

    if( tran != null )

           modXml = msData.ExecuteScalar(tran, System.Data.CommandType.StoredProcedure, SPNAME_GetContractModificationData, arParms).ToString();

    else

           modXml = msData.ExecuteScalar(CISConfig.DatabaseConnectionString, System.Data.CommandType.StoredProcedure, SPNAME_GetContractModificationData, arParms).ToString();

                        

    return modXml;

    .. έτσι, έπαιρνε πίσω ολόκληρο το string, και μετά το έδινε στο instance του DataSet για να το διαβάσει.

    Τώρα, στην πορεία ανακαλύψαμε οτι αυτό το πράγμα μάλλον δεν ήταν και η καλύτερη υλοποίηση στον κόσμο ... αυτό το string μπορεί να ήταν αρκετά μεγάλο, και θα προτιμούσαμε να διαβάζουμε αυτή την xml σαν .. stream ? ... sequentially ? απο ένα DataReader. Θα προτιμούσα με άλλα λόγια να παίρνς πίσω ένα stream, το οποίο θα έδινα κατευθείαν στη ReadXml(...) χωρίς να μπαίνει στη μέση αυτό το string. Ίσως ήταν καλή η έμπνευση, ίσως και όχι ... αυτός είναι άλλωστε και ο λόγος για το συγκεκριμένο post.

    Η δεύτερη υλοποίηση λοιπόν:

    try {

           if(null!=tran) {

                  objCmd = new SqlCommand(cmdText, tran.Connection, tran);

           } else {

                  SqlConnection objConn =  new SqlConnection(CISConfig.DatabaseConnectionString);

                  objConn.Open();

     

                  objCmd = new SqlCommand(cmdText, objConn);

           }

           // ok execute the command now .. as a SequentialReader ...

           SqlDataReader reader = objCmd.ExecuteReader(CommandBehavior.SequentialAccess);          

           // hmmm ... now comes the hard part ... I need to create some sort of

           // weird way to create a stream reader or something off this "binary" data that I'll be reading ...

           if(reader.Read()){

                  int bufferSize = 100;                   // Size of the BLOB buffer.

                  byte[] outbytes = new byte[bufferSize];  // buffer chunk ...

                  MemoryStream bufferStream = new MemoryStream();

                  // ok, now loop & read from the reader, writing to the MemoryStream ...

                  long retval;                            // The bytes returned from GetBytes.

                  long startIndex = 0;                    // The starting position in the BLOB output.

                  // Read the bytes into outbyte[] and retain the number of bytes returned.

                  retval = reader.GetBytes(0, startIndex, outbytes, 0, bufferSize);

                  // Continue reading and writing while there are bytes beyond the size of the buffer.

                  while (retval == bufferSize) {

                         bufferStream.Write(outbytes, 0, bufferSize);

                         // Reposition the start index to the end of the last buffer and fill the buffer.

                         startIndex += bufferSize;

                         retval = reader.GetBytes(0, startIndex, outbytes, 0, bufferSize);

                  }

                  // hmmm .. perhaps the last batch wasn't exactly 100 bytes long ?

                  if(retval>0){

                         bufferStream.Write(outbytes, 0, (int)retval);

                  }

                  // cool ... at this point, I've all the data in the bufferStream, so I don't really need

                  // the Reader anymore ... so kill it. AND REWIND THE BUFFER STREAM !!!

                  reader.Close();

                  bufferStream.Position = 0;

                  // and now ... create a StreamReader to read-in the bufferData & feed the

                  // DataSet.ReadXml(...) method ...

                  using(StreamReader xmlReader = new StreamReader(bufferStream, System.Text.UnicodeEncoding.Unicode, true)){

                         try {

                               dataSetInstance.ReadXml(xmlReader);

                         }finally {

                               // Make sure I kill the bufferStream, otherwise it might

                               // consume memory for ever - or at least for a long time ...

                               bufferStream.Close();

                               bufferStream = null;

                         }

                  }

           }

    }

    finally{

        // Hopefully this will close the connection as well if required ...

        if(null!=objCmd){

            // what about the connection ??? I wonder ...

            if(null==tran){

                objCmd.Connection.Close();

            }                                       

            objCmd.Dispose();

            objCmd = null;

        }

    }

    ... τώρα, όπως είναι προφανές, δεν γλύτωσα το μέρος του buffering αυτών των δεδομένων, απλώς τώρα είναι σ'ενα memory stream το οποίο γεμίζει λίγο πιο .. ομαλά ... λίγα λίγα δεδομένα, και το σκοτώνω explicitly όταν πλεόν δε μου χρειάζεται.

    ΑΛΛΑ ... κέρδισα τίποτα; Ίσως λίγο scalability ?  Ίσως όμως και όχι. Και τελικά, δεν έλυσα το βασικό μου πρόβλημα, να ξεφορτωθώ το "buffer" είτε σε string είτε σε MemoryStream.

    'Εχει κανείς καμμιά τρίτη ιδέα;;;



    Angel
    O:]
  •  16-02-2007, 12:55 25110 σε απάντηση της 25108

    Απ: Διαβάζοντας ένα xml πεδίο σαν stream ...

    Δε μας είπες τι τύπο έχει η στήλη σου, αλλά αν είναι τύπου Xml θα σε βολέψει η SqlDataReader.GetSqlXml. Αυτή επιστρέφει τύπο SqlXml που μπορεί να δημιουργήσει XmlReader ή να πάρεις το περιεχόμενο σαν string.

    Νατάσα Μανουσοπούλου
  •  16-02-2007, 13:43 25112 σε απάντηση της 25108

    Απ: Διαβάζοντας ένα xml πεδίο σαν stream ...

    Ουσιαστικά, ρωτάς πως θα φορτώσεις ένα blob, ολόκληρο και μετά θα προσπαθήσεις να το χρησιμοποιήσεις. Όταν δουλεύεις με datasets, είσαι υποχρεωμένος να διαβάσεις ολόκληρο το blob και να το περάσεις στο dataset. Είτε φορτώνεις ολόκληρο το blob στην αρχή, είτε το φορτώνεις μέσω stream, στο τέλος χρειάζεσαι ολόκληρο το blob. Όταν το blob γίνεται πολύ μεγάλο, θα έχεις πρόβλημα είτε το φορτώνεις ολόκληρο με τη μία είτε σε chunks. Ίσως, με ένα συνδυασμό XmlReader και Stream να βελτιώσεις την ταχύτητα, ελπίζοντας ότι δεν θα χρειαστεί να διαβάσεις όλο το blob πριν βρεις αυτό που χρειάζεσαι. Γενικά όμως, αυτή η λύση δεν είναι scaleable και δημιουργεί συχνά τέτοια προβλήματα όταν χρησιμοποιείται για μεγάλο μέγεθος δεδομένων.

    Αν χρειάζεσαι οπωσδήποτε ολοκληρο το XML, δεν νομίζω ότι έχεις άλλη λύση πέρα από το να το φορτώσεις ολόκληρο. Θα πρέπει όμως να σκεφτείς αν υπάρχει λόγος να έχεις ένα τόσο μεγάλο XML για κάθε row. Συνήθως, πίσω από την αποθήκευση properties σε XML κρύβεται μία προσπάθεια να παρακαμφθούν προβλήματα στη σχεδίαση της βάσης, η οποία σχεδόν πάντα καταλήγει σε προβλήματα απόδοσης και scalability. Αν δεν χρειάζεσαι όλα τα δεδομένα του XML κάθε στιγμή, έχεις μία σαφή ένδειξη ότι η σχεδίαση της βάσης θέλει διόρθωση.
    Εδώ που τα λέμε, περίπου το ίδιο πράγμα θα πετύχαινες αν έκανες serialize ένα dictionary από αντικείμενα σε ένα πεδίο στη βάση. Θα έβγαινε μικρότερο από το dataset αλλά και πάλι κάποια στιγμή θα είχες πρόβλημα.

    Στις περιπτώσεις που πραγματικά χρειάζεται να αποθηκεύση κανείς ένα blob, η καλύτερη λύση είναι να το φορτώνει μόνο όταν το χρειάζεται, ουσιαστικά μόνο για ένα row τη φορά.  Αν έχεις SQL 2005 και το πεδίο είναι XML έχεις τη δυνατότητα να χρησιμοποιήσεςι XPATH queries για να επιστρέψεις μόνο τα δεδομένα που χρειάζεσαι κάθε φορά. Σίγουρα όμως θα είναι πιο βαρύ από μία σωστή δομή πινάκων για να αποθηκεύσεις properties.

    Καταλήγοντας, η XML ή τα BLOBS δεν είναι καλή λύση για να αποθηκεύσεις extended properties, εκτός και αν ο αριθμός τους είναι μικρός. Αυτό δεν οφείλεται στο σχεσιακό μοντέλο ή την XML ή τον τρόπο με τον οποίο τα υλοποιεί ο SQL Server. Αναγκαστικά, όσο αυξάνεται το μέγεθος του blob που πρέπει να διαβάσεις, τόσο πιθανότερη θα είναι η εμφάνιση προβλημάτων scalability. Η μόνη λύση είναι να αποφύγεις τα μεγάλα blob.


    Παναγιώτης Καναβός, Freelancer
    Twitter: http://www.twitter.com/pkanavos
  •  16-02-2007, 14:18 25113 σε απάντηση της 25110

    Απ: Διαβάζοντας ένα xml πεδίο σαν stream ...

    Νατάσα Μανουσοπούλου:
    Δε μας είπες τι τύπο έχει η στήλη σου, αλλά αν είναι τύπου Xml θα σε βολέψει η SqlDataReader.GetSqlXml. Αυτή επιστρέφει τύπο SqlXml που μπορεί να δημιουργήσει XmlReader ή να πάρεις το περιεχόμενο σαν string.


    ... ναι, αλλά ... μου επιστρέφει αυτό το xml όπως θα το επέστρεφε ας πούμε ένα BufferedStreamReader της Java ? Η απλώς φορτώνει ολόκληρο το string όπως και νά 'χει, κι απλώς μου δίνει ένα XmlReader πάνω του;

    oh .. τί πρόσεξα τώρα ... είμαι παγιδευμένος σε 1.1 ( και κλαίω γι'αυτό ) ... δεν έχω έτσι κι αλλιώς GetSqlXml(...) ...



    Angel
    O:]
Προβολή Τροφοδοσίας RSS με μορφή XML
Με χρήση του Community Server (Commercial Edition), από την Telligent Systems