Join های توسعه یافته در LINQ

سوال و جواب های زیادی درباره موضوع Join در فرم های مختلف مشاهده می شود ؛ اما مطلبی که به طور کامل این موضوع را در بر بگیرد پیدا نمیشود. در این مقاله به طور کامل به این موضوع میپردازیم.

ما سعی کردیم با اضافه کردن یک پروژه نمونه کار شما را راحت کنیم، که تمام مطالب ذکرشده در این مقاله در آن اعمال شده است.

علاوه براین، این پروژه شامل یک کلاس افزونه است که می تواند برای کسانی که نمی خواهند زمان زیادی برای کدنویسی این مفاهیم صرف کنند، مفید باشد.

فهرست مطالب

  • نصب
  •  استفاده از کد
  •  Inner Join
  •  Left Join
  •  Right Join
  • Full Outer Join
  •  Left Excluding Join
  •  Right Excluding Join
  •  Full Outer Excluding Join

نصب پکیج Join

فرایند نصب بسیار ساده است و یک Nuget Package اضافه خواهد شد.

Join

با استفاده از MoralesLarios.Linq آن را به کلاس اضافه می کنیم.

استفاده از کد

ما در اینجا از دو کلاس برای نمایش مثال ها استفاده می کنیم که به شکل زیر هستند:

public class Person  
{  
    public string   ID        { get; set; }  
    public string   Name      { get; set; }  
    public int      Age       { get; set; }  
    public double   Salary    { get; set; }  
    public DateTime Born      { get; set; }  
    public int      IdAddress { get; set; }  
}  
  
public class Address  
{  
    public int    IdAddress { get; set; }  
    public string Street    { get; set; }  
    public int    Num       { get; set; }  
    public string City      { get; set; }  
}   

جدول زیر مقادیر پیش فرض برای کلاس Person هستند.

Join

مقادیر پیش فرض کلاس Address به شکل زیر است.

Join

کتابخانه متدهای افزونه ما دارای ۶ متد است. متد اصلی INNERJOIN در کتابخانه مبتنی بر LINQ توسعه داده شده است.در ادامه متدهای زیر توضیح داده خواهند شد:

  •  InnerJoin
  •  LeftJoin
  •  RightJoin
  •  Full OuterJoin
  •  Left Join excluding InnerJoin
  •  Right Join excluding Right Join excluding InnerJoin
  •  Full Outer Join excluding InnerJoin

 

بررسی INNERJOIN

Join

این متد اصلی است که در فریمورک .NET پیاده سازی شده است، بنابراین هیچ متد افزونه ای برای آن وجود ندارد.

var result = from p in Person.BuiltPersons()  
             join a in Address.BuiltAddresses()  
             on p.IdAddress equals a.IdAddress  
             select new   
       {   
                 Name             = a.MyPerson.Name,  
                 Age              = a.MyPerson.Age,  
                 PersonIdAddress  = a.MyPerson.IdAddress,  
                 AddressIdAddress = a.MyAddress.IdAddress,  
                 Street           = a.MyAddress.Street  
       };   

عبارات لامبدا

var result = from p in Person.BuiltPersons()  
             join a in Address.BuiltAddresses()  
             on p.IdAddress equals a.IdAddress  
             select new   
       {   
                 Name             = a.MyPerson.Name,  
                 Age              = a.MyPerson.Age,  
                 PersonIdAddress  = a.MyPerson.IdAddress,  
                 AddressIdAddress = a.MyAddress.IdAddress,  
                 Street           = a.MyAddress.Street  
       };   

همانطور که مشاهده می کنیم، متد افزونه ۵ بخش اصلی دارد که برای بقیه متدهای افزونه به اشتراک گذاشته می شود. که به صورت زیر است:

  1. ۱. یک مجموعه اصلی است.
    ۲. یک مجموعه داخلی است
    ۳. یک کلید اصلی است.
    ۴. یک کلید خارجی است.
    ۵. و یک نوع برای مجموعه نهایی است.

نتیجه کوئری بالا به شکل زیر است:

Join
همانطور که مشاهده می کنیم، مقدار آدرس شخصی با مقدار AddressIdAddressها مطابقت دارد.
Join

 

متد افزونه

