Execute Reader – ADO.NET Command Object

Execute Reader: जैसाकि हमने पिछले Section में देखा कि जब हमें Underlying Data Source से केवल एक Single Value Retrieve करना होता है, तब हम हमारे Command Object के साथ ExecuteScalar() Method का प्रयोग करते हैं।

लेकिन जब हमारे Command Object में Specified SQL Statement द्वारा एक से ज्यादा मान यानी Resultset Return हो रहा होता है, तो इस प्रकार के Resultset को Frontend में Retrieve करने के लिए ADO.NET हमें ExecuteReader() नाम का एक अलग Object Provide करवाता है।

यानी जब हमें Underlying Data Source से Return होने वाले एक से ज्यादा मानों के समूह को Access व Manipulate करना होता है, तब ये जरूरी होता है कि हमारा Command Object IDbCommand Interface को Implement करे, जिसे Implement करने पर हमें इस Interface को Implement करने वाली Class में ExecuteReader() Method को Define करना जरूरी होता है और SQL Server व Oracle जैसे Specific .NET Data Providers को Use करते समय SqlCommand या OracleCommand Class में हमें ये Method के पहले से ही Implemented होने की वजह से Use करने के लिए Directly Available मिलता है।

अत: जब हम SqlCommand या OracleCommand Type का Object Create करते हैं, तब Underlying SQL Server या Oracle Data Source से Return होने वाले एक से ज्यादा Data के Resultset को Access व Manipulate करने के लिए हम Command Objects के साथ ExecuteReader() Method को Use कर सकते हैं।

जब हम ExecuteReader() Method का प्रयोग करते हुए SQL Server पर Batched Query Fire करते हैं अथवा Oracle Server पर REF CURSOR Fire करते हैं, तब हमें जो बहुत सारे Resultset Return होते हैं, उन Return होने वाले विभिन्न Resultset को Browse करने के लिए हमें NextResult() नाम का एक Method भी प्राप्त होता है।

IDataRecord Interface

ExecuteReader() Method हमें IDataReader Data Type का एक Object Return करता है जिसमें Fire होने वाली SQL Query के आधार पर Return होने वाले विभिन्न Records Stored होते हैं और ये IDataReader Object हमें Return होने वाले Resultset के विभिन्न Rows Columns पर Read-only / Forward-Only Fashion में Traverse करने की सुविधा Provide करते हैं।

IDataReader Interface, IDataRecordIDisposable नाम के दो Interfaces को भी Implement करता है, जबकि जैसाकि हम जानते हैं कि IDisposable Interface को Implement करने पर हमें Dispose() Method को भी Define करना जरूरी होता है, जो सभी Unmanaged Resources को Free, Release व Reset करने के लिए जिम्मेदार होता है। जबकि IDataRecord Interface ADO.NET Architecture द्वारा Return होने वाले Resultset के किसी Individual Record को Represent करता है।

इसलिए हम इस बात के लिए Ensure हो सकते हैं कि IDataRecord में वह Minimum Functionality Exist है, जिसे ADO.NET में किसी Row को Represent करने के लिए Implement करना Compulsory होता है। IDataRecord में निम्नानुसार कुछ महत्वपूर्ण Methods होते हैं जिन्हें Compulsory रूप से Implement करना जरूरी होता है:

Get{DataType}() Method

ये Methods वास्तव में विभिन्न Get Methods का Shortname है। यानी वास्तविक Methods का नाम GetInt32(), GetFloat(), GetBoolean() आदि हैं। ये Method पूरी तरह से Return होने वाले मान पर निर्भर करते हैं। यानी यदि Floating Point मान Return Retrieve करना हो, तो हम GetFloat() Method Use करते हैं, जबकि Boolean मान Return करने के लिए हमें GetBoolean() Method को Use करना होता है। जबकि यदि हमें Return होने वाले Data के Type का पता न हो, तो हम GetValue() नाम के Generic Method को भी Use कर सकते हैं, जो कि एक Object Data Type का मान Return करता है।

ये सभी Get Methods Int32 Type का एक Parameter Accept करते हैं। ये Int32 Value एक Column Ordinal होता है, जो कि किसी Record/Row से Retrieve होने वाले विभिन्न Columns में से किसी Particular Zero Based Column को Represent करता है। यानी Record के पहले Column का Index Number 0 होता है। इसलिए यदि हमें Return होने वाले Records के चौथे Column का मान Retrieve करना हो, तो हमें Parameter के रूप में मान 3 Specify करना होता है।

GetName() Method

