C# Class Library Example: जिस प्रकार से हमने Connected Layer के लिए Common Functionalities को Perform करने के लिए एक Class Library Create किया था, उसी तरह से हम इस Disconnected Layer के लिए Command Functionalities को Perform करने के लिए भी Class Library Create कर सकते हैं।
चूंकि जब हम Disconnected Layer के लिए Common Functionalities को Library के रूप में Manage करते हैं, तब हमें Connection को Open/Close करने की जरूरत नहीं होती, क्योंकि इस काम को DataAdapter द्वारा स्वयं ही अपने स्तर पर Perform किया जाता है, जबकि हमें केवल ConnectionString ही Specify करना होता है।
इसलिए इस Library में एक Custom Constructor Specify करते हुए हम निम्नानुसार Connection String को एक Private Variable में Store कर सकते हैं, ताकि उस ConnectionString को Library के अन्य Methods द्वारा उपयोग में लिया जा सके:
using System; using System.Collections.Generic; using System.Data; using System.Data.SqlClient; using System.Data.Common; namespace NWCommon { public class DisconnectedLayer { // Field data. private string cnString = string.Empty; private SqlDataAdapter dAdapt = null; public DisconnectedLayer(string connectionString) { cnString = connectionString; // Configure the SqlDataAdapter. ConfigureAdapter(out dAdapt); } } }
ConnectionString को एक Private Field में Set करने के साथ ही इस Code में हमने ConfigureAdapter() नाम के एक Method को भी Constructor में ही Call कर लिया है, जो कि Parameter के रूप में out Type का एक DataAdapter Object का Reference Accept करता है।
चूंकि इस Parameter को out Keyword के साथ Specify किया गया है, इसलिए DisconnectedLayer Class का Object Create करते समय इस Constructor के Execution के साथ ही DataAdapter Object को हम जिन Values से Set करेंगे, उन Values को पूरी Class के अन्य Methods द्वारा भी समान रूप से Use किया जा सकेगा।
Configuring DataAdapter
जब हम DataAdapter Object के माध्यम से DataSet Object की DataTables के Data को Modify करते हैं, तो सबसे पहले हमें InsertCommand, UpdateCommand व DeleteCommand Properties को Valid Command Objects या null से Set करना जरूरी होता है।
InsertCommand, UpdateCommand या DeleteCommand Properties को Manually Configure करने पर हमें बहुत सारा Code लिखना पडता है, विशेष रूप से तब जबकि हम Command में Parameterized Queries को Use करते हैं।
इसलिए यदि हम चाहें तो लम्बा रास्ता Use करते हुए ConfigureAdapter() नाम का एक Method Create कर सकते हैं, जो कि इन तीनों Command Objects को Manually Define करता है। साथ ही Parameterized Query को Represent करने के लिए हम इसमें SqlParameter Objects का Set भी क्मपिदम कर सकते हैं।
लेकिन यहां हम केवल DataAdapter Object की Working को समझना चाहते हैं, इसलिए ConfigureAdapter() Method को निम्नानुसार Define कर रहे हैं, जहां हमने किसी भी Command को अलग से Specify नहीं किया है:
private void ConfigureAdapter(out SqlDataAdapter dAdapt) { // Create the adapter and set up the SelectCommand. dAdapt = new SqlDataAdapter("SELECT * FROM Products", cnString); // Obtain the remaining command objects dynamically at runtime // using the SqlCommandBuilder. SqlCommandBuilder builder = new SqlCommandBuilder(dAdapt); }
DataAdapter Object के Construction के लिए Microsoft द्वारा Provided ADO.NET Data Provider हमें CommandBuilder Type Provide करता है। इस CommandBuilder Object की विशेषता ये होती है कि हमें DataAdapter के केवल SelectCommand Property को ही Appropriate SQL Query से Set करना होता है।
जबकि अन्य तीनों Command Properties को CommandBuilder Object स्वयं ही SelectCommand Query के आधार पर Appropriate INSERT, UPDATE व DELETE Query से InsertCommand, UpdateCommand व DeleteCommand Properties को Initialize कर देता है। इस CommandBuilder का फायदा ये होता है कि हमें सभी SqlCommand व SqlParameter Types को Manually Create करने की जरूरत नहीं होती।
यहां एक सवाल ये पैदा होता है कि Command Builder को इस बात का पता कैसे चलता है कि उसे SELECT Statement के आधार पर अन्य Commands के लिए क्या SQL Command Create करना है। इस सवाल का सरल सा जवाब Metadata है। जब हम Runtime में DataAdapter के Update() Method को Call करते हैं, तो उस DataAdapter से सम्बंधित Command Builder, Database के Schema Data को Read करता है और उस Metadata के आधार पर Underlying Database में Data को Insert, Update या Delete करने के लिए Automatically Code Generate कर देता है।
हालांकि Command Builder के इस Automatically Command Generate करने की वजह से Underlying Database पर Extra Round Trip Perform होता है, जिसकी वजह से Database की Performance प्रभावित होता है। फिर भी यहां पर हमने ConfigureAdapter() Method Create करके Performance पर पडने वाले Negative प्रभाव को कम कर दिया है।
क्योंकि ये Method उसी समय Execute हो जाता है, जब DisconnectedLayer Class का Object Construct होता है और Construction के दौरान Create होने वाले DataAdapter Object के Reference को Class के Object की Lifetime तक Use करने के लिए उसे एक Private Variable में Store कर दिया है। परिणामस्वरूप DataAdapter Object के लिए सभी SQL Command Objects केवल एक ही बार Create होते हैं, जिन्हें DataAdapter Object की Lifetime तक बार-बार Reuse किया जाता है।
हालांकि Command Builder की वजह से हमें कुछ कम Coding द्वारा अधिक सुविधाऐं प्राप्त हो जाती हैं। फिर भी Command Builder की कुछ Special Restrictions भी हैं और इसकी मुख्य Restriction ये है कि Command Builder केवल उसी स्थिति में DataAdapter Object द्वारा Use करने के लिए Auto-Generated SQL Command Create कर सकता है, जबकि निम्नानुसार तीनों Conditions Satisfy होती हों:
- SELECT SQL Command केवल एक Single Table से Interact करता हो।
- Single Table में Primary Key हो।
- Single Table में एक या अधिक DataColumns जिस Primary Key को Represent कर रहे हों, उन्हें SQL SELECT Statement में Include किया गया हो।
GetAllProducts() Method Implementation
चूंकि अब हमारा DataAdapter Object पूरी तरह से तैयार है, इसलिए हम हमारे DataAdapter Object के साथ Fill() Method का प्रयोग करके Products Table के सभी Records को DataTable में Fetch कर सकते हैं और ये काम करने के लिए हमें निम्न Code लिखना होता है:
public DataTable GetAllProducts() { DataTable prod = new DataTable("Products"); dAdapt.Fill(prod); return prod; }
UpdateProducts() Method Implementation
जिस तरह से पिछले Code द्वारा हम हमारे DataTable Object को Underlying Database के Products Table के Data से DataAdapter Object के माध्यम से Fill करते हैं, उसी तरह से DataTable Object में किए गए Changes को Underlying Database की Products Table में Update करने के लिए भी हमें DataAdapter Object को ही Use करना होता है और ये काम करने के लिए हम निम्नानुसार एक Method Create कर सकते हैं:
public void UpdateProucts(DataTable modifiedTable) { dAdapt.Update(modifiedTable); }
जैसाकि उपरोक्त दोनों Methods के Codes को देखकर ही हम समझ सकते हैं कि कितने कम Codes लिखकर भी हम ADO.NET के Disconnected Layer का प्रयोग करके वही काम कर सकते हैं, जिसे Connected Layer में Perform करने के लिए हमें बहुत सारा Code लिखना पडा था। क्योंकि यहां हमें किसी प्रकार का Extra काम नहीं करना पडता, जैसाकि Connected Layers को Use करते समय करता पडता है।
उदाहरण के लिए जब हम DataAdapter Object के माध्यम से Underlying Database की Products Table के Data को Update करते हैं, तो DataAdapter Object स्वयं ही DataTable Object की विभिन्न Rows की RowState Values (RowState.Added, RowState.Deleted या RowState.Modified) के आधार पर इस बात का निर्ण; कर लेता है कि किन Rows को Underlying Database में Update करना है, जबकि Connected Layer में इस बात को Check करने की जिम्मेदारी भी कुछ हद तक हमारी ही होती है। इस प्रकार से यदि हम हमारी Disconnected Layer Library के विभिन्न Methods को देखें, तो हमारी Library के सभी Codes कुछ निम्नानुसार होंगे-
using System; using System.Collections.Generic; using System.Data; using System.Data.SqlClient; using System.Data.Common; namespace NWCommon { public class DisconnectedLayer { // Field data. private string cnString = string.Empty; private SqlDataAdapter dAdapt = null; public DisconnectedLayer(string connectionString) { cnString = connectionString; // Configure the SqlDataAdapter. ConfigureAdapter(out dAdapt); } private void ConfigureAdapter(out SqlDataAdapter dAdapt) { // Create the adapter and set up the SelectCommand. dAdapt = new SqlDataAdapter("SELECT * FROM Products", cnString); // Obtain the remaining command objects dynamically at runtime // using the SqlCommandBuilder. SqlCommandBuilder builder = new SqlCommandBuilder(dAdapt); } public DataTable GetAllProducts() { DataTable prod = new DataTable("Products"); dAdapt.Fill(prod); return prod; } public void UpdateProucts(DataTable modifiedTable) { dAdapt.Update(modifiedTable); } } }
एक बार Library के सभी जरूरी Codes लिखने के बाद हमें इसे Compile करना होता है। चूंकि Library एक Executable (.EXE) File नहीं होती, बल्कि Library की हमेंशा एक .DLL File Create होती है। इसलिए यदि हम इस Library को Use करते हुए अपना Windows Forms Based Application Design करना चाहें, तो इस Library को अपने Program में Use करने के लिए हमें दो काम करने होते हैं:
सबसे पहले हमें एक Windows Forms Application Create करना होता है और Newly Create होने वाले Project में उपरोक्तानुसार Create की गई Library की .DLL File का Reference Add करना होता है। क्योंकि बिना Reference Add किए हुए हम हमारे Application में उपरोक्त Library को Use नहीं कर सकते।
जब कि दूसरे काम के रूप में हमें हमारे Windows Forms Application का Source Code Create करते समय अपनी उपरोक्त Library के Namespace को using Keyword के माध्यम से Current Form पर Include करना होता है, ताकि हम Library में Define किए गए Properties व Methods Current Program में Use कर सकें।
यदि आप नई Library Create करने, Library के Reference को Visual Studio में Add करने व Namespace को Current Project में Use करने से सम्बंधित Basic Concepts के बारे में नहीं जानते, तो आपको “C#.NET in Hindi” पुस्तक पढनी चाहिए, जिसमें इन Concepts के बारे में विस्तार से Discuss किया गया है।
चूंकि हम इस Project में अपनी Library को Use करते हुए Underlying Database के Products Table को Access व Manipulate करना चाहते हैं, इसलिए सबसे पहले हमें हमारे Form को निम्नानुसार Design करना होता है:
इस Form को Design करने के बाद हम Save व Exit But के लिए Code लिखते हैं। परिणामस्वरूप Code लिखने के बाद हमारा पूरा Program कुछ निम्नानुसार बन जाता है:
using System; using System.Windows.Forms; using System.Data; using NWCommon; namespace UsingDisconnectedLibrary { public partial class frmUsingDL : Form { DisconnectedLayer dlObj = null; public frmUsingDL() { InitializeComponent(); string cnStr = "Data Source=KULDEEP\\SQLSERVEREXPRESS;Initial Catalog=Northwind;Integrated Security=True"; // Create our data access object. dlObj = new DisconnectedLayer(cnStr); // Fill up our grid! dgvProducts.DataSource = dlObj.GetAllProducts(); } private void btnExit_Click(object sender, EventArgs e) { this.Close(); } private void btnSave_Click(object sender, EventArgs e) { { // Get modified data from the grid. DataTable changedDT = (DataTable)dgvProducts.DataSource; try { // Commit our changes. dlObj.UpdateProucts(changedDT); } catch (Exception ex) { MessageBox.Show(ex.Message); } } } } }
जब हम इस Code को Specify करने के बाद अपने Windows Forms Application को Compile करके Run करते हैं, तो हमें हमारा Form कुछ निम्नानुसार दिखाई देता है:
इस Form के DataGridView Control में दिखाई देने वाले अन्तिम Record के अन्त में हमने स्वयं का एक नया Record Insert किया है। हालांकि हमने इस Record के सभी Columns में Data Fill किए हैं, लेकिन ProductID Field में कोई Data Insert नहीं किया है, क्योंकि किसी भी Table का ID Field हमेंशा Auto-Incremented रखा जाता है, जिसमें Appropriate मान Fill करने का काम Underlying Database Software स्वयं करता है।
इस नए Record को Underlying Database की Products Table में Save करने के लिए हम जैसे ही Form पर दिखाई देने वाले Save Button पर Click करते हैं, हमारा Record Underlying Database में Save हो जाता है।
Record Save हुआ या नहीं इस बात का पता लगाने के लिए हम इस Form को Close करके फिर से Run कर सकते हैं। जब हम इस Form को फिर से Run करते हैं, तो अन्तिम Record के रूप में हमें निम्न चित्रानुसार एक और Address Record दिखाई देता है, जिसका ProductID 78 है, जबकि हमने हमारे Record को Insert करते समय ProductID Field में कोई ID Specify नहीं किया था:
ProductID 78 के साथ हमारे Record का Display होना इसी बात का Indication है कि हमारा Record Underlying Database में Save हो चुका है, इसीलिए Program को दुबारा Start करने पर हमारा Insert किया गया Record ProductID 78 के साथ Show हो रहा है।
यदि हमारा Record Underlying Database में Save नहीं होता, तो हमें ये Record दिखाई ही नहीं देता, क्योंकि Application Close करते ही DataGridView Control की Memory के सभी Data Memory ls Remove हो जाते हैं।
हम समझ सकते हैं कि Library का प्रयोग करके कितना कम Code लिखते हुए भी हम एक Working Database Application Create कर सकते हैं। चलिए, हम इस Code की Internal Working को थोडा Detail से समझने की कोशिश करते हैं।
अपनी Newly Created NWCommon नाम की Library का Reference Current Project में Add करने के बाद सबसे पहले हमने हमारे इस Program में निम्न Statement }kk अपनी Library को Include किया है, ताकि हम हमारी Library में Specify किए गए Class का Object Create कर सकें व उस Object के लिए Define किए गए Methods व Properties को Access व Manipulate कर सकें:
using NWCommon;
इस Statement हमने निम्न Statement द्वारा DisconnectedLayer Type का एक Object dlObj Create किया है। इस Class को हमने NWCommon नाम की Library में Define किया है, जो कि Reference Set करने व उपरोक्त Namespace को Use करने के कारण Current Project में Use करने के लिए Available है:
DisconnectedLayer dlObj = null;
फिर हमारे Form के Constructor में निम्नानुसार Method Execute होता है, जो Form के विभिन्न Components को Initialize करता है, ताकि हमारा Form Working State में आ सके और Screen पर Normally Display होते हुए काम कर सके:
InitializeComponent();
अब निम्नानुसार ConnectionString को Specify किया है, जिसके आधार पर DataAdapter Object Underlying Database को Identify करने में सक्षम हो पाता है:
string cnStr = "Data Source=KULDEEP\\SQLSERVEREXPRESS;Initial Catalog=Northwind;Integrated Security=True";
ConnectionString Specify करने के बाद अब हमने हमारे DisconnectedLayer Type के Object dlObj को निम्नानुसार Statement द्वारा Underlying Database से Connect किया है:
// Create our data access object.
dlObj = new DisconnectedLayer(cnStr);
जब ये Statement Run होता है, तो हमारी Library में Specified DisconnectedLayer Class का Constructor Execute होता है, जो कि निम्नानुसार Defined है:
public class DisconnectedLayer { // Field data. private string cnString = string.Empty; private SqlDataAdapter dAdapt = null; public DisconnectedLayer(string connectionString) { cnString = connectionString; // Configure the SqlDataAdapter. ConfigureAdapter(out dAdapt); } . . . }
परिणामस्वरूप Form के Constructor में Specify किया गया हमारा ConnectionString हमारी Library के DisconnectedLayer Class के cnString Object में Store हो जाता है, ताकि उस पूरी Class द्वारा इसी Common ConnectionString को जरूरत के अनुसार बार-बार Reuse किया जा सके।
साथ ही निम्नानुसार Define किया गया ConfigureAdapter() Method Execute होता है और हमारे Current DisconnectedLayer Object के लिए एक DataAdapter Object Create करके Configure करता है, जो कि Underlying Database के Products Table के Data को Access व Manipulate करने के लिए विभिन्न SelectCommand, InsertCommand, UpdateCommand व DeleteCommand Properties को Setup करता है तथा इस Setup किए गए DataAdapter Object का Reference भी एक Private Property के रूप में DisconnectedLayer Class में Store हो जाता है, जो कि इस Class के Object की Lifetime तक Object द्वारा Use होने के लिए Available रहता है।
private void ConfigureAdapter(out SqlDataAdapter dAdapt) { // Create the adapter and set up the SelectCommand. dAdapt = new SqlDataAdapter("Select * From Products", cnString); // Obtain the remaining command objects dynamically at runtime // using the SqlCommandBuilder. SqlCommandBuilder builder = new SqlCommandBuilder(dAdapt); }
इस Code के Run होने तक हमारा Form पूरी तरह से Setup हो जाता है, लेकिन Form पर स्थित DataGridView Control में Data Filled नहीं होता, जिसे Underlying Database की Products Table के Data से Fill करने के लिए हमारे Main Program का निम्न Statement Execute होता है:
// Fill up our grid!
dgvProducts.DataSource = dlObj.GetAllProducts();
चूंकि .NET में DataGridView Control एक ऐसा Control है, जिसकी DataSource Property को Directly किसी DataSet या DataTable Object के Reference से Set किया जा सकता है और ये Control स्वयं ही DataTable/DataSet Object के आधार पर स्वयं के Structure को Define कर लेता है, इसलिए DataGridView Control को Underlying Database के Products Table के Data से Fill करने के लिए हमने dlObj Object के लिए GetAllProducts() Method को Call किया है। इस Method को भी हमारी Library के DisconnectedLayer Class में निम्नानुसार Define किया गया है:
public DataTable GetAllProducts() { DataTable prod = new DataTable("Products"); dAdapt.Fill(prod); return prod; }
परिणामस्वरूप जब ये Code Execute होता है, तो DataTable Type का prod नाम का एक Object Create होता है जो “Products” नाम का एक DataTable Create करता है। फिर जब DataAdapter Object के Fill() Method को Invoke करके इस prod नाम के DataTable Object को इस Method में Parameter के रूप में Pass किया जाता है, तो DataAdapter Object Underlying Database के Products Table के सभी Records को Retrieve करता है और उन Records को Parameter के रूप में Pass किए गए prod नाम के DataTable Object में Fill करके अन्तिम Statement के माध्यम से Calling Method को Return कर देता है।
परिणामस्वरूप Products Table का सारा Data Return होकर DataGridView Control की DataSource Property को Assign हो जाता है, परिणामस्वरूप हमारे Form पर DataGridView Control में हमें Underlying Database के Products Table का Data Show होने लगता है।
फिर जब हम इस DataGridView Control में दिखाई देने वाले Data को Update, Insert या Delete करते हैं और DataGridView Control में किए गए Changes को Underlying Database में Save करने के लिए Form पर दिखाई देने वाले Save Button पर Click करते हैं, तो Click Event के Response में Form का निम्नानुसार btnSave_Click() नाम का Event Handler Code Execute होता है:
private void btnSave_Click(object sender, EventArgs e) { { // Get modified data from the grid. DataTable changedDT = (DataTable)dgvProducts.DataSource; try { // Commit our changes. dlObj.UpdateProucts(changedDT); } catch (Exception ex) { MessageBox.Show(ex.Message); } } }
ये Code सबसे पहले DataTable Type का changedDT नाम का एक Object Create करता है और इस Object में हमारे DataGridView Control की DataSource Property में Stored Content यानी DataGridView Control में Stored सारे Content को इस changedDT नाम के DataTable Object में Store कर देता है तथा एक बार फिर DataAdapter Object dlObj को Use करते हुए इसके Update() Method को Call करता है और Parameter के रूप में changedDT नाम के DataTable Object को Pass कर देता है।
परिणामस्वरूप DataAdapter Object के लिए CommandBuilder द्वारा Defined InsertCommand, UpdateCommand व DeleteCommand Properties में Stored Appropriate SQL Statements Execute होते हैं तथा DataGridView Control में किए गए Changes को Underlying Database के Products Table पर Permanently Apply करते हैं। इस काम को हमारी DisconnectedLayer Class में निम्नानुसार Define किया गया UpdateProducts() नाम का Method Perform करता है:
public void UpdateProucts(DataTable modifiedTable) { dAdapt.Update(modifiedTable); }
इस प्रकार से हम हमारी Library के माध्यम से बहुत ही कम Codes का प्रयोग करते हुए Disconnected Mode में Underlying Database के साथ Interact कर सकते हैं और एक High Performing Database Application Create कर सकते हैं।
हालांकि हमारी इस Library में हमने केवल Products Table के लिए ही Functionality को Define किया है, जबकि हमारे Database Application में बहुत सारी Tables हो सकती हैं। उस स्थिति में हमें हर Table के लिए दो Methods Create करने होते हैं, पहला Table के Data को Access करने के लिए और दूसरा Table में Data को Update करने के लिए।
ये Article इस वेबसाईट पर Selling हेतु उपलब्ध EBook ADO.NET with C# in Hindi से लिया गया है। इसलिए यदि ये Article आपके लिए उपयोगी रहा, तो निश्चित रूप से ये पुस्तक भी आपके लिए काफी उपयोगी साबित होगी।
ADO.NET with C# in Hindi | Page:501 | Format: PDF