آموزش LINQ

پنجشنبه 21 خرداد 1394

LINQ تکنولوژی جدیدی است که شرکت ماکروسافت با عرضه نسخه ی سوم Net. آنرا معرفی نمود . هدف این مقاله ارائه برخی اصول درمورد LINQ می باشد .

آموزش LINQ

هدف این مقاله ارائه برخی اصول درمورد LINQ برای برخی افراد که ممکن است هنوز درک درستی در این مورد  به دست نیاورده اند ، می باشد . LINQ دسترسی اطلاعات و منابع داده را یکپارچه می کند و اجازه ترکیب اطلاعات ازمنابع مختلف با یکدیگر را می دهد . LINQ اختصار یافته Language  Integrated Query می باشد .  دستوراتی که LINQ برای پایگاه داده ارائه می دهد دقیقا مشابه دستورات SQL می باشد . Query  ها را مستقیما در داخل زبان های برنامه نویسی NET. مانند #C و Visual Basic از طریق مجموعه از برنامه های افزودنی به این زبان ها یکپارچه می کند . قبل از LINQ ، توسعه دهندگان مجبور به استفاده از زبان های مختلفی مانند XML ، SQL یا XPath  و فناوری های متفاوت و API هایی مانند ADO.NET یا XML  در هر برنامه نوشته شده با استفاده از زبانی مانند #C یا VB.NET بودند.

یکی از جنبه های کلیدی LINQ این است که دربرابر هر نوع از اشیا یا منابع داده مورد استفاده قرار می گیرد و یک محیط برنامه نویسی سازگار فراهم می کند . نحوه و مفاهیم در تمامی موارد استفاده یکسان هستند :

زمانیکه نحوه استفاده از LINQ دربرابریک  آرایه یا مجموعه را یاد می گیرید ، همچنین بسیاری از مفاهیم مورد نیاز برای استفاده از LINQ با یک پایگاه داده یا یک فایل XML را می دانید . یکی دیگر از جنبه های مهم LINQ این است که وقتی از آن استفاده می کنید، با یک نوع داده خاص کار می کنید . این کد پایه را بررسی کنید و لینک ها به منبع اطلاعات را ببینید :

using System;
using System.Linq;
public sealed class Program
{
    static double Square(double n)
    {
        Console.WriteLine("Computing Square(" + n + ")...");
        return Math.Pow(n, 2);
    }
    public static void Main()
    {
        int[] numbers = { 1, 2, 3 };
        var query =
            from n in numbers
            select Square(n);
        foreach (var n in query)
            Console.WriteLine(n);
    }
}

OUTPUT:

Computing Square(1)...
1
Computing Square(2)...
4
Computing Square(3)...
9

این کد یک متد بنام Square برای اعلام یک متغیر محلی implicitly-typed  به منظور انجام عملیات بر آرایه یا دنباله از سه اعداد صحیح است . متد انتخابی توالی که در آن هر عنصر ورودی درlambda  داده شده تبدیل شده است . تکرار هریک از اجزا را قادر می سازد که عملیات در هر اجزا  انجام شود. در حقیقت،ایده کلی پشت یک شمارنده که تنها هدف از طریق پیشبرد و خواندن مطالب مجموعه دیگر است .شمارنده ها قابلیت نوشتن را فراهم نمی کنند . این نوع می تواند به عنوان مکان نما که پیشرفت های بیش از هر عنصر منحصر به فرد در مجموعه مشاهده شود،دریک زمان . IEnumerable نوع محتوایی که می توانند شمارش شوند را نشان می دهد ، در حالی که IEnumerable مسئول انجام شمارش واقعی است . پایه اصلی  اطلاعات در تکنولوژی LINQ به ترتیب  و عناصر آن است . دنباله هر شی که رابط IEnumerable و هر عنصر در هر مورد در دنباله پیاده سازی می کند . در اینجا به نمونه ای از کدهای پایه اشاره می کنیم :

using System;
using System.Collections.Generic;
using System.Linq;
public class Program
{
    public static void Main()
    {
        string[] names = { "Tom", "Mitch", "Steve" };
        IEnumerable<string> filteredNames = System.Linq.Enumerable.Where
        (names, n => n.Length >= 4);
        foreach (string n in filteredNames)
            Console.Write(n + "|");
    }
}

And here is the output:

Mitch

Steve

عبارات Lambda :   عملگر های زنجیره ای Query

مثال قبلی تا حدی واقعی نبود به دلیل اینکه دو Query پایه lambda نشان داده بود ، هر یک شامل یک عملگر Query واحد می باشد .برای ساختن بیشتر Query های پیچیده ، از عملگرهای زنجیره ای بیشتری استفاده می شود :

using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
public class Program
{
    public static void Main()
    {
        string[] names = { "Tom", "Dick", "Harry", "Mary", "Jay" };
        IEnumerable query = names
                         .Where   (n => n.Contains ("a"))
                         .OrderBy (n => n.Length)
                         .Select  (n => n.ToUpper());
        foreach (string name in query)
        Console.Write(name + "|");
    }
}

