Query Method and Expression Tree

Query Syntax के अलावा जो दूसरा तरीका Use किया जाता है, उसे Method Syntax या Query Methods के नाम से जाना जाता है और इन Methods को किसी भी Enumerable Object जैसे कि Array, Stack, List, Dictionary आदि पर Call किया जा सकता है।

विभिन्न Query Methods को System.Linq.Enumerable Namespace में Define किया गया है और Extension Methods की तरह Implement किया गया है, जो कि IEnumerable<T> की Functionality को Extend करते हैं। साथ ही Query Methods को System.Linq.Queryable Namespace में भी Define किया गया है, जो कि IQueryable<T> की Functionality को Extend करते हैं।

जैसाकि हम जानते हैं कि एक Extension Method वास्तव में किसी अन्‍य Class के Internal Codes को छेडे बिना व उसे Inherit किए बिना उसकी Functionality को Extend करने की क्षमता रखता है।

Enumerable Class हमें कई Query Methods Provide करता है, लेकिन Core में केवल उन्हीं Methods को Use किया जाता है, जो कि Query Keywords से सम्बंधित होते हैं। इन Methods को निम्न सारणी द्वारा Represent किया गया है:

Query Method and Expression Tree - Hindi

delegate TResult Func<in T, out TResult>(T arg)जैसाकि हम इस सारणी में देख सकते हैं कि join() Query Method के अलावा अन्‍य सभी Query Methods केवल एक ही Parameter Accept करते हैं, जो कि Func<T, TResult> Generic Type का ही कोई Implemented Form है। जो कि एक Built-In Delegate Type है जिसका Syntax निम्नानुसार है:

जहां TResult Delegate के Result Type को Specify करता है और T Element के Type को Specify करता है। इन Query Methods में selector, predicate या keySelector Argument इस बात को निश्चित करते हैं कि Query Method को क्या Action Perform करना है। उदाहरण के लिए यदि हम Where() Method को देखें, तो ये Method इस बात को Determine करता है कि Query Data को Filter कैसे करना है।

ये सभी Query Methods एक Enumerable Object Return करते हैं। अत: एक Object के Result को किसी दूसरे Object पर किसी Call को Execute करने के लिए Reuse किया जा सकता है। यानी हम ठीक उसी तरह से Methods की Chaining कर सकते हैं जिस तरह से jQuery में करते हैं।

जैसाकि हम उपरोक्त सारणी में देख सकते हैं कि Join() Method कुल चार Parameters Accept करता है। पहला Parameter Join होने वाले दूसरे Sequence का Reference है। जबकि पहला Sequence वह स्वयं है, जिसके साथ Join() को Call किया जा रहा है। पहले Sequence के KeySelector को outerKeySelector के माध्‍यम से Pass किया जाता है और दूसरे Sequence के Key Selector को innerKeySelector के माध्‍यम से Pas किया जाता है।

outerKeySelector Func<TOuter, TKey> Type का होता है जबकि innerKeySelector Func<TOuter, TInner, TKey> Type का होता है। जहां TOuter Invoking Sequence का Element Type होता है, जबकि TInner Parameter की तरह Passs होने वाले Sequence का Element Type होता है। जबकि TResult Resulting Element का Type होता है और Joining के बाद एक Enumerable Object Return होता है, जिसमें Joining का Result Stored होता है।

हालांकि Where() जैसे किसी Query Method में Pass किया जाने वाला Argument Func Delegate के Compatible होना चाहिए, लेकिन इसे Explicitly Declare करना जरूरी नहीं होता। वास्तव में तो हम अक्सर इसे Use ही नहीं करते बल्कि इसके स्थान पर हम Lambda Expressions Use करते हैं, जो कि C# में Anonymous Methods को Represent करने का बहुत ही Powerful तरीका है। यहां तक कि C# Compiler स्वयं ही किसी Lambda Expression को Automatically एक Appropriate Func Parameter में Convert करते हुए Where() जैसे किसी Query Method में Use कर लेता है

इस Chapter की शुरूआत में ही हमने Query Syntax का प्रयोग करते हुए एक Basic Program Create किया था, जिसमें LINQ Query को Specify किया था। उसी Program को यदि हम Method Syntax के आधार पर Query Method का प्रयोग करते हुए Recreate करें, तो हमारा Program कुछ निम्नानुसार हो सकता है:

File Name: MethodQueryExample.cs
using System;
using System.Linq;

namespace CSharpLINQ
{
    class SimpleQuery
    {
        static void Main()
        {
            int[] numbers = { 1, -2, 3, 0, -4, 5 };

            // Create a query that obtains only positive numbers.
            var posNums = numbers.Where(n => n > 0).Select(r => r);

            Console.Write("The positive values in numbers: ");

            // Execute the query and display the results.
            foreach (var i in posNums) Console.Write(i + " ");
        }
    }
}

Output:
	The positive values in numbers: 1 3 5