public static IEnumerable<TResult>   
    LeftJoin<TSource, TInner, TKey, TResult>(this IEnumerable<TSource> source,  
                                                 IEnumerable<TInner> inner,   
                                                 Func<TSource, TKey> pk,   
                                                 Func<TInner, TKey> fk,   
                                                 Func<TSource, TInner, TResult> result)  
                where TSource : class where TInner : class  
{  
    IEnumerable<TResult> _result = Enumerable.Empty<TResult>();  
   
    _result = from s in source  
              join i in inner  
              on pk(s) equals fk(i) into joinData  
              from left in joinData.DefaultIfEmpty()  
              select result(s, left);  
   
    return _result;  
}   

عبارت لامبدا

var resultJoint = Person.BuiltPersons().LeftJoin(                    /// Source Collection  
                    Address.BuiltAddresses(),                        /// Inner Collection  
                    p => p.IdAddress,                                /// PK  
                    a => a.IdAddress,                                /// FK  
                    (p, a) => new { MyPerson = p, MyAddress = a })   /// Result Collection  
                    .Select(a => new  
                    {  
                        Name             = a.MyPerson.Name,  
                        Age              = a.MyPerson.Age,  
                        PersonIdAddress  = a.MyPerson.IdAddress,  
                        AddressIdAddress = (a.MyAddress != null ? a.MyAddress.IdAddress : -1),  
             Street           = (a.MyAddress != null ? a.MyAddress.Street    : "Null-Value")  
                    });    

در اینجا باید توجه داشته باشیم که در زمان فراخوانی، متد select نوع نتیجه جدید ما را می سازد؛ که باید کنترل

کنیم مقادیر توسط کلاس Address برگردانده شوند، چرا که شی برگشتی می تواند null باشد و در این مورد،

خواندن هر یک از ویژگی های آن باعث NullReferenceException می شود.

 

نتیجه کوئری بالا به شکل زیر است:

<Join>

RightJoin

 

<Join>

متد افزونه

public static IEnumerable<TResult>   
    RightJoin<TSource, TInner, TKey, TResult>(this IEnumerable<TSource> source,  
                                                  IEnumerable<TInner> inner,  
                                                  Func<TSource, TKey> pk,  
                                                  Func<TInner, TKey> fk,  
                                                  Func<TSource, TInner, TResult> result)  
                where TSource : class where TInner : class  
{  
    IEnumerable<TResult> _result = Enumerable.Empty<TResult>();  
   
    _result  = from i in inner  
                join s in source  
                on fk(i) equals pk(s) into joinData  
                from right in joinData.DefaultIfEmpty()  
                select result(right, i);  
   
    return _result;  
}   
}   

عبارت لامبادا

var resultJoint = Person.BuiltPersons().RightJoin(                   /// Source Collection  
                    Address.BuiltAddresses(),                        /// Inner Collection  
                    p => p.IdAddress,                                /// PK  
                    a => a.IdAddress,                                /// FK  
                    (p, a) => new { MyPerson = p, MyAddress = a })   /// Result Collection  
                    .Select(a => new  
                    {  
                        Name           = (a.MyPerson != null ? a.MyPerson.Name : "Null-Value"),  
                        Age              = (a.MyPerson != null ? a.MyPerson.Age : -1),  
                        PersonIdAddress  = (a.MyPerson != null ? a.MyPerson.IdAddress : -1),  
                        AddressIdAddress = a.MyAddress.IdAddress,  
                        Street           = a.MyAddress.Street  
                    });   

توجه داشته باشید که برای جلوگیری از خطا باید مقادیر null کلاس Person را کنترل کنیم.
نتیجه کوئری بالا به شکل زیر است:

 

Join

Full OuterJoin

Join

متد افزونه

public static IEnumerable<TResult>   
    FullOuterJoinJoin<TSource, TInner, TKey, TResult>(this IEnumerable<TSource> source,  
                                                          IEnumerable<TInner> inner,  
                                                          Func<TSource, TKey> pk,  
                                                          Func<TInner, TKey> fk,  
                                                          Func<TSource, TInner, TResult> result)  
                where TSource : class where TInner : class  
{  
   
    var left = source.LeftJoin(inner, pk, fk, result).ToList();  
    var right = source.RightJoin(inner, pk, fk, result).ToList();  
   
    return left.Union(right);  
}       

عبارت لامبادا