// end of program
// The same query constructed progressively:

IEnumerable filtered   = names.Where      (n => n.Contains ("a"));
IEnumerable sorted     = filtered.OrderBy (n => n.Length);
IEnumerable finalQuery = sorted.Select    (n => n.ToUpper());

در اینجا Query  پیچیده تری که با استفاده ازکلید واژه "var"از متغیر محلی implicitly استفاده میکند :

 

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
static class LanguageFeatures
{
    class ProcessData
    {
        public Int32 Id { get; set; }
        public Int64 Memory { get; set; }
        public String Name { get; set; }
    }
    static void DisplayProcesses(Func<Process, Boolean> match)
    {
        // implicitly-typed local variables
        var processes = new List<ProcessData>();
        foreach (var process in Process.GetProcesses())
        {
            if (match(process))
            {
                // object initializers
                processes.Add(new ProcessData
                {
                    Id = process.Id,
                    Name = process.ProcessName,
                    Memory = process.WorkingSet64
                });
            }
        }
        // extension methods
        Console.WriteLine("Total memory: {0} MB",
          processes.TotalMemory() / 1024 / 1024);
        var top2Memory =
          processes
            .OrderByDescending(process => process.Memory)
            .Take(2)
            .Sum(process => process.Memory) / 1024 / 1024;
        Console.WriteLine(
          "Memory consumed by the two most hungry processes: {0} MB",
          top2Memory);
        // anonymous types
        var results = new
        {
            TotalMemory = processes.TotalMemory() / 1024 / 1024,
            Top2Memory = top2Memory,
            Processes = processes
        };
        ObjectDumper.Write(results, 1);
        ObjectDumper.Write(processes);
    }
    static Int64 TotalMemory(this IEnumerable<ProcessData> processes)
    {
        Int64 result = 0;
        foreach (var process in processes)
            result += process.Memory;
        return result;
    }
    static void Main()
    {
        // lambda expressions
        DisplayProcesses(process => process.WorkingSet64 >= 20 * 1024 * 1024);
    }
}

If you examine this code, you will see that "ObjectDumper" is not defined, yet referred to. This means that we have a DLL reference file to compile as well: 