जैसाकि हम इस Program के Output द्वारा समझ सकते हैं कि ये Program भी Exactly वैसा ही Output दे रहा है, जैसा SimpleLINQExample.cs दे रहा था, जबकि SimpleLINQExample.cs Program को हमने Query Syntax का प्रयोग करते हुए Create किया था। जबकि इस Program को हमने Method Syntax का प्रयोग करते हुए Create किया है। तकनीकी रूप से देखा जाए तो इन दोनों ही Programs में Internally कोई अन्तर नहीं है। अन्तर केवल इतना ही है कि इस Program में हमने LINQ Query को निम्नानुसार तरीके से Specify किया है:

var posNums = numbers.Where(n => n > 0).Select(r => r);

var posNums = from n in numbers where n > 0 select n;

जैसाकि दोनों Statements को देखकर हम समझ सकते हैं कि दोनों ही Statements को Specify करने में भी काफी समानता है। क्योंकि C# Compiler एक Query Syntax को स्वयं ही Internally Method Syntax में Convert करने के बाद ही Execute करता है। इसलिए निम्न Statement%

where n > 0

वास्तव में C# Compiler द्वारा निम्न Statement में Convert करने के बाद ही Execute किया जाता है:

where(n => n > 0)

इस Program में Specified Statement में हमने numbers नाम के अपने Collection यानी Array के साथ Where() Query Method को Use किया है। चूंकि Where() Query Method एक प्रकार से Data Source यानी numbers नाम के Array से Retrieve होने वाले Data को Filter करने का काम करता है। इसलिए इस Method में हमने निम्नानुसार तरीके से एक Anonymous Method को Lambda Expression के रूप में Specify किया है:

n => n > 0

जो कि वास्तव में निम्नानुसार Delegate Anonymous Method के समान है:

result = delegate(int n) { n > 0; };

Where() Method को numbers नाम के Array के साथ इसलिए Invoke किया जा सकता है क्योंकि C# में हर Array IEnumerable<T> Interface को Internally Implement करता है, इसलिए Query Extension Methods को भी Support करता है। यानी जो Collections IEnumerable<T> Interface को Implement करते हैं, उन सभी में Query Extension Methods Supported होते हैं। जिसकी वजह से उन सभी Collections में Query Methods को Use किया जा सकता है।

Technically देखा जाए तो इस Program के LINQ Query Statement में Specified Select() Method की जरूरत नहीं है, क्योंकि हमारे उदाहरण में Where() Method जो Sequence Return कर रहा है, उसमें पहले से ही Result Contained है, लेकिन जब हम Where() Extension Method में ज्यादा Typical Selection Criteria Specify करते हैं, तब Select() Method को Use करना Compulsory हो सकता है। इसलिए किसी भी प्रकार की परेशानी से बचने के लिए हमें हमेंशा Select() Extension Method को Use कर ही लेना चाहिए।

चूंकि C# में हम दो तरीकों से किसी LINQ Query को Specify कर सकते हैं। जहां पहले तरीके में LINQ Query केवल एक Variable को Assign होता है और जब तक foreach Loop Execute नहीं किया जाताए तब तक ये LINQ Query किसी तरह का Result Generate नहीं करता। इस प्रकार की LINQ Query Execution को Deferred Execution कहा जाता है, जबकि हमने एक और तरीका देखा था, जिसमें Query को Specify करते ही वह Execute हो जाता है और Result Generate करता है। इस प्रकार के LINQ Query Execution को Immediate Execution के नाम से जाना जाता है।

Expression Tree

Expression Tree भी LINQ Related Feature ही है। Expression Tree वास्तव में किसी Lambda Expression का Data के रूप में Representation ही होता है। यानी जब हम किसी Executable Code को Data के रूप में Treat करते हुए उपयोग में लेते हैं, तो इस प्रकार के Code को Expression Tree के नाम से जाना जाता है।

चूंकि हम किसी Lambda Expression को जब Expression Tree के रूप में LINQ Query में Use करते हैं, तो इस प्रकार के Lambda Expression, जो कि वास्तव में Executable Code होता है, अपने स्तर पर Directly Executable Form में Convert नहीं हो सकता।

Expression Tree को System.Linq.Expressions.Expression<TDelegate> Class में Encapsulate किया गया है। Expression Trees उस स्थिति में काफी उपयोगी होते हैं, जब किसी Code को तुरन्त Execute नहीं करना होताए बल्कि Data के रूप में किसी अन्‍य Object में Pass करना होता है और वह अन्‍य Object उस Expression Tree में Specified Code को Execute करता है।

ठीक उसी तरह से जिस तरह से हम किसी SQL Database पर किसी SQL Query को Fire करने के लिए किसी C# Program में एक SQL Query Statement Create करते हैं और उसे Data के रूप में Database Server पर Pass कर देते हैं।

परिणामस्वरूप जब वह SQL Query Statement, C# Program में होता है, तब तक एक Normal Data की तरह होता है, लेकिन जब वह Database Server पर पहुंच जाता है, तो Database Server उसे Executable Code की तरह Execute करके उस SQL Query के आधार पर कोई Result Generate करता है।