यदि हमें किसी Particular Column के मान को उसके नाम से Retrieve करना हो, तो इस Method में Parameter के रूप में हम उस Column का नाम एक String Value के रूप में Specify करते हैं। ये Method GetOrdinal() Method के समान ही काम करता है।

GetOrdinal() Method

यदि हमें किसी Column के Ordinal यानी Database की Table में किसी Column का जो नाम Specified है, उस नाम की जरूरत हो, तो हम इस Method को Use कर सकते हैं। ये Method Exactly GetName() Method के समान काम करता है, जिसमें हमें Parameter के रूप में Column के नाम को एक String Value के रूप में Specify करना होता है।

IsDbNull() Method

Databases में किसी Unknown Value को Represent करने के लिए किसी Column में Null Store किया जाता सकता है। इसलिए इस Method का प्रयोग करके हम इस बात का पता लगा सकते हैं कि किसी Particular Column में Null Value है या नहीं। ये एक ऐसा Method है जो Parameter में Specify किए गए Zero Based Column Number को इस बात के लिए Check करता है कि उसमें Null Value है या नहीं और Return Value के रूप में एक Boolean मान Return करता है।

किसी Column को Directly Nullable Value Assign करने से पहले भी हमें इस Method को Use करके इस बात का पता लगा लेना चाहिए कि उसमें पहले से Null Value Exist है या नहीं। क्योंकि यदि जिस Column में हम Null Value Assign कर रहे हैं, उसमें पहले से Null हो, तो एक Exception Throw होता है। Performance बनाए रखने के लिए भी बेहतर यही रहता है कि IsDbNull() Method से Return होने वाली Value को System.DBNull.Value के साथ Compare कर लिया जाए।

चूंकि जब हम Recordset के रूप में Multiple Records प्राप्त करने के लिए ExecuteReader() Method को Use करते हैं, तब Return होने वाले Resultset के विभिन्न Records के विभिन्न Columns को Access व Manipulate करने के लिए हमारे पास IDataRecord Type का Object होता है, इसलिए इस Object के साथ किसी उपयुक्त Method का प्रयोग करके हम Record के किसी Column के मान को निम्नानुसार तरीके से Access कर सकते हैं:

IDataRecordInstance[“ColumnName”];         //Using Column Name
IDataRecordInstance[0];                                   //Using Column Ordinal

हालांकि हम चाहें तो Column Name का प्रयोग करते हुए भी किसी Record के विभिन्न Columns को ज्यादा आसानी से Access कर सकते हैं। लेकिन Column Name String Format में होता है जबकि Computer में Integer Values को ज्यादा तेज गति से Process किया जा सकता है। इसलिए यदि हम Zero Based Column Ordinal को Use करते हैं, तो Database से Return होने वाले Data को ज्यादा तेज गति से Access कर सकते हैं।

साथ ही यदि हमें किसी Record के सभी Columns को एक साथ Access करना हो, तो Zero Based Column Ordinal तरीके का प्रयोग करके Looping के माध्‍यम से ये काम बडी ही आसानी से कर सकते हैं, जबकि Column Name को Use करने पर हम Looping का प्रयोग नहीं कर सकते।

इसके अलावा String एक Immutable Object होता है, इसलिए हम जितनी बार भी Column Name को String Format में Specify करते हैं, उस String को Store करने के लिए Computer को नया Memory Allocate करना पडता है साथ ही Integer की तुलना में ज्यादा Memory Allocate करना पडता है।

इन सभी कारणों की वजह से किसी Record के विभिन्न Columns को Zero Based Ordinal Value के माध्‍यम से Access करनाए Column के नाम को Use करने की तुलना में ज्यादा Performing होता है।

Ordinal Column उस स्थिति में और भी ज्यादा उपयोगी हो जाता है, जब हमारा Command Execute होने के बाद IDataRecord Objects का एक Collection Return करता है और हमें उस Collection को Iterate करते हुए विभिन्न Records के Columns की Values को Access करना होता है।

Performance Issues को ध्‍यान में रखते हुए हमें Loop में हमेंशा Int32 Indexer को Use करना चाहिए न कि String Indexer को। हम GetOrdinal() GetName() Methods को Column के Name व Ordinal के बीच Conversion करने के लिए भी उपयोग में ले सकते हैं, क्योंकि GetOrdinal() Method हमेंशा अपने Parameter के रूप में Specified Column Name का Index Ordinal Return करता है जबकि Get{DataType}() Methods हमेंशा अपने Parameter के रूप में Specified Index Ordinal का Column Name Return करता है