var resultJoint = Person.BuiltPersons().FullOuterJoinJoin(           /// Source Collection  
                    Address.BuiltAddresses(),                        /// Inner Collection  
                    p => p.IdAddress,                                /// PK  
                    a => a.IdAddress,                                /// FK  
                    (p, a) => new { MyPerson = p, MyAddress = a })   /// Result Collection  
                    .Select(a => new  
                    {  
                        Name             = (a.MyPerson  != null ? a.MyPerson.Name       : "Null-Value"),  
                        Age              = (a.MyPerson  != null ? a.MyPerson.Age        : -1),  
                        PersonIdAddress  = (a.MyPerson  != null ? a.MyPerson.IdAddress  : -1),  
                        AddressIdAddress = (a.MyAddress != null ? a.MyAddress.IdAddress : -1),  
                        Street           = (a.MyAddress != null ? a.MyAddress.Street    : "Null-Value")  
                    });   

توجه داشته باشید که مقادیر null در هر دو کلاس باید کنترل شود.

نتیجه کوئری بالا به شکل زیر است.

<Join>

Left ExcludingJoin

<Join>

متد افزونه

public static IEnumerable<TResult>   
    LeftExcludingJoin<TSource, TInner, TKey, TResult>(this IEnumerable<TSource> source,  
                                                          IEnumerable<TInner> inner,  
                                                          Func<TSource, TKey> pk,  
                                                          Func<TInner, TKey> fk,  
                                                          Func<TSource, TInner, TResult> result)  
                where TSource : class where TInner : class  
{  
    IEnumerable<TResult> _result = Enumerable.Empty<TResult>();  
   
    _result = from s in source  
                join i in inner  
                on pk(s) equals fk(i) into joinData  
                from left in joinData.DefaultIfEmpty()  
                where left == null  
                select result(s, left);  
   
    return _result;  
}   
}  

عبارت لامبادا

var resultJoint = Person.BuiltPersons().LeftExcludingJoin(           /// Source Collection  
                    Address.BuiltAddresses(),                        /// Inner Collection  
                    p => p.IdAddress,                                /// PK  
                    a => a.IdAddress,                                /// FK  
                    (p, a) => new { MyPerson = p, MyAddress = a })   /// Result Collection  
                    .Select(a => new  
                    {  
                        Name             = a.MyPerson.Name,  
                        Age              = a.MyPerson.Age,  
                        PersonIdAddress  = a.MyPerson.IdAddress,  
                        AddressIdAddress = (a.MyAddress != null ? a.MyAddress.IdAddress : -1),  
                        Street           = (a.MyAddress != null ? a.MyAddress.Street    : "Null-Value")  
                    });  

توجه داشته باشید که باید مقادیر null در کلاس Address کنترل شود.

نتیجه کوئری بالا به شکل زیر است:
<Join>
Join>

متد افزونه

public static IEnumerable<TResult>   
     RightExcludingJoin<TSource, TInner, TKey, TResult>(this IEnumerable<TSource> source,  
                                                        IEnumerable<TInner> inner,  
                                                        Func<TSource, TKey> pk,  
                                                        Func<TInner, TKey> fk,  
                                                        Func<TSource, TInner, TResult> result)  
                where TSource : class where TInner : class  
{  
    IEnumerable<TResult> _result = Enumerable.Empty<TResult>();  
   
    _result = from i in inner  
                join s in source  
                on fk(i) equals pk(s) into joinData  
                from right in joinData.DefaultIfEmpty()  
                where right == null  
                select result(right, i);  
   
    return _result;  
}   

عبارت لامبادا

var resultJoint = Person.BuiltPersons().RightExcludingJoin(          /// Source Collection  
                    Address.BuiltAddresses(),                        /// Inner Collection  
                    p => p.IdAddress,                                /// PK  
                    a => a.IdAddress,                                /// FK  
                    (p, a) => new { MyPerson = p, MyAddress = a })   /// Result Collection  
                    .Select(a => new  
                    {  
                        Name             = (a.MyPerson != null ? a.MyPerson.Name      : "Null-Value"),  
                        Age              = (a.MyPerson != null ? a.MyPerson.Age       : -1),  
                        PersonIdAddress  = (a.MyPerson != null ? a.MyPerson.IdAddress : -1),  
                        AddressIdAddress = a.MyAddress.IdAddress,  
                        Street           = a.MyAddress.Street  
                    });  

توجه داشته باشید که باید مقادیر null در کلاس Person کنترل شود.

نتیجه کوئری بالا به شکل زیر است:

 

 

Join
Join

متد افزونه