अत: हम हमारे किसी C# Program में किसी Query Statement को Data की तरह Represent कर सकते हैं जिसे Database अपने समझने योग्‍य Format में Convert कर सकता है। इस Process को सामान्‍यत: LINQ से SQL Conversion के लिए Use किया जाता है। अत: Expression Tree, C# को विभिन्न प्रकार के Data Sources के लिए इस Conversion को Perform करने में मदद करता है।

हम किसी Defined Expression Tree को Executable Form में Convert करने के लिए Expression Type के Compile() Method को Use कर सकते हैं। ये Method एक Reference Return करता है, जिसे किसी Delegate को Assign करके Execute किया जा सकता है। हम हमारा स्वयं का भी Delegate Declare कर सकते हैं या फिर हम System Namespace में Defined Func नाम के Predefined Delegate को भी Use कर सकते हैं।

LINQ is .NET API

इस Chapter में हमने LINQ से सम्बंधित कुछ Basic बातों के बारे में समझने की कोशिश की है। हमने Chapter की शुरूआत में भी कहा था कि LINQ अपने आप में बहुत बडा Subject है और इस एक Single Chapter में हम LINQ से सम्बंधित सारी बातों को Describe नहीं कर सकते, क्योंकि LINQ वास्तव में कोई Programming Construct नहीं है, जिसके बारे में इस पुस्तक में कुछ नया सीखना हो बल्कि वास्तव में LINQ, Enumerators, Extension Methods व Delegate जैसे कुछ Basic Programming Constructs पर आधारित .NET API है।

इस API का प्रयोग करके हम हमारे C# Program में विभिन्न प्रकार के Collections को ठीक उसी तरह से Access व Manipulate करने की क्षमता प्राप्त कर लेते हैं, जिस तरह से किसी Relational Database की Tables से SQL Queries द्वारा Required Records को Retrieve किया जाता है।

साथ ही C# हमें विभिन्न प्रकार के Data Sources से Connect होने की सुविधा भी Provide करता है ताकि हम विभिन्न प्रकार के Data Sources में Stored Data को अपने C# Program में उपयोग में ले सकें। LINQ उन विभिन्न प्रकार के Data Sources में Stored Data को एक Standard तरीके से Access व Manipulate करने की सुविधा Provide करता है।

लेकिन इसका मतलब ये नहीं है कि हम विभिन्न प्रकार के Relational Databases या अन्‍य प्रकार के Data Sources के Data को C# Program में केवल LINQ के माध्‍यम से ही Access कर सकते हैं। वास्तव में LINQ हमें केवल इन विभिन्न प्रकार के Data Sources से Common Syntax द्वारा Data को Access व Manipulate करने की सुविधा Provide करता है।

जबकि यदि हम चाहें तो Pure SQL Statements का प्रयोग करके भी उन सभी जरूरतों को पूरा कर सकते हैं, जिन्हें पूरा करने के लिए हम LINQ Queries का प्रयोग करते हैं। यहां तक कि हम LINQ Queries द्वारा सभी प्रकार की जरूरतों को पूरा नहीं कर सकते, जिन्हें Pure SQL Queries द्वारा पूरा किया जा सकता है। इसलिए LINQ को उपयोग में लेने के बावजूद कई ऐसी Situations होती हैं, जहां हमें Pure SQL Statement को ही उपयोग में लेना जरूरी या आसान होता है। क्योंकि ऐसी जरूरतों को पूरा करने के लिए यदि हम LINQ का प्रयोग करें, तो Program की Complexity काफी बढ जाती है।

साथ ही LINQ को मूल रूप से Objects, XML व SQL के साथ में उपयोग में लिया जा सकता है। हालांकि हम इन तीन प्रकार के Data Collections के साथ LINQ का प्रयोग ज्यादा करते हैं, लेकिन LINQ का प्रयोग हम हर उस जगह पर कर सकते हैं, जहां Data का Collection होता है। फिर वह Collection किसी भी प्रकार के Data का हो सकता है।

यानी LINQ वास्तव में एक API है जो हमें एक Standard Language के रूप में विभिन्न Data Sources के Data को Easy तरीके से Access व Manipulate करने की सुविधा Provide करता है। इसलिए LINQ वास्तव में काफी बडा API है, क्योंकि ये पूरी तरह से C# Programming Language के अन्दर Use की जा सकने वाली एक Structure Query Language है, जो कि पूरी तरह से SQL के समान है। इसलिए यदि हम LINQ को पूरी तरह से Cover करना चाहें, तो वास्तव में हमें SQL के विभिन्न Concepts को LINQ के माध्‍यम से Cover करना होगा, जिसके लिए एक Chapter नहीं बल्कि एक अलग पुस्तक लिखने की जरूरत पडेगी।

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

C#.NET in Hindi | Page:908 | Format: PDF

BUY NOW GET DEMO REVIEWS