जब हम Underlying Data Source से बहुत सारे Records Retrieve करना चाहते हैं, तब हमें Command Object के Command Text के रूप में ऐसा SQL Statement Specify करना होता है, जो Underlying Data Source से एक से ज्यादा Records Retrieve करे साथ ही Retrieve होने वाले Multiple Records को Frontend Client में Hold करने के लिए भी एक Object की जरूरत होती है।

Underlying Data Source से IDataRecord के Collection के रूप में Resultset को Retrieve करने के लिए हमें DbCommand.ExecuteReader() Method को Use करना होता है। ये Method एक DbDataReader Object Return करता है। इसलिए यदि हम हमारे pubs Database के authors Table से सभी Authors की Details को Retrieve करना चाहें, तो हमें Create किए जाने वाले Command Object की CommandText Property में निम्न Query को Specify करना होगा:

        SELECT * FROM authors

इस Query वाले Command Object को हम निम्नानुसार Create कर सकते हैं:

        SqlCommand testCommand = new SqlCommand

        (

                “SELECT * FROM authors”,

                testConnection

        );

और क्योंकि ये SQL Statement Underlying Data Source से Multiple Records Retrieve करेगा, इसलिए इसे Execute करने के लिए हमें निम्नानुसार ExecuteReader() Method को Use करना होगा:

SqlDataReader allAuthors  =  testCommand.ExecuteReader(CommandBehavior.CloseConnection);

चूंकि ExecuteReader() Method Underlying Database से Frontend में एक से अधिक Records Return करता है, इसलिए इस Return होने वाले Multiple Records को Hold करने के लिए हमें SqlDataReader Type का Object Create करना होता है, जिसमें IDataReader Interface को Implement किया गया है साथ ही इस Class को DBDataReader से Inherit भी किया गया है।

यहां सवाल ये है कि ExecuteReader Method Directly IDataRecord Type का Collection Return क्यों नहीं करता बल्कि SqlDataReader Type का Object क्यों Return करता है। ऐसा करने के दो कारण हैं:

  • पहला कारण ये है कि SqlDataReader Class, IDataRecords Interface का Disconnected Cache नहीं है। फिर भी ये हमें एक Disconnected Cache Return करने की क्षमता Provide करता है। हम Database से जो Request करते हैं, इस Class का Default Behavior उस Request से कुछ ज्यादा Provide करता है। यानी ये Object Request Perform होने के बाद भी Database से Connected रहता है, ताकि SqlCommand Execute होने पर यदि कोई और Additional Matching Record प्राप्त हो, तो उसे Return कर सके।
  • दूसरा कारण ये है कि SqlDataReader, IDataRecords की तुलना में कहीं ज्यादा Versatile है। ये हमें न केवल Multiple Resultsets Return करने की सुविधा देता है, बल्कि BLOB जैसे Larger Data Columns के लिए ये Sequential Access को भी Support करता है और हमें इस प्रकार के Row/Columns के Data को Stream के रूप में On-Demand Read करने की सुविधा भी Provide करता है। क्योंकि इस प्रकार के Large Amount of Data को IDataRecords Collection में पहले से Load करना कई स्थितियों में Ideal नहीं होता।

Common Behavior Parameters of ExecuteReader() Method

जैसाकि हम पिछले Code में देख भी सकते हैं कि हमने ExecuteReader() Method में एक CommandBehavior.CloseConnection Parameter भी Pass किया है। जबकि यदि हम चाहें तो ExecuteReader() Method का एक Overloaded Version भी है, जिसे Use करने पर हमें इस Method में कोई Parameter Pass करने की जरूरत नहीं रहती।

इस Parameter को Pass करके हम इस बात को Ensure करते हैं कि जब Underlying Database पर हमारे Command Object में Specified SQL Statement पूरी तरह से Execute हो जाएगा, उसके बाद Underlying Data Source से Connected Connection Close हो जाएगा साथ ही SqlDataReader भी Close हो जाएगा। इस Parameter के अलावा कुछ और Common Behavior Options भी Available हैं, जिन्हें ExecuteReader() Method में Specify किया जा सकता है, जो कि निम्नानुसार हैं:

Default Parameter

ये Option, Functionally ExecuteReader() Method के समान ही होता है।

CloseConnection Parameter

जब Command Execution पूर्ण हो चुका होता है, तब DataReader Connection दोनों ही Close होते हैं। फिर DataReader.Close Query के Final Result जैसे कि RecroodsAffected आदि को Populate करता है। इसलिए Complicated Queries के लिए ये Closing Process Execute होने में कुछ समय लगता है। इस स्थिति में इस Parameter को Use करने के स्थान पर हम DataReader.Cancel को Call कर सकते हैं।