using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
public class ObjectDumper
{
    public static void Write(object element)
    {
        Write(element, 0);
    }
    public static void Write(object element, int depth)
    {
        Write(element, depth, Console.Out);
    }
    public static void Write(object element, int depth, TextWriter log)
    {
        ObjectDumper dumper = new ObjectDumper(depth);
        dumper.writer = log;
        dumper.WriteObject(null, element);
    }
    TextWriter writer;
    int pos;
    int level;
    int depth;
    private ObjectDumper(int depth)
    {
        this.depth = depth;
    }
    private void Write(string s)
    {
        if (s != null)
        {
            writer.Write(s);
            pos += s.Length;
        }
    }
    private void WriteIndent()
    {
        for (int i = 0; i < level; i++) writer.Write("  ");
    }
    private void WriteLine()
    {
        writer.WriteLine();
        pos = 0;
    }
    private void WriteTab()
   {
        Write("  ");
        while (pos % 8 != 0) Write(" ");
    }
    private void WriteObject(string prefix, object element)
    {
        if (element == null || element is ValueType || element is string)
        {
            WriteIndent();
            Write(prefix);
            WriteValue(element);
            WriteLine();
        }
        else
        {
            IEnumerable enumerableElement = element as IEnumerable;
            if (enumerableElement != null)
            {
                foreach (object item in enumerableElement)
                {
                    if (item is IEnumerable && !(item is string))
                    {
                        WriteIndent();
                        Write(prefix);
                        Write("...");
                        WriteLine();
                        if (level < depth)
                        {
                            level++;
                            WriteObject(prefix, item);
                            level--;
                        }
                    }
                    else
                    {
                        WriteObject(prefix, item);
                    }
                }
            }
            else
            {
                MemberInfo[] members = element.GetType().GetMembers(BindingFlags.Public | BindingFlags.Instance);
                WriteIndent();
                Write(prefix);
                bool propWritten = false;
                foreach (MemberInfo m in members)
                {
                    FieldInfo f = m as FieldInfo;
                    PropertyInfo p = m as PropertyInfo;
                    if (f != null || p != null)
                    {
                        if (propWritten)
                        {
                            WriteTab();
                        }
                        else
                        {
                            propWritten = true;
                        }
                        Write(m.Name);
                        Write("=");
                        Type t = f != null ? f.FieldType : p.PropertyType;
                        if (t.IsValueType || t == typeof(string))
                        {
                            WriteValue(f != null ? f.GetValue(element) : p.GetValue(element, null));
                        }
                        else
                        {
                            if (typeof(IEnumerable).IsAssignableFrom(t))
                            {
                                Write("...");
                            }
                            else
                            {
                                Write("{ }");
                            }
                        }
                    }
                }
                if (propWritten) WriteLine();
                if (level < depth)
                {
                    foreach (MemberInfo m in members)
                    {
                        FieldInfo f = m as FieldInfo;
                        PropertyInfo p = m as PropertyInfo;
                        if (f != null || p != null)
                        {
                            Type t = f != null ? f.FieldType : p.PropertyType;
                            if (!(t.IsValueType || t == typeof(string)))
                            {
                                object value = f != null ? f.GetValue(element) : p.GetValue(element, null);
                                if (value != null)
                                {
                                    level++;
                                    WriteObject(m.Name + ": ", value);
                                    level--;
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    private void WriteValue(object o)
    {
        if (o == null)
        {
            Write("null");
        }
        else if (o is DateTime)
        {
            Write(((DateTime)o).ToShortDateString());
        }
        else if (o is ValueType || o is string)
        {
            Write(o.ToString());
        }
        else if (o is IEnumerable)
        {
            Write("...");
        }
        else
        {
            Write("{ }");
        }
    }
}

حالا  فایل ObjectDumper.cs را در DLL با استفاده از '/target:library'با رفتن به خط دستور  کامپایل می کنیم،یا آن را بصورت فایل Class در VS Studio 2010 کامپایل می کنیم.توجه داشته باشید اگر از VS 2010 استفاده می کنید ،

به قسمت Project's properties بروید و اطمینان حاصل کنید که NET. Framework برابر با 4.0  می باشد . حالا فایل MyProgram.cs   با مراجعه به ObjectDumper.dll: csc.exe /r:ObjectDumper.dll MyProgram.cs کامپایل می کنیم .

خروجی بصورت زیر می باشد :

C:\Windows\MICROS~1.NET\FRAMEW~1\V40~1.303>myprogram
Total memory: 968 MB
Memory consumed by the two most hungry processes: 314 MB
TotalMemory=968         Top2Memory=314  Processes=...
  Processes: Id=3244      Memory=65527808         Name=sqlservr
  Processes: Id=5320      Memory=23556096         Name=sqlservr
  Processes: Id=3320      Memory=37498880         Name=DkService
  Processes: Id=952       Memory=47443968         Name=svchost
  Processes: Id=5272      Memory=167903232        Name=WINWORD
  Processes: Id=1108      Memory=68866048         Name=svchost
  Processes: Id=1096      Memory=90230784         Name=svchost
  Processes: Id=500       Memory=120848384        Name=AcroRd32
  Processes: Id=2856      Memory=75415552         Name=explorer
  Processes: Id=1672      Memory=71299072         Name=digitaleditions
  Processes: Id=4348      Memory=162045952        Name=LINQPad
  Processes: Id=2576      Memory=35442688         Name=Babylon
  Processes: Id=2172      Memory=49131520         Name=SearchIndexer
  Id=3244                 Memory=65527808         Name=sqlservr
  Id=5320                 Memory=23556096         Name=sqlservr
  Id=3320                 Memory=37498880         Name=DkService
  Id=952                  Memory=47443968         Name=svchost
  Id=5272                 Memory=167903232        Name=WINWORD
  Id=1108                 Memory=68866048         Name=svchost
  Id=1096                 Memory=90230784         Name=svchost
  Id=500                  Memory=120848384        Name=AcroRd32
  Id=2856                 Memory=75415552         Name=explorer
  Id=1672                 Memory=71299072         Name=digitaleditions
  Id=4348                 Memory=162045952        Name=LINQPad
  Id=2576                 Memory=35442688         Name=Babylon
  Id=2172                 Memory=49131520         Name=SearchIndexer

لازم به ذکر است ،مواردی که در ادامه به آنها اشاره شده برای مدیریت کد های موجود در LINQ بکار می روند :

    Implicitly typed local variables
    Object initializers
    Lambda expressions
    Extension methods
    Anonymous types

حالا این کد را مجددا بررسی می کنیم :

var processes =
    Process.GetProcesses()
    .Where(process => process.WorkingSet64 > 20 * 1024 * 1024)
    .OrderByDescending(process => process.WorkingSet64)
    .Select(process => new
    {
        process.Id,
        Name = process.ProcessName
    });

یک متغیر با استفده از کلمه کلیدی  var در C# 3.0 اعلام می کنیم . این یک متغیر محلی implicitly typed می باشد. WorkingSet64 یک عبارت در lambda است . اکثر اپراتور های Query  عبارت lambda را به عنوان استدلال بکار می برند . OrderByDescending و پارامتر های آن از extesion method هستند . کلمه کلیدی new از نوع ناشناخته (anonymous type) ، و  Name از نوع object initializer می باشد .

قربانی

نویسنده 44 مقاله در برنامه نویسان
  • C#.net
  • 3k بازدید
  • 4 تشکر

کاربرانی که از نویسنده این مقاله تشکر کرده اند

در صورتی که در رابطه با این مقاله سوالی دارید، در تاپیک های انجمن مطرح کنید