public static IEnumerable<TResult>   
   FulltExcludingJoin<TSource, TInner, TKey, TResult>(this IEnumerable<TSource> source,  
                                                      IEnumerable<TInner> inner,  
                                                      Func<TSource, TKey> pk,  
                                                      Func<TInner, TKey> fk,  
                                                      Func<TSource, TInner, TResult> result)  
                where TSource : class where TInner : class  
{  
    var left = source.LeftExcludingJoin(inner, pk, fk, result).ToList();  
    var right = source.RightExcludingJoin(inner, pk, fk, result).ToList();  
   
    return left.Union(right);  
}  

عبارت لامبادا

var resultJoint = Person.BuiltPersons().FulltExcludingJoin(          /// Source Collection  
                    Address.BuiltAddresses(),                        /// Inner Collection  
                    p => p.IdAddress,                                /// PK  
                    a => a.IdAddress,                                /// FK  
                    (p, a) => new { MyPerson = p, MyAddress = a })   /// Result Collection  
                    .Select(a => new  
                    {  
                        Name             = (a.MyPerson  != null ? a.MyPerson.Name       : "Null-Value"),  
                        Age              = (a.MyPerson  != null ? a.MyPerson.Age        : -1),  
                        PersonIdAddress  = (a.MyPerson  != null ? a.MyPerson.IdAddress  : -1),  
                        AddressIdAddress = (a.MyAddress != null ? a.MyAddress.IdAddress : -1),  
                        Street           = (a.MyAddress != null ? a.MyAddress.Street    : "Null-Value")  
                    });  

توجه داشته باشید که باید مقادیر null را برای هر دو کلاس کنترل کنیم.

نتیجه کوئری بالا به شکل زیر است:

<Join>

بهترین راه حل

ما معتقدیم که این بهترین راه حل برای توسعه دهندگان شی گراست.

var GroupPersons = this.Persons.GroupJoin(this.Addresses,     /// Inner Collection  
                                          p => p.IdAddress,   /// PK  
                                          a => a.IdAddress,   /// FK  
                                          (p, a) =>           /// Result Collection  
                                          new {   
                                                  MyPerson  = p,   
                                                  Addresses = a.Select(ad => ad).ToList()   
                                               }).ToList();     
 or   
var GroupAddresses = this.Addresses.GroupJoin(this.Persons,         /// Inner Collection  
                                              a => a.IdAddress,     /// PK  
                                              p => p.IdAddress,     /// FK  
                                              (a, p) =>             /// Result Collection  
                                              new {   
                                                     MyAddress = a,   
                                                     Persons   = p.Select(ps => ps).ToList()   
                                                  }).ToList();   

کد مربوط به پر کردن پیش نمایش به شکل زیر است:

foreach (var data in GroupPersons)  
{  
    TreeViewItem tbi = new TreeViewItem{ Header = data.MyPerson };  
    this.treePersons.Items.Add(tbi);  
    foreach (var d in data.Addresses)  
    {  
        TreeViewItem tbiChild =   
        new TreeViewItem { Header = d , Background = Brushes.Gainsboro };  
        this.treePersons.Items.OfType<TreeViewItem>().Last().Items.Add(tbiChild);  
    }                          
}      
or   
foreach (var data in GroupAddresses)  
{  
    TreeViewItem tbi = new TreeViewItem{ Header = data.MyAddress };  
    this.treeAddresses.Items.Add(tbi);  
    foreach (var d in data.Persons)  
    {  
        TreeViewItem tbiChild =   
        new TreeViewItem { Header = d , Background = Brushes.Gainsboro };  
        this.treeAddresses.Items.OfType<TreeViewItem>().Last().Items.Add(tbiChild);  
    }                           
}    

نتایج به شکل زیر هستند:

 

Join

مقادیر IdAddress را تغییر دادیم که این کار برای مشاهده واضح تر لازم است.

<Join>

نتیجه به شکل زیر است:

<hJoin>

تست اپلیکیشن

در تست اپلیکیشن، ما می توانیم مقادیر مجموعه های Person و Address را تغییر داده و جوین را انتخاب کنیم تا

اعمال شود که تغییرات در مجموعه نهایی اعمال خواهد شد.

Join

زهره سلطانیان

نوشته‌های مرتبط

دیدگاه‌ها

*
*

این سایت از اکیسمت برای کاهش هرزنامه استفاده می کند. بیاموزید که چگونه اطلاعات دیدگاه های شما پردازش می‌شوند.

جشنواره فروش ویژه عید تا عید با تخفیف های باورنکردنی در ام اس پی سافتبزن بریم
+