KeyInfo Parameter

ये Parameter DataReader को इस बात का Instruction देता है कि वह केवल Unique Columns व Primary Key को ही Retrieve करे।

SchemaOnly Parameter

ये Parameter Specify करने पर Data Reader केवल Underlying Data Source से Column Information को ही Return करता है।

SequentialAccess Parameter

ये Parameter इस बात को Specify करता है कि हम Data Reader के Return होने वाले Data को Sequentially Read करेंगे। इस Parameter को हम तब Use करते हैं, जब हमें BLOB या बडे XML Chunks को Read करना होता है। फिर भी OleDbDataReader हमें DataReader के Data को फिर से Read करने की सुविधा Provide करता है, जबकि SqlDataReader ऐसा नहीं करता। साथ ही जब हम इस Parameter को Specify करते हैं, तब हमें सभी Columns को Sequentially ही Access करना होता है।

SingleResult Parameter

चूंकि हम DataReader के साथ Batched Query को Specify करके Multiple Results प्राप्त कर सकते हैं और NextResult() Method का प्रयोग करके अगले Resultset को Access व Manipulate कर सकते हैं। लेकिन जब हम इस Parameter को Specify करते हैं, तब हम DataReader Object केवल एक Single Resultset ही Return करता है।

SingleRow Parameter

ये Parameter Specify करने पर हर Resultset से केवल एक ही Row, Frontend को Fetch होता है। साथ ही इस Parameter को Specify करने पर Command Execution रूक जाता है तथा SqlDataReader.Close को Call करने की जिम्मेदारी हमारी हो जाती है।

इस प्रकार से जब हमें पूरे Resultset के साथ प्रक्रिया करनी होती है, तब हमें SqlDataReader Object के साथ Deal करना होता है, जो कि एक अलग प्रकार का Object है। इसलिए SqlDataReader से Return होने वाले Data को Retrieve करने के लिए हमें एक अलग तरीके को Use करते हुए निम्नानुसार Code Segment का प्रयोग करना होता है:

        SqlDataReader allAuthors  =  testCommand.ExecuteReader(CommandBehavior.CloseConnection);
	if(allAuthors.HasRows)
	{
		while(allAuthors.Read())
		{
			Console.WriteLine("Author: " 
				+ allAuthors.GetInt32(0)
				+ allAuthors.GetInt32(1)
				…
				…
			);
		}
	}

इस Code में हमने सबसे पहले एक if Statement में Condition के रूप में DataReader Object की HasRows Property को Use किया है। इस Property में उसी स्थिति में कोई मान होता है, जबकि DataReader Object में Command Execute होने के बाद कोई Appropriate Record या Row Exist हो।

DataReader Object में Records Exist होने की स्थिति में Program Control if Statement Block में प्रवेश करता है और एक while() Loop में Forward Only Fashion में तब तक DataReader Object के सभी Records को One-by-One Read करता रहता है, जब तक कि DataReader Object के सभी Records Read नहीं हो जाते।

चूंकि DataReader में Stored सभी Records में Record Pointer या Cursor हमेंशा पहले Record पर Placed होता है, इसलिए Row को Read करने के लिए हमें DataReader Object के Read() Method को Use करना होता है।

साथ ही इस while Loop में जब तक Record Pointer किसी Particular Record को Point कर रहा होता है, तब तक हम उस Record के सभी Columns में Stored Data को GetInt32() या GetString() Methods का प्रयोग करके Read कर सकते हैं, जैसाकि उपरोक्त Code Segment में किया गया है।

इस प्रकार से यदि हम उपरोक्त Code Segment को एक Practical Example Program में Use करते हुए इसकी Proper Working देखना चाहें, तो हमारा Program कुछ निम्नानुसार हो सकता है:

using System;
using System.Data;
using System.Data.SqlClient;

namespace DBApplication {
    class Program {
        static void Main(string[] args) {
            SqlConnectionStringBuilder sqlConBuilder = new SqlConnectionStringBuilder
		("Data Source=.\\SQLSERVEREXPRESS;Initial Catalog=pubs;Integrated Security = true");

            SqlConnection testConnection = new SqlConnection(sqlConBuilder.ToString());
            SqlCommand testCommand = new SqlCommand
		("SELECT * FROM authors", testConnection);

            using (testConnection)
            {
                testConnection.Open();
                SqlDataReader allAuthors  = 
			testCommand.ExecuteReader(CommandBehavior.CloseConnection);

	            if(allAuthors.HasRows)
	            {
                           Console.WriteLine("Author ID\tAuthor Name\t\t\tPhone");
		            while(allAuthors.Read())
		            {
   	                     Console.WriteLine(
				allAuthors.GetString(0) + "\t" + 
				allAuthors.GetString(1) + " " +  allAuthors.GetString(2) + "\t\t\t" +
				allAuthors.GetString(3));
		            }
	            } 
            }
        }
    }
}

जब हम इस Program को Run करते हैं तो हमें निम्नानुसार Output प्राप्त होता है:

Execute Reader - ADO.NET Command Object - Hindi

इस Program की एक मुख्‍य रूप से ध्‍यान रखने वाली बात ये है कि जब हम किसी DataReader Object से Retrieve होने वाले Data को Console.WriteLine() या Console.Write() Method द्वारा Display करते हैं, तब सभी Columns के मानों को String Format में ही प्राप्त करना जरूरी होता है, क्योंकि ये दोनों Methods केवल String Data को ही Console में Display करते हैं।

इसीलिए हमने इस Program में Console.WriteLine() Method को निम्नानुसार Use किया है जिसमें Read किए जाने वाले Column के Data को GetString() Method का प्रयोग करते हुए String Format में Read किया है:

Console.WriteLine(
allAuthors.GetString(0) + “\t” +
allAuthors.GetString(1) + ” ” +  allAuthors.GetString(2) + “\t\t\t” +
allAuthors.GetString(3));

जबकि यदि हमें किसी Column के Data के साथ किसी प्रकार का Calculation Perform करना हो, तो हम किसी अन्य प्रकार के Get Method जैसे कि GetInt32(), GetFloat() आदि Methods को Use कर सकते हैं।

साथ ही DataReader को Use करते समय हमें इस बात का भी ध्‍यान रखना होता है कि DataReader एक Read-Only / Forward-Only Method है, इसलिए इस Object को Use करने पर हम किसी भी तरीके से Backward Data को Retrieve नहीं कर सकते।

हम SqlDataReader में किसी बडे Resultset को Test करने के लिए Use कर सकते हैं। ऐसे में यदि हम DataReader से किसी Record को Read करने के बाद एक Breakpoint Set कर दें और Underlying SQL Server को Stop कर दें और फिर उसी Breakpoint से DataReader को आगे Iterate करें, तो SQL Server Stop होने के बाद भी ये Object Database से Data को Retrieve करता रहता है।

ऐसा इसलिए होता है क्योंकि DataReader Object एक प्रकार से Double Sided Pipe की तरह होता है, जिसका एक सिरा Database से Connected रहता है, जबकि दूसरा सिरा हमारे Frontend Application से।

इसलिए यदि Database के सिरे को यानी Database Server को थोडी देर के लिए Off भी कर दिया जाए, तब भी Application के सिरे पर Data Continually आता रहता है। जबकि यदि हम एक ही बार में हजारों Records को Retrieve कर रहे हों और इस स्थिति में SQL Server को Off कर दिया जाए, तो उस स्थिति में Frontend एक Exception Generate करता है, जो इसी बात का Signal है कि उसका Connection Underlying Database से Disconnected है।

अत: जब हम Underlying Database से एक बार में केवल एक ही Record को Retrieve कर रहे होते हैं, तो उस Database के साथ Connection तब तक Open रहता है जब तक कि Database Accessing Logic Run हो रहा होता है साथ ही उस समय भी Open रहता है जबकि Frontend उस Logic के Run होते समय Result Retrieve कर रहा होता है। जबकि Logic Execute होने में Result Retrieve होने की तुलना में काफी कम समय लगता है।

यानी Database पर Logic के Run होते ही Result Generate होना शु: हो जाता है और Frontend में Result आना भी शुरू हो जाता है। लेकिन Network की Speed कम होने की वजह से Database पर Logic के पूरी तरह से Execute होकर पूरा Result Generate कर देने के बाद भी, जब तक वह सारा Resultset Frontend को प्राप्त नहीं हो जाता, तब तक Database पर Connection Open रहता है।

ADO.NET with C# in Hindi - BccFalna.com: TechTalks in Hindi ये Article इस वेबसाईट पर Selling हेतु उपलब्‍ध EBook ADO.NET with C# in Hindi से लिया गया है। इसलिए यदि ये Article आपके लिए उपयोगी रहा, तो निश्चित रूप से ये पुस्तक भी आपके लिए काफी उपयोगी साबित होगी। 

ADO.NET with C# in Hindi | Page:501 | Format: PDF

BUY NOW GET DEMO REVIEWS